[chore]: Bump github.com/tdewolff/minify/v2 from 2.20.0 to 2.20.6 (#2337)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
dependabot[bot] 2023-11-06 14:41:31 +00:00 committed by GitHub
commit 74b600655d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 2421 additions and 1141 deletions

View file

@ -8,12 +8,13 @@ import (
// Token is a single token unit with an attribute value (if given) and hash of the data.
type Token struct {
html.TokenType
Hash Hash
Data []byte
Text []byte
AttrVal []byte
Traits traits
Offset int
Hash Hash
Data []byte
Text []byte
AttrVal []byte
Traits traits
Offset int
HasTemplate bool
}
// TokenBuffer is a buffer that allows for token look-ahead.
@ -40,10 +41,11 @@ func (z *TokenBuffer) read(t *Token) {
t.Offset = z.r.Offset()
t.TokenType, t.Data = z.l.Next()
t.Text = z.l.Text()
t.HasTemplate = z.l.HasTemplate()
if t.TokenType == html.AttributeToken {
t.Offset += 1 + len(t.Text) + 1
t.AttrVal = z.l.AttrVal()
if len(t.AttrVal) > 1 && (t.AttrVal[0] == '"' || t.AttrVal[0] == '\'') {
if 1 < len(t.AttrVal) && (t.AttrVal[0] == '"' || t.AttrVal[0] == '\'') {
t.Offset++
t.AttrVal = t.AttrVal[1 : len(t.AttrVal)-1] // quotes will be readded in attribute loop if necessary
}

File diff suppressed because it is too large Load diff

View file

@ -41,6 +41,13 @@ var (
////////////////////////////////////////////////////////////////
var GoTemplateDelims = [2]string{"{{", "}}"}
var HandlebarsTemplateDelims = [2]string{"{{", "}}"}
var MustacheTemplateDelims = [2]string{"{{", "}}"}
var EJSTemplateDelims = [2]string{"<%", "%>"}
var ASPTemplateDelims = [2]string{"<%", "%>"}
var PHPTemplateDelims = [2]string{"<?", "?>"}
// Minifier is an HTML minifier.
type Minifier struct {
KeepComments bool
@ -50,6 +57,7 @@ type Minifier struct {
KeepEndTags bool
KeepQuotes bool
KeepWhitespace bool
TemplateDelims [2]string
}
// Minify minifies HTML data, it reads from r and writes to w.
@ -71,7 +79,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
z := parse.NewInput(r)
defer z.Restore()
l := html.NewLexer(z)
l := html.NewTemplateLexer(z, o.TemplateDelims)
tb := NewTokenBuffer(z, l)
for {
t := *tb.Shift()
@ -126,8 +134,9 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
w.Write(t.Data)
}
case html.TextToken:
// CSS and JS minifiers for inline code
if rawTagHash != 0 {
if t.HasTemplate {
w.Write(t.Data)
} else if rawTagHash != 0 {
if rawTagHash == Style || rawTagHash == Script || rawTagHash == Iframe {
var mimetype []byte
var params map[string]string
@ -372,6 +381,9 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
break
} else if attr.Text == nil {
continue // removed attribute
} else if attr.HasTemplate {
w.Write(attr.Data)
continue // don't minify attributes that contain templates
}
val := attr.AttrVal
@ -389,35 +401,30 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
attr.Hash == Action && t.Hash == Form) {
continue // omit empty attribute values
}
if attr.Traits&caselessAttr != 0 {
val = parse.ToLower(val)
}
if rawTagHash != 0 && attr.Hash == Type {
rawTagMediatype = parse.Copy(val)
}
if attr.Hash == Enctype || attr.Hash == Codetype || attr.Hash == Accept || attr.Hash == Type && (t.Hash == A || t.Hash == Link || t.Hash == Embed || t.Hash == Object || t.Hash == Source || t.Hash == Script || t.Hash == Style) {
if attr.Hash == Enctype ||
attr.Hash == Formenctype ||
attr.Hash == Accept ||
attr.Hash == Type && (t.Hash == A || t.Hash == Link || t.Hash == Embed || t.Hash == Object || t.Hash == Source || t.Hash == Script) {
val = minify.Mediatype(val)
}
// default attribute values can be omitted
if !o.KeepDefaultAttrVals && (attr.Hash == Type && (t.Hash == Script && jsMimetypes[string(val)] ||
t.Hash == Style && bytes.Equal(val, cssMimeBytes) ||
t.Hash == Link && bytes.Equal(val, cssMimeBytes) ||
t.Hash == Input && bytes.Equal(val, textBytes) ||
t.Hash == Button && bytes.Equal(val, submitBytes)) ||
attr.Hash == Language && t.Hash == Script ||
attr.Hash == Method && bytes.Equal(val, getBytes) ||
attr.Hash == Enctype && bytes.Equal(val, formMimeBytes) ||
if !o.KeepDefaultAttrVals && (attr.Hash == Type && (t.Hash == Script && jsMimetypes[string(parse.ToLower(parse.Copy(val)))] ||
t.Hash == Style && parse.EqualFold(val, cssMimeBytes) ||
t.Hash == Link && parse.EqualFold(val, cssMimeBytes) ||
t.Hash == Input && parse.EqualFold(val, textBytes) ||
t.Hash == Button && parse.EqualFold(val, submitBytes)) ||
attr.Hash == Method && parse.EqualFold(val, getBytes) ||
attr.Hash == Enctype && parse.EqualFold(val, formMimeBytes) ||
attr.Hash == Colspan && bytes.Equal(val, oneBytes) ||
attr.Hash == Rowspan && bytes.Equal(val, oneBytes) ||
attr.Hash == Shape && bytes.Equal(val, rectBytes) ||
attr.Hash == Shape && parse.EqualFold(val, rectBytes) ||
attr.Hash == Span && bytes.Equal(val, oneBytes) ||
attr.Hash == Clear && bytes.Equal(val, noneBytes) ||
attr.Hash == Frameborder && bytes.Equal(val, oneBytes) ||
attr.Hash == Scrolling && bytes.Equal(val, autoBytes) ||
attr.Hash == Valuetype && bytes.Equal(val, dataBytes) ||
attr.Hash == Media && t.Hash == Style && bytes.Equal(val, allBytes)) {
attr.Hash == Media && t.Hash == Style && parse.EqualFold(val, allBytes)) {
continue
}
@ -440,7 +447,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
val = val[11:]
}
attrMinifyBuffer.Reset()
if err := m.MinifyMimetype(jsMimeBytes, attrMinifyBuffer, buffer.NewReader(val), nil); err == nil {
if err := m.MinifyMimetype(jsMimeBytes, attrMinifyBuffer, buffer.NewReader(val), inlineParams); err == nil {
val = attrMinifyBuffer.Bytes()
} else if err != minify.ErrNotExist {
return minify.UpdateErrorPosition(err, z, attr.Offset)

View file

@ -13,7 +13,6 @@ const (
const (
booleanAttr traits = 1 << iota
caselessAttr
urlAttr
trimAttr
)
@ -163,106 +162,124 @@ var tagMap = map[Hash]traits{
}
var attrMap = map[Hash]traits{
Accept: trimAttr,
Accept_Charset: caselessAttr,
Action: urlAttr,
Align: caselessAttr,
Alink: caselessAttr,
Allowfullscreen: booleanAttr,
Async: booleanAttr,
Autofocus: booleanAttr,
Autoplay: booleanAttr,
Axis: caselessAttr,
Background: urlAttr,
Bgcolor: caselessAttr,
Charset: caselessAttr,
Checked: booleanAttr,
Cite: urlAttr,
Class: trimAttr,
Classid: urlAttr,
Clear: caselessAttr,
Codebase: urlAttr,
Codetype: trimAttr,
Color: caselessAttr,
Cols: trimAttr,
Colspan: trimAttr,
Compact: booleanAttr,
Controls: booleanAttr,
Data: urlAttr,
Declare: booleanAttr,
Default: booleanAttr,
DefaultChecked: booleanAttr,
DefaultMuted: booleanAttr,
DefaultSelected: booleanAttr,
Defer: booleanAttr,
Dir: caselessAttr,
Disabled: booleanAttr,
Enabled: booleanAttr,
Enctype: trimAttr,
Face: caselessAttr,
Formaction: urlAttr,
Formnovalidate: booleanAttr,
Frame: caselessAttr,
Hidden: booleanAttr,
Href: urlAttr,
Hreflang: caselessAttr,
Http_Equiv: caselessAttr,
Icon: urlAttr,
Inert: booleanAttr,
Ismap: booleanAttr,
Itemscope: booleanAttr,
Lang: trimAttr,
Language: caselessAttr,
Link: caselessAttr,
Longdesc: urlAttr,
Manifest: urlAttr,
Maxlength: trimAttr,
Media: caselessAttr | trimAttr,
Method: caselessAttr,
Multiple: booleanAttr,
Muted: booleanAttr,
Nohref: booleanAttr,
Noresize: booleanAttr,
Noshade: booleanAttr,
Novalidate: booleanAttr,
Nowrap: booleanAttr,
Open: booleanAttr,
Pauseonexit: booleanAttr,
Poster: urlAttr,
Profile: urlAttr,
Readonly: booleanAttr,
Rel: caselessAttr | trimAttr,
Required: booleanAttr,
Rev: caselessAttr,
Reversed: booleanAttr,
Rows: trimAttr,
Rowspan: trimAttr,
Rules: caselessAttr,
Scope: caselessAttr,
Scoped: booleanAttr,
Scrolling: caselessAttr,
Seamless: booleanAttr,
Selected: booleanAttr,
Shape: caselessAttr,
Size: trimAttr,
Sortable: booleanAttr,
Span: trimAttr,
Src: urlAttr,
Srcset: trimAttr,
Tabindex: trimAttr,
Target: caselessAttr,
Text: caselessAttr,
Translate: caselessAttr,
Truespeed: booleanAttr,
Type: trimAttr,
Typemustmatch: booleanAttr,
Undeterminate: booleanAttr,
Usemap: urlAttr,
Valign: caselessAttr,
Valuetype: caselessAttr,
Vlink: caselessAttr,
Visible: booleanAttr,
Xmlns: urlAttr,
Accept: trimAttr, // list of mimetypes
Accept_Charset: trimAttr,
Accesskey: trimAttr,
Action: urlAttr,
Allow: trimAttr,
Allowfullscreen: booleanAttr,
As: trimAttr,
Async: booleanAttr,
Autocapitalize: trimAttr,
Autocomplete: trimAttr,
Autofocus: booleanAttr,
Autoplay: booleanAttr,
Blocking: trimAttr,
Capture: trimAttr,
Charset: trimAttr,
Checked: booleanAttr,
Cite: urlAttr,
Class: trimAttr,
Color: trimAttr,
Cols: trimAttr, // uint bigger than 0
Colspan: trimAttr, // uint bigger than 0
Contenteditable: trimAttr,
Controls: booleanAttr,
Coords: trimAttr, // list of floats
Crossorigin: trimAttr,
Data: urlAttr,
Datetime: trimAttr,
Decoding: trimAttr,
Default: booleanAttr,
Defer: booleanAttr,
Dir: trimAttr,
Disabled: booleanAttr,
Draggable: trimAttr,
Enctype: trimAttr, // mimetype
Enterkeyhint: trimAttr,
Fetchpriority: trimAttr,
For: trimAttr,
Form: trimAttr,
Formaction: urlAttr,
Formenctype: trimAttr, // mimetype
Formmethod: trimAttr,
Formnovalidate: booleanAttr,
Formtarget: trimAttr,
Headers: trimAttr,
Height: trimAttr, // uint
Hidden: trimAttr, // TODO: boolean
High: trimAttr, // float
Href: urlAttr,
Hreflang: trimAttr, // BCP 47
Http_Equiv: trimAttr,
Imagesizes: trimAttr,
Imagesrcset: trimAttr,
Inert: booleanAttr,
Inputmode: trimAttr,
Is: trimAttr,
Ismap: booleanAttr,
Itemid: urlAttr,
Itemprop: trimAttr,
Itemref: trimAttr,
Itemscope: booleanAttr,
Itemtype: trimAttr, // list of urls
Kind: trimAttr,
Lang: trimAttr, // BCP 47
List: trimAttr,
Loading: trimAttr,
Loop: booleanAttr,
Low: trimAttr, // float
Max: trimAttr, // float or varies
Maxlength: trimAttr, // uint
Media: trimAttr,
Method: trimAttr,
Min: trimAttr, // float or varies
Minlength: trimAttr, // uint
Multiple: booleanAttr,
Muted: booleanAttr,
Nomodule: booleanAttr,
Novalidate: booleanAttr,
Open: booleanAttr,
Optimum: trimAttr, // float
Pattern: trimAttr, // regex
Ping: trimAttr, // list of urls
Playsinline: booleanAttr,
Popover: trimAttr,
Popovertarget: trimAttr,
Popovertargetaction: trimAttr,
Poster: urlAttr,
Preload: trimAttr,
Profile: urlAttr,
Readonly: booleanAttr,
Referrerpolicy: trimAttr,
Rel: trimAttr,
Required: booleanAttr,
Reversed: booleanAttr,
Rows: trimAttr, // uint bigger than 0
Rowspan: trimAttr, // uint
Sandbox: trimAttr,
Scope: trimAttr,
Selected: booleanAttr,
Shadowrootmode: trimAttr,
Shadowrootdelegatesfocus: booleanAttr,
Shape: trimAttr,
Size: trimAttr, // uint bigger than 0
Sizes: trimAttr,
Span: trimAttr, // uint bigger than 0
Spellcheck: trimAttr,
Src: urlAttr,
Srclang: trimAttr, // BCP 47
Srcset: trimAttr,
Start: trimAttr, // int
Step: trimAttr, // float or "any"
Tabindex: trimAttr, // int
Target: trimAttr,
Translate: trimAttr,
Type: trimAttr,
Usemap: trimAttr,
Width: trimAttr, // uint
Wrap: trimAttr,
Xmlns: urlAttr,
}
var jsMimetypes = map[string]bool{

View file

@ -56,16 +56,26 @@ func (tt TokenType) String() string {
////////////////////////////////////////////////////////////////
var GoTemplate = [2]string{"{{", "}}"}
var HandlebarsTemplate = [2]string{"{{", "}}"}
var MustacheTemplate = [2]string{"{{", "}}"}
var EJSTemplate = [2]string{"<%", "%>"}
var ASPTemplate = [2]string{"<%", "%>"}
var PHPTemplate = [2]string{"<?", "?>"}
// Lexer is the state for the lexer.
type Lexer struct {
r *parse.Input
err error
r *parse.Input
tmplBegin []byte
tmplEnd []byte
err error
rawTag Hash
inTag bool
text []byte
attrVal []byte
hasTmpl bool
}
// NewLexer returns a new Lexer for a given io.Reader.
@ -75,6 +85,14 @@ func NewLexer(r *parse.Input) *Lexer {
}
}
func NewTemplateLexer(r *parse.Input, tmpl [2]string) *Lexer {
return &Lexer{
r: r,
tmplBegin: []byte(tmpl[0]),
tmplEnd: []byte(tmpl[1]),
}
}
// Err returns the error encountered during lexing, this is often io.EOF but also other errors can be returned.
func (l *Lexer) Err() error {
if l.err != nil {
@ -88,14 +106,25 @@ func (l *Lexer) Text() []byte {
return l.text
}
// AttrKey returns the attribute key when an AttributeToken was returned from Next.
func (l *Lexer) AttrKey() []byte {
return l.text
}
// AttrVal returns the attribute value when an AttributeToken was returned from Next.
func (l *Lexer) AttrVal() []byte {
return l.attrVal
}
// HasTemplate returns the true if the token value contains a template.
func (l *Lexer) HasTemplate() bool {
return l.hasTmpl
}
// Next returns the next Token. It returns ErrorToken when an error was encountered. Using Err() one can retrieve the error message.
func (l *Lexer) Next() (TokenType, []byte) {
l.text = nil
l.hasTmpl = false
var c byte
if l.inTag {
l.attrVal = nil
@ -122,7 +151,7 @@ func (l *Lexer) Next() (TokenType, []byte) {
}
if l.rawTag != 0 {
if rawText := l.shiftRawText(); len(rawText) > 0 {
if rawText := l.shiftRawText(); 0 < len(rawText) {
l.text = rawText
l.rawTag = 0
return TextToken, rawText
@ -135,12 +164,12 @@ func (l *Lexer) Next() (TokenType, []byte) {
if c == '<' {
c = l.r.Peek(1)
isEndTag := c == '/' && l.r.Peek(2) != '>' && (l.r.Peek(2) != 0 || l.r.PeekErr(2) == nil)
if l.r.Pos() > 0 {
if isEndTag || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '!' || c == '?' {
// return currently buffered texttoken so that we can return tag next iteration
l.text = l.r.Shift()
return TextToken, l.text
}
if !isEndTag && (c < 'a' || 'z' < c) && (c < 'A' || 'Z' < c) && c != '!' && c != '?' {
// not a tag
} else if 0 < l.r.Pos() {
// return currently buffered texttoken so that we can return tag next iteration
l.text = l.r.Shift()
return TextToken, l.text
} else if isEndTag {
l.r.Move(2)
// only endtags that are not followed by > or EOF arrive here
@ -159,8 +188,12 @@ func (l *Lexer) Next() (TokenType, []byte) {
l.r.Move(1)
return CommentToken, l.shiftBogusComment()
}
} else if 0 < len(l.tmplBegin) && l.at(l.tmplBegin...) {
l.r.Move(len(l.tmplBegin))
l.moveTemplate()
l.hasTmpl = true
} else if c == 0 && l.r.Err() != nil {
if l.r.Pos() > 0 {
if 0 < l.r.Pos() {
l.text = l.r.Shift()
return TextToken, l.text
}
@ -241,6 +274,10 @@ func (l *Lexer) shiftRawText() []byte {
} else {
l.r.Move(1)
}
} else if 0 < len(l.tmplBegin) && l.at(l.tmplBegin...) {
l.r.Move(len(l.tmplBegin))
l.moveTemplate()
l.hasTmpl = true
} else if c == 0 && l.r.Err() != nil {
return l.r.Shift()
} else {
@ -346,6 +383,11 @@ func (l *Lexer) shiftStartTag() (TokenType, []byte) {
func (l *Lexer) shiftAttribute() []byte {
nameStart := l.r.Pos()
var c byte
if 0 < len(l.tmplBegin) && l.at(l.tmplBegin...) {
l.r.Move(len(l.tmplBegin))
l.moveTemplate()
l.hasTmpl = true
}
for { // attribute name state
if c = l.r.Peek(0); c == ' ' || c == '=' || c == '>' || c == '/' && l.r.Peek(1) == '>' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == 0 && l.r.Err() != nil {
break
@ -360,6 +402,7 @@ func (l *Lexer) shiftAttribute() []byte {
}
break
}
nameHasTmpl := l.hasTmpl
if c == '=' {
l.r.Move(1)
for { // before attribute value state
@ -378,11 +421,20 @@ func (l *Lexer) shiftAttribute() []byte {
if c == delim {
l.r.Move(1)
break
} else if 0 < len(l.tmplBegin) && l.at(l.tmplBegin...) {
l.r.Move(len(l.tmplBegin))
l.moveTemplate()
l.hasTmpl = true
} else if c == 0 && l.r.Err() != nil {
break
} else {
l.r.Move(1)
}
l.r.Move(1)
}
} else if 0 < len(l.tmplBegin) && l.at(l.tmplBegin...) {
l.r.Move(len(l.tmplBegin))
l.moveTemplate()
l.hasTmpl = true
} else { // attribute value unquoted state
for {
if c := l.r.Peek(0); c == ' ' || c == '>' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == 0 && l.r.Err() != nil {
@ -396,7 +448,15 @@ func (l *Lexer) shiftAttribute() []byte {
l.r.Rewind(nameEnd)
l.attrVal = nil
}
l.text = parse.ToLower(l.r.Lexeme()[nameStart:nameEnd])
if 0 < len(l.tmplBegin) && l.at(l.tmplBegin...) {
l.r.Move(len(l.tmplBegin))
l.moveTemplate()
l.hasTmpl = true
}
l.text = l.r.Lexeme()[nameStart:nameEnd]
if !nameHasTmpl {
l.text = parse.ToLower(l.text)
}
return l.r.Shift()
}
@ -473,6 +533,35 @@ func (l *Lexer) shiftXML(rawTag Hash) []byte {
return l.r.Shift()
}
func (l *Lexer) moveTemplate() {
for {
if c := l.r.Peek(0); l.at(l.tmplEnd...) || c == 0 && l.r.Err() != nil {
if c != 0 {
l.r.Move(len(l.tmplEnd))
}
break
} else if c == '"' || c == '\'' {
l.r.Move(1)
escape := false
for {
if c2 := l.r.Peek(0); !escape && c2 == c || c2 == 0 && l.r.Err() != nil {
if c2 != 0 {
l.r.Move(1)
}
break
} else if c2 == '\\' {
escape = !escape
} else {
escape = false
}
l.r.Move(1)
}
} else {
l.r.Move(1)
}
}
}
////////////////////////////////////////////////////////////////
func (l *Lexer) at(b ...byte) bool {