mirror of
https://github.com/hanzoai/esbuild.git
synced 2026-09-24 16:58:50 +00:00
css: adjust parser to allow --foo: {...}
This commit is contained in:
@@ -2309,7 +2309,50 @@ func (p *parser) scanForEndOfRule() (endOfRuleScan, int) {
|
||||
var initialStack [4]css_lexer.T
|
||||
stack := initialStack[:0]
|
||||
|
||||
// Small state machine to recognize "--foo:"
|
||||
const (
|
||||
customPropIdent = iota
|
||||
customPropColon
|
||||
customPropAccept
|
||||
customPropReject
|
||||
)
|
||||
customProp := customPropIdent
|
||||
|
||||
for i, t := range p.tokens[p.index:] {
|
||||
// People sometimes use CSS custom properties to store JSON. This is
|
||||
// problematic because the "{...}" braces look like a CSS block unless
|
||||
// this edge case is special-cased. See the (quite long) discussion on
|
||||
// https://github.com/w3c/csswg-drafts/issues/7961 for more information.
|
||||
//
|
||||
// I couldn't find any actual specification for how this is supposed to
|
||||
// work. Just a lot of discussion on GitHub. I did find this text, from
|
||||
// https://drafts.csswg.org/css-syntax/#consume-a-declaration:
|
||||
//
|
||||
// Otherwise, if decl’s value contains a top-level simple block with
|
||||
// an associated token of <{-token>, and also contains any other non-
|
||||
// <whitespace-token> value, return nothing. (That is, a top-level {}-
|
||||
// block is only allowed as the entire value of a non-custom property.)
|
||||
//
|
||||
// However, current browsers appear to accept custom properties with a
|
||||
// {}-block contained anywhere inside them, not just as the entire value.
|
||||
// So that's what I'm attempting to do here.
|
||||
if customProp < customPropAccept {
|
||||
switch customProp {
|
||||
case customPropIdent:
|
||||
if t.Kind == css_lexer.TIdent && strings.HasPrefix(p.source.Contents[t.Range.Loc.Start:], "--") {
|
||||
customProp = customPropColon
|
||||
} else if t.Kind != css_lexer.TWhitespace {
|
||||
customProp = customPropReject
|
||||
}
|
||||
case customPropColon:
|
||||
if t.Kind == css_lexer.TColon {
|
||||
customProp = customPropAccept
|
||||
} else if t.Kind != css_lexer.TWhitespace {
|
||||
customProp = customPropReject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch t.Kind {
|
||||
case css_lexer.TSemicolon:
|
||||
if len(stack) == 0 {
|
||||
@@ -2323,7 +2366,7 @@ func (p *parser) scanForEndOfRule() (endOfRuleScan, int) {
|
||||
stack = append(stack, css_lexer.TCloseBracket)
|
||||
|
||||
case css_lexer.TOpenBrace:
|
||||
if len(stack) == 0 {
|
||||
if len(stack) == 0 && customProp != customPropAccept {
|
||||
return endOfRuleOpenBrace, p.index + i
|
||||
}
|
||||
stack = append(stack, css_lexer.TCloseBrace)
|
||||
|
||||
@@ -1361,6 +1361,15 @@ func TestNestedSelector(t *testing.T) {
|
||||
"::placeholder {\n opacity: 0.5;\n}\n@layer base {\n ::placeholder {\n color: green;\n }\n}\n", "")
|
||||
}
|
||||
|
||||
func TestCustomPropertyContainingBraces(t *testing.T) {
|
||||
// CSS custom properties must be able to store JSON-like values
|
||||
// See: https://github.com/w3c/csswg-drafts/issues/7961
|
||||
expectPrinted(t, "div { --foo:hover{color:red}zoom:2; }", "div {\n --foo:hover{color:red}zoom:2;\n}\n", "")
|
||||
expectPrinted(t, "div { --foo:hover { color: red } zoom: 2; }", "div {\n --foo:hover { color: red } zoom: 2;\n}\n", "")
|
||||
expectPrinted(t, "div { --foo : hover { color : red } zoom : 2 ; }", "div {\n --foo: hover { color : red } zoom : 2 ;\n}\n", "")
|
||||
expectPrinted(t, "div { --foo + hover { color : red } zoom : 2 ; }", "div {\n --foo + hover {\n color: red;\n }\n zoom: 2;\n}\n", "")
|
||||
}
|
||||
|
||||
func TestBadQualifiedRules(t *testing.T) {
|
||||
expectPrinted(t, "$bad: rule;", "$bad: rule; {\n}\n", "<stdin>: WARNING: Unexpected \"$\"\n")
|
||||
expectPrinted(t, "$bad: rule; div { color: red }", "$bad: rule; div {\n color: red;\n}\n", "<stdin>: WARNING: Unexpected \"$\"\n")
|
||||
|
||||
@@ -277,19 +277,19 @@ func TestVerbatimWhitespace(t *testing.T) {
|
||||
expectPrintedMinify(t, "* { --x:[y ]; }", "*{--x:[y ]}")
|
||||
expectPrintedMinify(t, "* { --x:[ y]; }", "*{--x:[ y]}")
|
||||
|
||||
// Note: These cases now behave like qualified rules
|
||||
expectPrinted(t, "* { --x:{y}; }", "* {\n --x: {\n y;\n }\n}\n")
|
||||
expectPrinted(t, "* { --x:{y} ; }", "* {\n --x: {\n y;\n }\n}\n")
|
||||
expectPrinted(t, "* { --x: {y}; }", "* {\n --x: {\n y;\n }\n}\n")
|
||||
expectPrinted(t, "* { --x:{y }; }", "* {\n --x: {\n y;\n }\n}\n")
|
||||
expectPrinted(t, "* { --x:{ y}; }", "* {\n --x: {\n y;\n }\n}\n")
|
||||
// These cases temporarily behaved like qualified rules, but now they're back to being declarations
|
||||
expectPrinted(t, "* { --x:{y}; }", "* {\n --x:{y};\n}\n")
|
||||
expectPrinted(t, "* { --x:{y} ; }", "* {\n --x:{y} ;\n}\n")
|
||||
expectPrinted(t, "* { --x: {y}; }", "* {\n --x: {y};\n}\n")
|
||||
expectPrinted(t, "* { --x:{y }; }", "* {\n --x:{y };\n}\n")
|
||||
expectPrinted(t, "* { --x:{ y}; }", "* {\n --x:{ y};\n}\n")
|
||||
|
||||
// Note: These cases now behave like qualified rules
|
||||
// These cases temporarily behaved like qualified rules, but now they're back to being declarations
|
||||
expectPrintedMinify(t, "* { --x:{y}; }", "*{--x:{y}}")
|
||||
expectPrintedMinify(t, "* { --x:{y} ; }", "*{--x:{y}}")
|
||||
expectPrintedMinify(t, "* { --x: {y}; }", "*{--x:{y}}")
|
||||
expectPrintedMinify(t, "* { --x:{y }; }", "*{--x:{y}}")
|
||||
expectPrintedMinify(t, "* { --x:{ y}; }", "*{--x:{y}}")
|
||||
expectPrintedMinify(t, "* { --x:{y} ; }", "*{--x:{y} }")
|
||||
expectPrintedMinify(t, "* { --x: {y}; }", "*{--x: {y}}")
|
||||
expectPrintedMinify(t, "* { --x:{y }; }", "*{--x:{y }}")
|
||||
expectPrintedMinify(t, "* { --x:{ y}; }", "*{--x:{ y}}")
|
||||
|
||||
expectPrintedMinify(t, "@supports ( --x : y , z ) { a { color: red; } }", "@supports ( --x : y , z ){a{color:red}}")
|
||||
expectPrintedMinify(t, "@supports ( --x : ) { a { color: red; } }", "@supports ( --x : ){a{color:red}}")
|
||||
|
||||
Reference in New Issue
Block a user