diff --git a/internal/css_ast/css_ast.go b/internal/css_ast/css_ast.go index f0d7450286e..84e06fd47cb 100644 --- a/internal/css_ast/css_ast.go +++ b/internal/css_ast/css_ast.go @@ -35,9 +35,12 @@ type AST struct { GlobalScope map[string]ast.LocRef Composes map[ast.Ref]*Composes - // This contains all layer names in the file. It can be used to replace the - // layer-related side effects of importing this file. - Layers [][]string + // These contain all layer names in the file. It can be used to replace the + // layer-related side effects of importing this file. They are split into two + // groups (those before and after "@import" rules) so that the linker can put + // them in the right places. + LayersPreImport [][]string + LayersPostImport [][]string } type Composes struct { diff --git a/internal/css_parser/css_parser.go b/internal/css_parser/css_parser.go index 797fd3bdda2..7cfbaa47518 100644 --- a/internal/css_parser/css_parser.go +++ b/internal/css_parser/css_parser.go @@ -31,7 +31,8 @@ type parser struct { nestingWarnings map[logger.Loc]struct{} tracker logger.LineColumnTracker enclosingAtMedia [][]css_ast.Token - layers [][]string + layersPreImport [][]string + layersPostImport [][]string enclosingLayer []string anonLayerCount int index int @@ -42,6 +43,7 @@ type parser struct { options Options nestingIsPresent bool makeLocalSymbols bool + hasSeenAtImport bool } type Options struct { @@ -153,7 +155,8 @@ func Parse(log logger.Log, source logger.Source, options Options) css_ast.AST { LocalScope: p.localScope, GlobalScope: p.globalScope, Composes: p.composes, - Layers: p.layers, + LayersPreImport: p.layersPreImport, + LayersPostImport: p.layersPostImport, } } @@ -359,7 +362,7 @@ func (p *parser) recordAtLayerRule(layers [][]string) { clone := make([]string, 0, len(p.enclosingLayer)+len(layer)) layer = append(append(clone, p.enclosingLayer...), layer...) } - p.layers = append(p.layers, layer) + p.layersPostImport = append(p.layersPostImport, layer) } } @@ -1248,6 +1251,14 @@ abortRuleParser: Path: logger.Path{Text: path}, Range: r, }) + + // Fill in the pre-import layers once we see the first "@import" + if !p.hasSeenAtImport { + p.hasSeenAtImport = true + p.layersPreImport = p.layersPostImport + p.layersPostImport = nil + } + return css_ast.Rule{Loc: atRange.Loc, Data: &css_ast.RAtImport{ ImportRecordIndex: importRecordIndex, ImportConditions: importConditions, diff --git a/internal/linker/linker.go b/internal/linker/linker.go index 9e830868a57..45daf7df894 100644 --- a/internal/linker/linker.go +++ b/internal/linker/linker.go @@ -3496,7 +3496,8 @@ func (c *linkerContext) findImportedFilesInCSSOrder(entryPoints []uint32) (exter // Remove this redundant entry either if it has no named layers // or if it's wrapped in an anonymous layer without a name - hasNamedLayers := len(c.graph.Files[order.sourceIndex].InputFile.Repr.(*graph.CSSRepr).AST.Layers) > 0 + tree := c.graph.Files[order.sourceIndex].InputFile.Repr.(*graph.CSSRepr).AST + hasNamedLayers := len(tree.LayersPreImport) > 0 || len(tree.LayersPostImport) > 0 hasAnonymousLayer := false for _, conditions := range order.conditions { if len(conditions.Layers) == 1 { @@ -5891,9 +5892,17 @@ func (c *linkerContext) generateChunkCSS(chunkIndex int, chunkWaitGroup *sync.Wa } rules = append(rules, rule) } - } else if len(ast.Layers) > 0 { + } else if pre, post := len(ast.LayersPreImport), len(ast.LayersPostImport); pre > 0 || post > 0 { // Only include "@layer" information for redundant import order entries - rules = []css_ast.Rule{{Data: &css_ast.RAtLayer{Names: ast.Layers}}} + var layers [][]string + if pre == 0 { + layers = ast.LayersPostImport + } else if post == 0 { + layers = ast.LayersPreImport + } else { + layers = append(append(make([][]string, 0, pre+post), ast.LayersPreImport...), ast.LayersPostImport...) + } + rules = []css_ast.Rule{{Data: &css_ast.RAtLayer{Names: layers}}} } for i := len(entry.conditions) - 1; i >= 0; i-- {