Skip to content

Commit

Permalink
css: emit mappings for subclass selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jul 16, 2023
1 parent c6e14ef commit 9410725
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 23 deletions.
8 changes: 8 additions & 0 deletions internal/css_ast/css_ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,13 @@ type NamespacedName struct {
Name NameToken
}

func (n NamespacedName) FirstLoc() logger.Loc {
if n.NamespacePrefix != nil {
return n.NamespacePrefix.Loc
}
return n.Name.Loc
}

func (n NamespacedName) Clone() NamespacedName {
clone := n
if n.NamespacePrefix != nil {
Expand All @@ -796,6 +803,7 @@ func (a NamespacedName) Equal(b NamespacedName) bool {

type SubclassSelector struct {
Data SS
Loc logger.Loc
}

type SS interface {
Expand Down
58 changes: 38 additions & 20 deletions internal/css_parser/css_nesting.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ package css_parser
import (
"github.com/evanw/esbuild/internal/ast"
"github.com/evanw/esbuild/internal/css_ast"
"github.com/evanw/esbuild/internal/logger"
)

func lowerNestingInRule(rule css_ast.Rule, results []css_ast.Rule) []css_ast.Rule {
switch r := rule.Data.(type) {
case *css_ast.RSelector:
scope := css_ast.ComplexSelector{
Selectors: []css_ast.CompoundSelector{{
SubclassSelectors: []css_ast.SubclassSelector{{
Data: &css_ast.SSPseudoClass{Name: "scope"},
scope := func(loc logger.Loc) css_ast.ComplexSelector {
return css_ast.ComplexSelector{
Selectors: []css_ast.CompoundSelector{{
SubclassSelectors: []css_ast.SubclassSelector{{
Loc: loc,
Data: &css_ast.SSPseudoClass{Name: "scope"},
}},
}},
}},
}
}

// Filter out pseudo elements because they are ignored by nested style
Expand Down Expand Up @@ -158,7 +162,7 @@ func lowerNestingInRuleWithContext(rule css_ast.Rule, context *lowerNestingConte
sel := &r.Selectors[i]
sel.Selectors = sel.Selectors[1:]
}
merged := multipleComplexSelectorsToSingleComplexSelector(r.Selectors)
merged := multipleComplexSelectorsToSingleComplexSelector(r.Selectors)(rule.Loc)
merged.Selectors = append([]css_ast.CompoundSelector{{NestingSelectorLoc: nestingSelectorLoc}}, merged.Selectors...)
r.Selectors = []css_ast.ComplexSelector{merged}
} else if canUseGroupSubSelector {
Expand All @@ -169,7 +173,7 @@ func lowerNestingInRuleWithContext(rule css_ast.Rule, context *lowerNestingConte
sel := &r.Selectors[i]
sel.Selectors[0].NestingSelectorLoc = ast.Index32{}
}
merged := multipleComplexSelectorsToSingleComplexSelector(r.Selectors)
merged := multipleComplexSelectorsToSingleComplexSelector(r.Selectors)(rule.Loc)
merged.Selectors[0].NestingSelectorLoc = nestingSelectorLoc
r.Selectors = []css_ast.ComplexSelector{merged}
}
Expand Down Expand Up @@ -239,9 +243,16 @@ const (
stripLeadingCombinator
)

func substituteAmpersandsInCompoundSelector(sel css_ast.CompoundSelector, replacement css_ast.ComplexSelector, results []css_ast.CompoundSelector, strip leadingCombinatorStrip) []css_ast.CompoundSelector {
func substituteAmpersandsInCompoundSelector(
sel css_ast.CompoundSelector,
replacementFn func(logger.Loc) css_ast.ComplexSelector,
results []css_ast.CompoundSelector,
strip leadingCombinatorStrip,
) []css_ast.CompoundSelector {
if sel.HasNestingSelector() {
nestingSelectorLoc := logger.Loc{Start: int32(sel.NestingSelectorLoc.GetIndex())}
sel.NestingSelectorLoc = ast.Index32{}
replacement := replacementFn(nestingSelectorLoc)

// Convert the replacement to a single compound selector
var single css_ast.CompoundSelector
Expand All @@ -266,6 +277,7 @@ func substituteAmpersandsInCompoundSelector(sel css_ast.CompoundSelector, replac
// ".foo .bar { > &:hover {} }" => ".foo .bar > :is(.foo .bar):hover {}"
single = css_ast.CompoundSelector{
SubclassSelectors: []css_ast.SubclassSelector{{
Loc: nestingSelectorLoc,
Data: &css_ast.SSPseudoClassWithSelectorList{
Kind: css_ast.PseudoClassIs,
Selectors: []css_ast.ComplexSelector{replacement.CloneWithoutLeadingCombinator()},
Expand All @@ -280,6 +292,7 @@ func substituteAmpersandsInCompoundSelector(sel css_ast.CompoundSelector, replac
if single.TypeSelector != nil {
if sel.TypeSelector != nil {
subclassSelectorPrefix = append(subclassSelectorPrefix, css_ast.SubclassSelector{
Loc: sel.TypeSelector.FirstLoc(),
Data: &css_ast.SSPseudoClassWithSelectorList{
Kind: css_ast.PseudoClassIs,
Selectors: []css_ast.ComplexSelector{{Selectors: []css_ast.CompoundSelector{{TypeSelector: sel.TypeSelector}}}},
Expand All @@ -305,7 +318,7 @@ func substituteAmpersandsInCompoundSelector(sel css_ast.CompoundSelector, replac
for _, complex := range class.Selectors {
inner := make([]css_ast.CompoundSelector, 0, len(complex.Selectors))
for _, sel := range complex.Selectors {
inner = substituteAmpersandsInCompoundSelector(sel, replacement, inner, stripLeadingCombinator)
inner = substituteAmpersandsInCompoundSelector(sel, replacementFn, inner, stripLeadingCombinator)
}
outer = append(outer, css_ast.ComplexSelector{Selectors: inner})
}
Expand All @@ -319,9 +332,11 @@ func substituteAmpersandsInCompoundSelector(sel css_ast.CompoundSelector, replac
// Turn the list of selectors into a single selector by wrapping lists
// without a single element with ":is(...)". Note that this may result
// in an empty ":is()" selector (which matches nothing).
func multipleComplexSelectorsToSingleComplexSelector(selectors []css_ast.ComplexSelector) css_ast.ComplexSelector {
func multipleComplexSelectorsToSingleComplexSelector(selectors []css_ast.ComplexSelector) func(logger.Loc) css_ast.ComplexSelector {
if len(selectors) == 1 {
return selectors[0]
return func(logger.Loc) css_ast.ComplexSelector {
return selectors[0]
}
}

var leadingCombinator css_ast.Combinator
Expand All @@ -333,15 +348,18 @@ func multipleComplexSelectorsToSingleComplexSelector(selectors []css_ast.Complex
clones[i] = sel.CloneWithoutLeadingCombinator()
}

return css_ast.ComplexSelector{
Selectors: []css_ast.CompoundSelector{{
Combinator: leadingCombinator,
SubclassSelectors: []css_ast.SubclassSelector{{
Data: &css_ast.SSPseudoClassWithSelectorList{
Kind: css_ast.PseudoClassIs,
Selectors: clones,
},
return func(loc logger.Loc) css_ast.ComplexSelector {
return css_ast.ComplexSelector{
Selectors: []css_ast.CompoundSelector{{
Combinator: leadingCombinator,
SubclassSelectors: []css_ast.SubclassSelector{{
Loc: loc,
Data: &css_ast.SSPseudoClassWithSelectorList{
Kind: css_ast.PseudoClassIs,
Selectors: clones,
},
}},
}},
}},
}
}
}
7 changes: 7 additions & 0 deletions internal/css_parser/css_parser_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ subclassSelectors:
nameLoc := logger.Loc{Start: subclassToken.Range.Loc.Start + 1}
name := p.decoded()
sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{
Loc: subclassToken.Range.Loc,
Data: &css_ast.SSHash{
Name: ast.LocRef{Loc: nameLoc, Ref: p.symbolForName(name)},
},
Expand All @@ -317,6 +318,7 @@ subclassSelectors:
nameLoc := p.current().Range.Loc
name := p.decoded()
sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{
Loc: subclassToken.Range.Loc,
Data: &css_ast.SSClass{
Name: ast.LocRef{Loc: nameLoc, Ref: p.symbolForName(name)},
},
Expand All @@ -331,13 +333,15 @@ subclassSelectors:
return
}
sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{
Loc: subclassToken.Range.Loc,
Data: &attr,
})

case css_lexer.TColon:
if p.next().Kind == css_lexer.TColon {
// Special-case the start of the pseudo-element selector section
for p.current().Kind == css_lexer.TColon {
firstColonLoc := p.current().Range.Loc
isElement := p.next().Kind == css_lexer.TColon
if isElement {
p.advance()
Expand All @@ -359,12 +363,15 @@ subclassSelectors:
}

sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{
Loc: firstColonLoc,
Data: pseudo,
})
}
break subclassSelectors
}

sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{
Loc: subclassToken.Range.Loc,
Data: p.parsePseudoClassSelector(false),
})

Expand Down
7 changes: 4 additions & 3 deletions internal/css_printer/css_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,6 @@ func (p *printer) printRule(rule css_ast.Rule, indent int32, omitTrailingSemicol
}

case *css_ast.RSelector:
if p.options.AddSourceMappings {
p.builder.AddSourceMapping(rule.Loc, "", p.css)
}
p.printComplexSelectors(r.Selectors, indent, layoutMultiLine)
if !p.options.MinifyWhitespace {
p.print(" ")
Expand Down Expand Up @@ -465,6 +462,10 @@ func (p *printer) printCompoundSelector(sel css_ast.CompoundSelector, isFirst bo
whitespace = canDiscardWhitespaceAfter
}

if p.options.AddSourceMappings {
p.builder.AddSourceMapping(ss.Loc, "", p.css)
}

switch s := ss.Data.(type) {
case *css_ast.SSHash:
p.print("#")
Expand Down

0 comments on commit 9410725

Please sign in to comment.