userland: add archive tools and pinned GoAWK, esbuild, and QuickJS
This commit is contained in:
@@ -0,0 +1,567 @@
|
||||
package css_parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/evanw/esbuild/internal/compat"
|
||||
"github.com/evanw/esbuild/internal/css_ast"
|
||||
"github.com/evanw/esbuild/internal/css_lexer"
|
||||
"github.com/evanw/esbuild/internal/logger"
|
||||
)
|
||||
|
||||
func (p *parser) commaToken(loc logger.Loc) css_ast.Token {
|
||||
t := css_ast.Token{
|
||||
Loc: loc,
|
||||
Kind: css_lexer.TComma,
|
||||
Text: ",",
|
||||
}
|
||||
if !p.options.minifyWhitespace {
|
||||
t.Whitespace = css_ast.WhitespaceAfter
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func expandTokenQuad(tokens []css_ast.Token, allowedIdent string) (result [4]css_ast.Token, ok bool) {
|
||||
n := len(tokens)
|
||||
if n < 1 || n > 4 {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't do this if we encounter any unexpected tokens such as "var()"
|
||||
for i := 0; i < n; i++ {
|
||||
if t := tokens[i]; !t.Kind.IsNumeric() && (t.Kind != css_lexer.TIdent || allowedIdent == "" || t.Text != allowedIdent) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
result[0] = tokens[0]
|
||||
if n > 1 {
|
||||
result[1] = tokens[1]
|
||||
} else {
|
||||
result[1] = result[0]
|
||||
}
|
||||
if n > 2 {
|
||||
result[2] = tokens[2]
|
||||
} else {
|
||||
result[2] = result[0]
|
||||
}
|
||||
if n > 3 {
|
||||
result[3] = tokens[3]
|
||||
} else {
|
||||
result[3] = result[1]
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func compactTokenQuad(a css_ast.Token, b css_ast.Token, c css_ast.Token, d css_ast.Token, minifyWhitespace bool) []css_ast.Token {
|
||||
tokens := []css_ast.Token{a, b, c, d}
|
||||
if tokens[3].EqualIgnoringWhitespace(tokens[1]) {
|
||||
if tokens[2].EqualIgnoringWhitespace(tokens[0]) {
|
||||
if tokens[1].EqualIgnoringWhitespace(tokens[0]) {
|
||||
tokens = tokens[:1]
|
||||
} else {
|
||||
tokens = tokens[:2]
|
||||
}
|
||||
} else {
|
||||
tokens = tokens[:3]
|
||||
}
|
||||
}
|
||||
for i := range tokens {
|
||||
var whitespace css_ast.WhitespaceFlags
|
||||
if !minifyWhitespace || i > 0 {
|
||||
whitespace |= css_ast.WhitespaceBefore
|
||||
}
|
||||
if i+1 < len(tokens) {
|
||||
whitespace |= css_ast.WhitespaceAfter
|
||||
}
|
||||
tokens[i].Whitespace = whitespace
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
|
||||
func (p *parser) processDeclarations(rules []css_ast.Rule, composesContext *composesContext) (rewrittenRules []css_ast.Rule) {
|
||||
margin := boxTracker{key: css_ast.DMargin, keyText: "margin", allowAuto: true}
|
||||
padding := boxTracker{key: css_ast.DPadding, keyText: "padding", allowAuto: false}
|
||||
inset := boxTracker{key: css_ast.DInset, keyText: "inset", allowAuto: true}
|
||||
borderRadius := borderRadiusTracker{}
|
||||
rewrittenRules = make([]css_ast.Rule, 0, len(rules))
|
||||
didWarnAboutComposes := false
|
||||
wouldClipColorFlag := false
|
||||
var declarationKeys map[string]struct{}
|
||||
|
||||
// Don't automatically generate the "inset" property if it's not supported
|
||||
if p.options.unsupportedCSSFeatures.Has(compat.InsetProperty) {
|
||||
inset.key = css_ast.DUnknown
|
||||
inset.keyText = ""
|
||||
}
|
||||
|
||||
// If this is a local class selector, track which CSS properties it declares.
|
||||
// This is used to warn when CSS "composes" is used incorrectly.
|
||||
if composesContext != nil {
|
||||
for _, ref := range composesContext.parentRefs {
|
||||
composes, ok := p.composes[ref]
|
||||
if !ok {
|
||||
composes = &css_ast.Composes{}
|
||||
p.composes[ref] = composes
|
||||
}
|
||||
properties := composes.Properties
|
||||
if properties == nil {
|
||||
properties = make(map[string]logger.Loc)
|
||||
composes.Properties = properties
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if decl, ok := rule.Data.(*css_ast.RDeclaration); ok && decl.Key != css_ast.DComposes {
|
||||
properties[decl.KeyText] = decl.KeyRange.Loc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(rules); i++ {
|
||||
rule := rules[i]
|
||||
rewrittenRules = append(rewrittenRules, rule)
|
||||
decl, ok := rule.Data.(*css_ast.RDeclaration)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the previous loop iteration would have clipped a color, we will
|
||||
// duplicate it and insert the clipped copy before the unclipped copy
|
||||
var wouldClipColor *bool
|
||||
if wouldClipColorFlag {
|
||||
wouldClipColorFlag = false
|
||||
clone := *decl
|
||||
clone.Value = css_ast.CloneTokensWithoutImportRecords(clone.Value)
|
||||
decl = &clone
|
||||
rule.Data = decl
|
||||
n := len(rewrittenRules) - 2
|
||||
rewrittenRules = append(rewrittenRules[:n], rule, rewrittenRules[n])
|
||||
} else {
|
||||
wouldClipColor = &wouldClipColorFlag
|
||||
}
|
||||
|
||||
switch decl.Key {
|
||||
case css_ast.DComposes:
|
||||
// Only process "composes" directives if we're in "local-css" or
|
||||
// "global-css" mode. In these cases, "composes" directives will always
|
||||
// be removed (because they are being processed) even if they contain
|
||||
// errors. Otherwise we leave "composes" directives there untouched and
|
||||
// don't check them for errors.
|
||||
if p.options.symbolMode != symbolModeDisabled {
|
||||
if composesContext == nil {
|
||||
if !didWarnAboutComposes {
|
||||
didWarnAboutComposes = true
|
||||
p.log.AddID(logger.MsgID_CSS_CSSSyntaxError, logger.Warning, &p.tracker, decl.KeyRange, "\"composes\" is not valid here")
|
||||
}
|
||||
} else if composesContext.problemRange.Len > 0 {
|
||||
if !didWarnAboutComposes {
|
||||
didWarnAboutComposes = true
|
||||
p.log.AddIDWithNotes(logger.MsgID_CSS_CSSSyntaxError, logger.Warning, &p.tracker, decl.KeyRange, "\"composes\" only works inside single class selectors",
|
||||
[]logger.MsgData{p.tracker.MsgData(composesContext.problemRange, "The parent selector is not a single class selector because of the syntax here:")})
|
||||
}
|
||||
} else {
|
||||
p.handleComposesPragma(*composesContext, decl.Value)
|
||||
}
|
||||
rewrittenRules = rewrittenRules[:len(rewrittenRules)-1]
|
||||
}
|
||||
|
||||
case css_ast.DBackground:
|
||||
for i, t := range decl.Value {
|
||||
t = p.lowerAndMinifyColor(t, wouldClipColor)
|
||||
t = p.lowerAndMinifyGradient(t, wouldClipColor)
|
||||
decl.Value[i] = t
|
||||
}
|
||||
|
||||
case css_ast.DBackgroundImage,
|
||||
css_ast.DBorderImage,
|
||||
css_ast.DMaskImage:
|
||||
|
||||
for i, t := range decl.Value {
|
||||
t = p.lowerAndMinifyGradient(t, wouldClipColor)
|
||||
decl.Value[i] = t
|
||||
}
|
||||
|
||||
case css_ast.DBackgroundColor,
|
||||
css_ast.DBorderBlockEndColor,
|
||||
css_ast.DBorderBlockStartColor,
|
||||
css_ast.DBorderBottomColor,
|
||||
css_ast.DBorderColor,
|
||||
css_ast.DBorderInlineEndColor,
|
||||
css_ast.DBorderInlineStartColor,
|
||||
css_ast.DBorderLeftColor,
|
||||
css_ast.DBorderRightColor,
|
||||
css_ast.DBorderTopColor,
|
||||
css_ast.DCaretColor,
|
||||
css_ast.DColor,
|
||||
css_ast.DColumnRuleColor,
|
||||
css_ast.DFill,
|
||||
css_ast.DFloodColor,
|
||||
css_ast.DLightingColor,
|
||||
css_ast.DOutlineColor,
|
||||
css_ast.DStopColor,
|
||||
css_ast.DStroke,
|
||||
css_ast.DTextDecorationColor,
|
||||
css_ast.DTextEmphasisColor:
|
||||
|
||||
if len(decl.Value) == 1 {
|
||||
decl.Value[0] = p.lowerAndMinifyColor(decl.Value[0], wouldClipColor)
|
||||
}
|
||||
|
||||
case css_ast.DTransform:
|
||||
if p.options.minifySyntax {
|
||||
decl.Value = p.mangleTransforms(decl.Value)
|
||||
}
|
||||
|
||||
case css_ast.DBoxShadow:
|
||||
decl.Value = p.lowerAndMangleBoxShadows(decl.Value, wouldClipColor)
|
||||
|
||||
// Container name
|
||||
case css_ast.DContainer:
|
||||
p.processContainerShorthand(decl.Value)
|
||||
case css_ast.DContainerName:
|
||||
p.processContainerName(decl.Value)
|
||||
|
||||
// Animation name
|
||||
case css_ast.DAnimation:
|
||||
p.processAnimationShorthand(decl.Value)
|
||||
case css_ast.DAnimationName:
|
||||
p.processAnimationName(decl.Value)
|
||||
|
||||
// List style
|
||||
case css_ast.DListStyle:
|
||||
p.processListStyleShorthand(decl.Value)
|
||||
case css_ast.DListStyleType:
|
||||
if len(decl.Value) == 1 {
|
||||
p.processListStyleType(&decl.Value[0])
|
||||
}
|
||||
|
||||
// Font
|
||||
case css_ast.DFont:
|
||||
if p.options.minifySyntax {
|
||||
decl.Value = p.mangleFont(decl.Value)
|
||||
}
|
||||
case css_ast.DFontFamily:
|
||||
if p.options.minifySyntax {
|
||||
if value, ok := p.mangleFontFamily(decl.Value); ok {
|
||||
decl.Value = value
|
||||
}
|
||||
}
|
||||
case css_ast.DFontWeight:
|
||||
if len(decl.Value) == 1 && p.options.minifySyntax {
|
||||
decl.Value[0] = p.mangleFontWeight(decl.Value[0])
|
||||
}
|
||||
|
||||
// Margin
|
||||
case css_ast.DMargin:
|
||||
if p.options.minifySyntax {
|
||||
margin.mangleSides(rewrittenRules, decl, p.options.minifyWhitespace)
|
||||
}
|
||||
case css_ast.DMarginTop:
|
||||
if p.options.minifySyntax {
|
||||
margin.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxTop)
|
||||
}
|
||||
case css_ast.DMarginRight:
|
||||
if p.options.minifySyntax {
|
||||
margin.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxRight)
|
||||
}
|
||||
case css_ast.DMarginBottom:
|
||||
if p.options.minifySyntax {
|
||||
margin.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxBottom)
|
||||
}
|
||||
case css_ast.DMarginLeft:
|
||||
if p.options.minifySyntax {
|
||||
margin.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxLeft)
|
||||
}
|
||||
|
||||
// Padding
|
||||
case css_ast.DPadding:
|
||||
if p.options.minifySyntax {
|
||||
padding.mangleSides(rewrittenRules, decl, p.options.minifyWhitespace)
|
||||
}
|
||||
case css_ast.DPaddingTop:
|
||||
if p.options.minifySyntax {
|
||||
padding.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxTop)
|
||||
}
|
||||
case css_ast.DPaddingRight:
|
||||
if p.options.minifySyntax {
|
||||
padding.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxRight)
|
||||
}
|
||||
case css_ast.DPaddingBottom:
|
||||
if p.options.minifySyntax {
|
||||
padding.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxBottom)
|
||||
}
|
||||
case css_ast.DPaddingLeft:
|
||||
if p.options.minifySyntax {
|
||||
padding.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxLeft)
|
||||
}
|
||||
|
||||
// Inset
|
||||
case css_ast.DInset:
|
||||
if p.options.unsupportedCSSFeatures.Has(compat.InsetProperty) {
|
||||
if decls, ok := p.lowerInset(rule.Loc, decl); ok {
|
||||
rewrittenRules = rewrittenRules[:len(rewrittenRules)-1]
|
||||
for i := range decls {
|
||||
rewrittenRules = append(rewrittenRules, decls[i])
|
||||
if p.options.minifySyntax {
|
||||
inset.mangleSide(rewrittenRules, decls[i].Data.(*css_ast.RDeclaration), p.options.minifyWhitespace, i)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if p.options.minifySyntax {
|
||||
inset.mangleSides(rewrittenRules, decl, p.options.minifyWhitespace)
|
||||
}
|
||||
case css_ast.DTop:
|
||||
if p.options.minifySyntax {
|
||||
inset.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxTop)
|
||||
}
|
||||
case css_ast.DRight:
|
||||
if p.options.minifySyntax {
|
||||
inset.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxRight)
|
||||
}
|
||||
case css_ast.DBottom:
|
||||
if p.options.minifySyntax {
|
||||
inset.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxBottom)
|
||||
}
|
||||
case css_ast.DLeft:
|
||||
if p.options.minifySyntax {
|
||||
inset.mangleSide(rewrittenRules, decl, p.options.minifyWhitespace, boxLeft)
|
||||
}
|
||||
|
||||
// Border radius
|
||||
case css_ast.DBorderRadius:
|
||||
if p.options.minifySyntax {
|
||||
borderRadius.mangleCorners(rewrittenRules, decl, p.options.minifyWhitespace)
|
||||
}
|
||||
case css_ast.DBorderTopLeftRadius:
|
||||
if p.options.minifySyntax {
|
||||
borderRadius.mangleCorner(rewrittenRules, decl, p.options.minifyWhitespace, borderRadiusTopLeft)
|
||||
}
|
||||
case css_ast.DBorderTopRightRadius:
|
||||
if p.options.minifySyntax {
|
||||
borderRadius.mangleCorner(rewrittenRules, decl, p.options.minifyWhitespace, borderRadiusTopRight)
|
||||
}
|
||||
case css_ast.DBorderBottomRightRadius:
|
||||
if p.options.minifySyntax {
|
||||
borderRadius.mangleCorner(rewrittenRules, decl, p.options.minifyWhitespace, borderRadiusBottomRight)
|
||||
}
|
||||
case css_ast.DBorderBottomLeftRadius:
|
||||
if p.options.minifySyntax {
|
||||
borderRadius.mangleCorner(rewrittenRules, decl, p.options.minifyWhitespace, borderRadiusBottomLeft)
|
||||
}
|
||||
}
|
||||
|
||||
if prefixes, ok := p.options.cssPrefixData[decl.Key]; ok {
|
||||
if declarationKeys == nil {
|
||||
// Only generate this map if it's needed
|
||||
declarationKeys = make(map[string]struct{})
|
||||
for _, rule := range rules {
|
||||
if decl, ok := rule.Data.(*css_ast.RDeclaration); ok {
|
||||
declarationKeys[decl.KeyText] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prefixes & compat.WebkitPrefix) != 0 {
|
||||
rewrittenRules = p.insertPrefixedDeclaration(rewrittenRules, "-webkit-", rule.Loc, decl, declarationKeys)
|
||||
}
|
||||
if (prefixes & compat.KhtmlPrefix) != 0 {
|
||||
rewrittenRules = p.insertPrefixedDeclaration(rewrittenRules, "-khtml-", rule.Loc, decl, declarationKeys)
|
||||
}
|
||||
if (prefixes & compat.MozPrefix) != 0 {
|
||||
rewrittenRules = p.insertPrefixedDeclaration(rewrittenRules, "-moz-", rule.Loc, decl, declarationKeys)
|
||||
}
|
||||
if (prefixes & compat.MsPrefix) != 0 {
|
||||
rewrittenRules = p.insertPrefixedDeclaration(rewrittenRules, "-ms-", rule.Loc, decl, declarationKeys)
|
||||
}
|
||||
if (prefixes & compat.OPrefix) != 0 {
|
||||
rewrittenRules = p.insertPrefixedDeclaration(rewrittenRules, "-o-", rule.Loc, decl, declarationKeys)
|
||||
}
|
||||
}
|
||||
|
||||
// If this loop iteration would have clipped a color, the out-of-gamut
|
||||
// colors will not be clipped and this flag will be set. We then set up the
|
||||
// next iteration of the loop to duplicate this rule and process it again
|
||||
// with color clipping enabled.
|
||||
if wouldClipColorFlag {
|
||||
if p.options.unsupportedCSSFeatures.Has(compat.ColorFunctions) {
|
||||
// Only do this if there was no previous instance of that property so
|
||||
// we avoid overwriting any manually-specified fallback values
|
||||
for j := len(rewrittenRules) - 2; j >= 0; j-- {
|
||||
if prev, ok := rewrittenRules[j].Data.(*css_ast.RDeclaration); ok && prev.Key == decl.Key {
|
||||
wouldClipColorFlag = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if wouldClipColorFlag {
|
||||
// If the code above would have clipped a color outside of the sRGB gamut,
|
||||
// process this rule again so we can generate the clipped version next time
|
||||
i -= 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
wouldClipColorFlag = false
|
||||
}
|
||||
}
|
||||
|
||||
// Compact removed rules
|
||||
if p.options.minifySyntax {
|
||||
end := 0
|
||||
for _, rule := range rewrittenRules {
|
||||
if rule.Data != nil {
|
||||
rewrittenRules[end] = rule
|
||||
end++
|
||||
}
|
||||
}
|
||||
rewrittenRules = rewrittenRules[:end]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *parser) insertPrefixedDeclaration(rules []css_ast.Rule, prefix string, loc logger.Loc, decl *css_ast.RDeclaration, declarationKeys map[string]struct{}) []css_ast.Rule {
|
||||
keyText := prefix + decl.KeyText
|
||||
|
||||
// Don't insert a prefixed declaration if there already is one
|
||||
if _, ok := declarationKeys[keyText]; ok {
|
||||
// We found a previous declaration with a matching prefixed property.
|
||||
// The value is ignored, which matches the behavior of "autoprefixer".
|
||||
return rules
|
||||
}
|
||||
|
||||
// Additional special cases for when the prefix applies
|
||||
switch decl.Key {
|
||||
case css_ast.DBackgroundClip:
|
||||
// The prefix is only needed for "background-clip: text"
|
||||
if len(decl.Value) != 1 || decl.Value[0].Kind != css_lexer.TIdent || !strings.EqualFold(decl.Value[0].Text, "text") {
|
||||
return rules
|
||||
}
|
||||
|
||||
case css_ast.DPosition:
|
||||
// The prefix is only needed for "position: sticky"
|
||||
if len(decl.Value) != 1 || decl.Value[0].Kind != css_lexer.TIdent || !strings.EqualFold(decl.Value[0].Text, "sticky") {
|
||||
return rules
|
||||
}
|
||||
|
||||
case css_ast.DWidth, css_ast.DMinWidth, css_ast.DMaxWidth,
|
||||
css_ast.DHeight, css_ast.DMinHeight, css_ast.DMaxHeight:
|
||||
// The prefix is only needed for "width: stretch"
|
||||
if len(decl.Value) != 1 || decl.Value[0].Kind != css_lexer.TIdent || !strings.EqualFold(decl.Value[0].Text, "stretch") {
|
||||
return rules
|
||||
}
|
||||
}
|
||||
|
||||
value := css_ast.CloneTokensWithoutImportRecords(decl.Value)
|
||||
|
||||
// Additional special cases for how to transform the contents
|
||||
switch decl.Key {
|
||||
case css_ast.DPosition:
|
||||
// The prefix applies to the value, not the property
|
||||
keyText = decl.KeyText
|
||||
value[0].Text = "-webkit-sticky"
|
||||
|
||||
case css_ast.DWidth, css_ast.DMinWidth, css_ast.DMaxWidth,
|
||||
css_ast.DHeight, css_ast.DMinHeight, css_ast.DMaxHeight:
|
||||
// The prefix applies to the value, not the property
|
||||
keyText = decl.KeyText
|
||||
|
||||
// This currently only applies to "stretch" (already checked above)
|
||||
switch prefix {
|
||||
case "-webkit-":
|
||||
value[0].Text = "-webkit-fill-available"
|
||||
case "-moz-":
|
||||
value[0].Text = "-moz-available"
|
||||
}
|
||||
|
||||
case css_ast.DUserSelect:
|
||||
// The prefix applies to the value as well as the property
|
||||
if prefix == "-moz-" && len(value) == 1 && value[0].Kind == css_lexer.TIdent && strings.EqualFold(value[0].Text, "none") {
|
||||
value[0].Text = "-moz-none"
|
||||
}
|
||||
|
||||
case css_ast.DMaskComposite:
|
||||
// WebKit uses different names for these values
|
||||
if prefix == "-webkit-" {
|
||||
for i, token := range value {
|
||||
if token.Kind == css_lexer.TIdent {
|
||||
switch token.Text {
|
||||
case "add":
|
||||
value[i].Text = "source-over"
|
||||
case "subtract":
|
||||
value[i].Text = "source-out"
|
||||
case "intersect":
|
||||
value[i].Text = "source-in"
|
||||
case "exclude":
|
||||
value[i].Text = "xor"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't change the key, manually search for a previous duplicate rule
|
||||
if keyText == decl.KeyText {
|
||||
for _, rule := range rules {
|
||||
if prevDecl, ok := rule.Data.(*css_ast.RDeclaration); ok && prevDecl.KeyText == keyText && css_ast.TokensEqual(prevDecl.Value, value, nil) {
|
||||
return rules
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite the latest declaration with the prefixed declaration
|
||||
rules[len(rules)-1] = css_ast.Rule{Loc: loc, Data: &css_ast.RDeclaration{
|
||||
KeyText: keyText,
|
||||
KeyRange: decl.KeyRange,
|
||||
Value: value,
|
||||
Important: decl.Important,
|
||||
}}
|
||||
|
||||
// Re-add the latest declaration after the inserted declaration
|
||||
rules = append(rules, css_ast.Rule{Loc: loc, Data: decl})
|
||||
return rules
|
||||
}
|
||||
|
||||
func (p *parser) lowerInset(loc logger.Loc, decl *css_ast.RDeclaration) ([]css_ast.Rule, bool) {
|
||||
if tokens, ok := expandTokenQuad(decl.Value, ""); ok {
|
||||
mask := ^css_ast.WhitespaceAfter
|
||||
if p.options.minifyWhitespace {
|
||||
mask = 0
|
||||
}
|
||||
for i := range tokens {
|
||||
tokens[i].Whitespace &= mask
|
||||
}
|
||||
return []css_ast.Rule{
|
||||
{Loc: loc, Data: &css_ast.RDeclaration{
|
||||
KeyText: "top",
|
||||
KeyRange: decl.KeyRange,
|
||||
Key: css_ast.DTop,
|
||||
Value: tokens[0:1],
|
||||
Important: decl.Important,
|
||||
}},
|
||||
{Loc: loc, Data: &css_ast.RDeclaration{
|
||||
KeyText: "right",
|
||||
KeyRange: decl.KeyRange,
|
||||
Key: css_ast.DRight,
|
||||
Value: tokens[1:2],
|
||||
Important: decl.Important,
|
||||
}},
|
||||
{Loc: loc, Data: &css_ast.RDeclaration{
|
||||
KeyText: "bottom",
|
||||
KeyRange: decl.KeyRange,
|
||||
Key: css_ast.DBottom,
|
||||
Value: tokens[2:3],
|
||||
Important: decl.Important,
|
||||
}},
|
||||
{Loc: loc, Data: &css_ast.RDeclaration{
|
||||
KeyText: "left",
|
||||
KeyRange: decl.KeyRange,
|
||||
Key: css_ast.DLeft,
|
||||
Value: tokens[3:4],
|
||||
Important: decl.Important,
|
||||
}},
|
||||
}, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
Reference in New Issue
Block a user