Skip to content

Commit b5c241e

Browse files
committed
Refactor SyntaxRule attachment logic and enhance SyntaxLayer with variable and animation handling
1 parent e362582 commit b5c241e

File tree

10 files changed

+223
-140
lines changed

10 files changed

+223
-140
lines changed

packages/core/src/core.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ export default class MasterCSS {
619619
add(...classNames: string[]) {
620620
for (const className of classNames) {
621621
this.generate(className)
622-
.forEach((eachSyntaxRule) => eachSyntaxRule.attach())
622+
.forEach((eachSyntaxRule) => eachSyntaxRule.layer.insert(eachSyntaxRule))
623623
}
624624
return this
625625
}

packages/core/src/syntax-layer.ts

+32-26
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,36 @@ export default class SyntaxLayer extends Layer {
259259
}
260260
}
261261
super.insert(syntaxRule, index)
262+
this.insertVariables(syntaxRule)
263+
this.insertAnimations(syntaxRule)
264+
syntaxRule.definition.insert?.call(syntaxRule)
265+
return index
266+
}
267+
268+
delete(key: string) {
269+
const syntaxRule = super.delete(key) as SyntaxRule | undefined
270+
if (!syntaxRule) return
271+
if (syntaxRule.variableNames) {
272+
for (const eachVariableName of syntaxRule.variableNames) {
273+
if (!--this.css.themeLayer.usages[eachVariableName]) {
274+
this.css.themeLayer.delete(eachVariableName)
275+
delete this.css.themeLayer.usages[eachVariableName]
276+
}
277+
}
278+
}
279+
if (syntaxRule.animationNames) {
280+
for (const eachKeyframeName of syntaxRule.animationNames) {
281+
if (!--this.css.animationsNonLayer.usages[eachKeyframeName]) {
282+
this.css.animationsNonLayer.delete(eachKeyframeName)
283+
delete this.css.animationsNonLayer.usages[eachKeyframeName]
284+
}
285+
}
286+
}
287+
syntaxRule.definition.delete?.call(syntaxRule, syntaxRule.name)
288+
return syntaxRule
289+
}
290+
291+
insertVariables(syntaxRule: SyntaxRule) {
262292
if (syntaxRule.variableNames) {
263293
for (const eachVariableName of syntaxRule.variableNames) {
264294
const variable = this.css.variables[eachVariableName]
@@ -321,7 +351,9 @@ export default class SyntaxLayer extends Layer {
321351
}
322352
}
323353
}
354+
}
324355

356+
insertAnimations(syntaxRule: SyntaxRule) {
325357
if (syntaxRule.animationNames) {
326358
for (const eachAnimationName of syntaxRule.animationNames) {
327359
if (this.css.animationsNonLayer.rules.find(({ name }) => name === eachAnimationName)) {
@@ -343,31 +375,5 @@ export default class SyntaxLayer extends Layer {
343375
}
344376
}
345377
}
346-
347-
syntaxRule.definition.insert?.call(syntaxRule)
348-
return index
349-
}
350-
351-
delete(key: string) {
352-
const syntaxRule = super.delete(key) as SyntaxRule | undefined
353-
if (!syntaxRule) return
354-
if (syntaxRule.variableNames) {
355-
for (const eachVariableName of syntaxRule.variableNames) {
356-
if (!--this.css.themeLayer.usages[eachVariableName]) {
357-
this.css.themeLayer.delete(eachVariableName)
358-
delete this.css.themeLayer.usages[eachVariableName]
359-
}
360-
}
361-
}
362-
if (syntaxRule.animationNames) {
363-
for (const eachKeyframeName of syntaxRule.animationNames) {
364-
if (!--this.css.animationsNonLayer.usages[eachKeyframeName]) {
365-
this.css.animationsNonLayer.delete(eachKeyframeName)
366-
delete this.css.animationsNonLayer.usages[eachKeyframeName]
367-
}
368-
}
369-
}
370-
syntaxRule.definition.delete?.call(syntaxRule, syntaxRule.name)
371-
return syntaxRule
372378
}
373379
}

packages/core/src/syntax-rule.ts

-4
Original file line numberDiff line numberDiff line change
@@ -477,10 +477,6 @@ export class SyntaxRule extends Rule {
477477
}
478478
}
479479

480-
attach() {
481-
this.layer.insert(this)
482-
}
483-
484480
resolveAtComponent(atComponent: AtComponent) {
485481
switch (atComponent.type) {
486482
case 'arbitrary':

packages/extractor/src/core.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ export default class CSSExtractor extends EventEmitter {
175175
const validRules = generateValidRules(eachLatentClass, this.css)
176176
if (validRules.length) {
177177
for (const validRule of validRules) {
178-
validRule.attach()
178+
validRule.layer.insert(validRule)
179179
}
180180
validClasses.push(eachLatentClass)
181181
this.validClasses.add(eachLatentClass)

packages/runtime/e2e/progressive/comprehensive/index.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ test('basic', async ({ page }) => {
1414
await page.evaluate((html) => document.body.innerHTML = html, prerenderHTML)
1515
await init(page, prerenderCSS, config)
1616
const rules = await page.evaluate(() => globalThis.runtimeCSS.rules)
17-
expect(rules.map(({ name }) => name)).toEqual(['layer-statement', 'base', 'theme', 'preset', 'styles', 'general', 'fade'])
17+
expect(rules.map(({ name }) => name)).toEqual(['layer-statement', 'theme', 'fade', 'base', 'preset', 'styles', 'general'])
1818
})

packages/runtime/src/core.ts

+118-106
Original file line numberDiff line numberDiff line change
@@ -242,138 +242,150 @@ export class RuntimeCSS extends MasterCSS {
242242
}
243243

244244
hydrate(nativeCSSRules: CSSRuleList) {
245+
const cssSyntaxLayerBlockRules: CSSLayerBlockRule[] = []
245246
for (let i = 0; i < nativeCSSRules.length; i++) {
246247
const eachNativeCSSRule = nativeCSSRules[i]
247248
if (eachNativeCSSRule.constructor.name === 'CSSLayerBlockRule') {
248249
const cssLayerBlockRule = eachNativeCSSRule as CSSLayerBlockRule
249-
const handleSyntaxLayer = (layer: SyntaxLayer) => {
250-
layer.native = cssLayerBlockRule
250+
if ((eachNativeCSSRule as CSSLayerBlockRule).name === 'theme') {
251+
this.themeLayer.native = cssLayerBlockRule
252+
let variableRule: Rule | undefined
253+
let lastVariableName: string | undefined
251254
for (let j = 0; j < cssLayerBlockRule.cssRules.length; j++) {
252-
const cssRule = cssLayerBlockRule.cssRules[j] as CSSStyleRule
255+
const cssRule = cssLayerBlockRule.cssRules[j]
256+
const variableCSSRule = (cssRule.constructor.name === 'CSSMediaRule'
257+
? (cssRule as CSSMediaRule).cssRules[0]
258+
: cssRule) as CSSStyleRule
259+
const variableName = variableCSSRule.style[0].slice(2)
260+
if (variableName !== lastVariableName) {
261+
lastVariableName = variableName
262+
variableRule = new Rule(variableName)
263+
this.themeLayer.rules.push(variableRule)
264+
this.themeLayer.usages[variableRule.name] = 0
265+
}
266+
variableRule?.nodes.push({
267+
native: cssRule,
268+
text: cssRule.cssText
269+
})
270+
}
271+
if (this.themeLayer.rules.length) this.rules.push(this.themeLayer)
272+
} else {
273+
cssSyntaxLayerBlockRules.push(cssLayerBlockRule)
274+
}
275+
} else if (eachNativeCSSRule.constructor.name === 'CSSLayerStatementRule') {
276+
this.layerStatementRule.nodes[0].native = eachNativeCSSRule as CSSLayerStatementRule
277+
} else if (eachNativeCSSRule.constructor.name === 'CSSKeyframesRule') {
278+
const keyframsRule = eachNativeCSSRule as CSSKeyframesRule
279+
const animationRule = new Rule(keyframsRule.name, [{
280+
native: keyframsRule,
281+
text: keyframsRule.cssText
282+
}])
283+
this.animationsNonLayer.rules.push(animationRule)
284+
this.rules.push(animationRule)
285+
this.animationsNonLayer.usages[animationRule.name] = 0
286+
}
287+
}
288+
289+
for (const cssLayerBlockRule of cssSyntaxLayerBlockRules) {
290+
const handleSyntaxLayer = (layer: SyntaxLayer) => {
291+
layer.native = cssLayerBlockRule
292+
for (let j = 0; j < cssLayerBlockRule.cssRules.length; j++) {
293+
const cssRule = cssLayerBlockRule.cssRules[j] as CSSStyleRule
294+
const createSyntaxRule = (cssRule: CSSStyleRule): SyntaxRule | undefined => {
253295
if (cssRule.selectorText) {
254296
const syntaxRule = this.createFromSelectorText(cssRule.selectorText)[0]
255-
if (syntaxRule) {
256-
layer.rules.push(syntaxRule)
257-
syntaxRule.definition.insert?.call(syntaxRule)
258-
for (const eachNode of syntaxRule.nodes) {
259-
if (!eachNode.native) eachNode.native = cssRule
260-
}
261-
} else {
262-
cssLayerBlockRule.deleteRule?.(j--)
263-
console.error(`Cannot recognize the CSS rule in the ${layer.name} layer. \`${cssRule.cssText}\` (https://rc.css.master.co/messages/hydration-errors)`)
264-
}
297+
if (syntaxRule) return syntaxRule
265298
} else if (cssRule.cssRules) {
266299
for (const eachCSSRule of cssRule.cssRules) {
267-
const syntaxRule = this.createFromSelectorText((eachCSSRule as CSSStyleRule).selectorText)?.[0]
300+
const syntaxRule = createSyntaxRule(eachCSSRule as CSSStyleRule)
268301
if (syntaxRule) return syntaxRule
269302
}
270303
}
271304
}
272-
for (const eachRule of layer.rules) {
273-
for (let k = eachRule.nodes.length - 1; k >= 0; k--) {
274-
if (!eachRule.nodes[k].native) {
275-
eachRule.nodes.splice(k, 1)
276-
}
305+
const syntaxRule = createSyntaxRule(cssRule)
306+
if (syntaxRule) {
307+
layer.rules.push(syntaxRule)
308+
layer.insertVariables(syntaxRule)
309+
layer.insertAnimations(syntaxRule)
310+
for (const eachNode of syntaxRule.nodes) {
311+
if (!eachNode.native) eachNode.native = cssRule
277312
}
313+
} else {
314+
cssLayerBlockRule.deleteRule?.(j--)
315+
console.error(`Cannot recognize the CSS rule in the ${layer.name} layer. \`${cssRule.cssText}\` (https://rc.css.master.co/messages/hydration-errors)`)
278316
}
279-
if (layer.rules.length) this.rules.push(layer)
280317
}
281-
switch (cssLayerBlockRule.name) {
282-
case 'base':
283-
handleSyntaxLayer(this.baseLayer)
284-
break
285-
case 'theme':
286-
this.themeLayer.native = cssLayerBlockRule
287-
let variableRule: Rule | undefined
288-
let lastVariableName: string | undefined
289-
for (let j = 0; j < cssLayerBlockRule.cssRules.length; j++) {
290-
const cssRule = cssLayerBlockRule.cssRules[j]
291-
const variableCSSRule = (cssRule.constructor.name === 'CSSMediaRule'
292-
? (cssRule as CSSMediaRule).cssRules[0]
293-
: cssRule) as CSSStyleRule
294-
const variableName = variableCSSRule.style[0].slice(2)
295-
if (variableName !== lastVariableName) {
296-
lastVariableName = variableName
297-
variableRule = new Rule(variableName)
298-
this.themeLayer.rules.push(variableRule)
299-
this.themeLayer.usages[variableRule.name] = 1
300-
}
301-
variableRule?.nodes.push({
302-
native: cssRule,
303-
text: cssRule.cssText
304-
})
318+
for (const eachRule of layer.rules) {
319+
for (let k = eachRule.nodes.length - 1; k >= 0; k--) {
320+
if (!eachRule.nodes[k].native) {
321+
eachRule.nodes.splice(k, 1)
305322
}
306-
if (this.themeLayer.rules.length) this.rules.push(this.themeLayer)
307-
break
308-
case 'preset':
309-
handleSyntaxLayer(this.presetLayer)
310-
break
311-
case 'styles':
312-
this.stylesLayer.native = cssLayerBlockRule
313-
let stylePreText: string
314-
const createSyntaxRules = (cssRule: any): SyntaxRule[] | undefined => {
315-
if (cssRule.selectorText) {
316-
const syntaxRules = this.createFromSelectorText(cssRule.selectorText)
317-
if (syntaxRules.length) {
318-
stylePreText = cssRule.selectorText + '{'
319-
return syntaxRules
320-
}
321-
} else if (cssRule.cssRules) {
322-
for (const eachCSSRule of cssRule.cssRules) {
323-
const syntaxRules = this.createFromSelectorText((eachCSSRule as CSSStyleRule).selectorText)
324-
if (syntaxRules) return syntaxRules
325-
}
323+
}
324+
}
325+
if (layer.rules.length) this.rules.push(layer)
326+
}
327+
switch (cssLayerBlockRule.name) {
328+
case 'base':
329+
handleSyntaxLayer(this.baseLayer)
330+
break
331+
case 'preset':
332+
handleSyntaxLayer(this.presetLayer)
333+
break
334+
case 'styles':
335+
this.stylesLayer.native = cssLayerBlockRule
336+
let stylePreText: string
337+
const createSyntaxRules = (cssRule: any): SyntaxRule[] | undefined => {
338+
if (cssRule.selectorText) {
339+
const syntaxRules = this.createFromSelectorText(cssRule.selectorText)
340+
if (syntaxRules.length) {
341+
stylePreText = cssRule.selectorText + '{'
342+
return syntaxRules
343+
}
344+
} else if (cssRule.cssRules) {
345+
for (const eachCSSRule of cssRule.cssRules) {
346+
const syntaxRules = this.createFromSelectorText((eachCSSRule as CSSStyleRule).selectorText)
347+
if (syntaxRules) return syntaxRules
326348
}
327349
}
328-
for (let j = 0; j < cssLayerBlockRule.cssRules.length; j++) {
329-
const cssRule = cssLayerBlockRule.cssRules[j] as CSSStyleRule
330-
const syntaxRules = createSyntaxRules(cssRule)
331-
if (syntaxRules) {
332-
let matched = false
333-
for (const eachSyntaxRule of syntaxRules) {
334-
for (const node of eachSyntaxRule.nodes) {
335-
if (!node.native && node.text.includes(stylePreText!)) {
336-
node.native = cssRule
337-
if (!this.stylesLayer.rules.includes(eachSyntaxRule)) {
338-
this.stylesLayer.rules.push(eachSyntaxRule)
339-
eachSyntaxRule.definition.insert?.call(eachSyntaxRule)
340-
}
341-
matched = true
342-
break
350+
}
351+
for (let j = 0; j < cssLayerBlockRule.cssRules.length; j++) {
352+
const cssRule = cssLayerBlockRule.cssRules[j] as CSSStyleRule
353+
const syntaxRules = createSyntaxRules(cssRule)
354+
if (syntaxRules) {
355+
let matched = false
356+
for (const eachSyntaxRule of syntaxRules) {
357+
for (const node of eachSyntaxRule.nodes) {
358+
if (!node.native && node.text.includes(stylePreText!)) {
359+
node.native = cssRule
360+
if (!this.stylesLayer.rules.includes(eachSyntaxRule)) {
361+
this.stylesLayer.rules.push(eachSyntaxRule)
362+
this.stylesLayer.insertVariables(eachSyntaxRule)
363+
this.stylesLayer.insertAnimations(eachSyntaxRule)
343364
}
344-
}
345-
if (matched)
365+
matched = true
346366
break
367+
}
347368
}
348-
} else {
349-
cssLayerBlockRule?.deleteRule?.(j--)
350-
console.error(`Cannot recognize the CSS rule in the styles layer. \`${cssRule.cssText}\` (https://rc.css.master.co/messages/hydration-errors)`)
369+
if (matched)
370+
break
351371
}
372+
} else {
373+
cssLayerBlockRule?.deleteRule?.(j--)
374+
console.error(`Cannot recognize the CSS rule in the styles layer. \`${cssRule.cssText}\` (https://rc.css.master.co/messages/hydration-errors)`)
352375
}
353-
for (const eachRule of this.stylesLayer.rules) {
354-
for (let k = eachRule.nodes.length - 1; k >= 0; k--) {
355-
if (!eachRule.nodes[k].native) {
356-
eachRule.nodes.splice(k, 1)
357-
}
376+
}
377+
for (const eachRule of this.stylesLayer.rules) {
378+
for (let k = eachRule.nodes.length - 1; k >= 0; k--) {
379+
if (!eachRule.nodes[k].native) {
380+
eachRule.nodes.splice(k, 1)
358381
}
359382
}
360-
if (this.stylesLayer.rules.length) this.rules.push(this.stylesLayer)
361-
break
362-
case 'general':
363-
handleSyntaxLayer(this.generalLayer)
364-
break
365-
}
366-
} else if (eachNativeCSSRule.constructor.name === 'CSSLayerStatementRule') {
367-
this.layerStatementRule.nodes[0].native = eachNativeCSSRule as CSSLayerStatementRule
368-
} else if (eachNativeCSSRule.constructor.name === 'CSSKeyframesRule') {
369-
const keyframsRule = eachNativeCSSRule as CSSKeyframesRule
370-
const animationRule = new Rule(keyframsRule.name, [{
371-
native: keyframsRule,
372-
text: keyframsRule.cssText
373-
}])
374-
this.animationsNonLayer.rules.push(animationRule)
375-
this.rules.push(animationRule)
376-
this.animationsNonLayer.usages[animationRule.name] = 1
383+
}
384+
if (this.stylesLayer.rules.length) this.rules.push(this.stylesLayer)
385+
break
386+
case 'general':
387+
handleSyntaxLayer(this.generalLayer)
388+
break
377389
}
378390
}
379391
}

0 commit comments

Comments
 (0)