From 7e89a66aca080a0a2a1aecf83c0f90a770782eb4 Mon Sep 17 00:00:00 2001 From: Aron Date: Fri, 21 Feb 2025 19:58:01 +0800 Subject: [PATCH] Fix(Runtime): Create `Components Layer` twice, fixed #380 --- packages/core/src/core.ts | 4 ++-- packages/core/src/layer.ts | 7 ++++++- packages/runtime/e2e/init.ts | 10 ++++++---- packages/runtime/e2e/lifecycle.test.ts | 27 ++++++++++++++++++++++++++ packages/runtime/src/core.ts | 13 +++++++++++-- 5 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 packages/runtime/e2e/lifecycle.test.ts diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index da399eaee..370ef42f5 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -599,8 +599,8 @@ export default class MasterCSS { } destroy() { - // @ts-ignore - this.classUsages = new Map() + this.reset() + this.classUsages.clear() masterCSSs.splice(masterCSSs.indexOf(this), 1) return this } diff --git a/packages/core/src/layer.ts b/packages/core/src/layer.ts index dcfb83fd4..7fc658e39 100644 --- a/packages/core/src/layer.ts +++ b/packages/core/src/layer.ts @@ -104,7 +104,12 @@ export default class Layer { } // @ts-expect-error this.usages = {} - if (this.native) { + const nativeSheet = this.css.style?.sheet + if (this.native && nativeSheet) { + const foundIndex = findNativeCSSRuleIndex(nativeSheet.cssRules, this.native) + if (foundIndex !== -1) { + nativeSheet.deleteRule(foundIndex) + } this.native = null } } diff --git a/packages/runtime/e2e/init.ts b/packages/runtime/e2e/init.ts index f677fc369..5d9ee267a 100644 --- a/packages/runtime/e2e/init.ts +++ b/packages/runtime/e2e/init.ts @@ -9,10 +9,12 @@ const __dirname = dirname(__filename) export default async function init(page: Page, text?: string, config?: Config) { await page.evaluate(({ config, text }) => { if (config) window.masterCSSConfig = config - const style = document.createElement('style') - style.id = 'master' - if (text) style.textContent = text - document.head.appendChild(style) + if (text) { + const style = document.createElement('style') + style.id = 'master' + style.textContent = text + document.head.appendChild(style) + } }, { config, text }) await page.addScriptTag({ path: resolve(__dirname, '../dist/global.min.js') }) } \ No newline at end of file diff --git a/packages/runtime/e2e/lifecycle.test.ts b/packages/runtime/e2e/lifecycle.test.ts new file mode 100644 index 000000000..cd039cb2a --- /dev/null +++ b/packages/runtime/e2e/lifecycle.test.ts @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/test' +import init from './init' + +test('destroy on progressive', async ({ page }) => { + await init(page, '@layer base, theme, preset, components, general;') + await page.evaluate(() => { + document.body.classList.add('text:center') + }) + expect(await page.evaluate(() => globalThis.runtimeCSS.generalLayer.rules.length)).toBe(1) + expect(await page.evaluate(() => Array.from(globalThis.runtimeCSS.style?.sheet?.cssRules || []) + .filter(cssRule => cssRule === globalThis.runtimeCSS.generalLayer.native) + .length + )).toBe(1) + expect(await page.evaluate(() => Array.from(globalThis.runtimeCSS.style?.sheet?.cssRules || []).length)).toBe(2) + await page.evaluate(() => { + globalThis.runtimeCSS.destroy() + }) + expect(await page.evaluate(() => globalThis.runtimeCSS.generalLayer.rules.length)).toBe(0) + expect(await page.evaluate(() => Array.from(globalThis.runtimeCSS.style?.sheet?.cssRules || []).length)).toBe(1) + await page.evaluate(() => { + globalThis.runtimeCSS.init() + globalThis.runtimeCSS.observe() + document.body.classList.add('block') + document.body.classList.add('font:bold') + }) + expect(await page.evaluate(() => Array.from(globalThis.runtimeCSS.style?.sheet?.cssRules || []).length)).toBe(2) +}) \ No newline at end of file diff --git a/packages/runtime/src/core.ts b/packages/runtime/src/core.ts index c5c033178..f7c7a9189 100644 --- a/packages/runtime/src/core.ts +++ b/packages/runtime/src/core.ts @@ -4,9 +4,11 @@ import { type Config, SyntaxRule } from '@master/css' import './types/global' export class RuntimeCSS extends MasterCSS { + // @ts-expect-error readonly host: Element readonly observing = false readonly progressive = false + // @ts-expect-error readonly container: HTMLElement | ShadowRoot readonly observer?: MutationObserver @@ -15,16 +17,24 @@ export class RuntimeCSS extends MasterCSS { public customConfig: Config = defaultConfig ) { super(customConfig) + this.init() + } + + init() { const existingRuntimeCSS = (globalThis as any).runtimeCSSs.find((eachCSS: RuntimeCSS) => eachCSS.root === this.root) if (existingRuntimeCSS) throw new Error('Cannot create multiple RuntimeCSS instances for the same root element.') const rootConstructorName = this.root?.constructor.name if (rootConstructorName === 'HTMLDocument' || rootConstructorName === 'Document') { // @ts-ignore (this.root as Document).defaultView.globalThis.runtimeCSS = this + // @ts-ignore readonly this.container = (this.root as Document).head + // @ts-ignore readonly this.host = (this.root as Document).documentElement } else { + // @ts-ignore readonly this.container = this.root as RuntimeCSS['container'] + // @ts-ignore readonly this.host = (this.root as ShadowRoot).host } runtimeCSSs.push(this) @@ -398,8 +408,7 @@ export class RuntimeCSS extends MasterCSS { // @ts-ignore this.observing = false this.reset() - // @ts-expect-error - this.classUsages = {} + this.classUsages.clear() if (!this.progressive) { this.style?.remove() this.style = null