Skip to content

Commit

Permalink
feat(runtime)!: split uno-layer into separate <style> (#2131)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
chu121su12 and antfu committed Feb 22, 2023
1 parent 022a76f commit 7f20f85
Show file tree
Hide file tree
Showing 5 changed files with 430 additions and 39 deletions.
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -66,6 +66,7 @@
"@unocss/preset-web-fonts": "workspace:*",
"@unocss/preset-wind": "workspace:*",
"@unocss/reset": "workspace:*",
"@unocss/runtime": "workspace:*",
"@unocss/transformer-directives": "workspace:*",
"@unocss/transformer-variant-group": "workspace:*",
"@unocss/vite": "workspace:*",
Expand All @@ -84,6 +85,7 @@
"fast-glob": "^3.2.12",
"fs-extra": "^11.1.0",
"gzip-size": "^6.0.0",
"jsdom": "^21.1.0",
"lint-staged": "^13.1.2",
"lz-string": "^1.4.4",
"magic-string": "^0.27.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/config.ts
Expand Up @@ -47,7 +47,7 @@ export function resolveConfig<Theme extends {} = {}>(
...rawPresets.filter(p => p.enforce === 'post'),
]

const layers = Object.assign(DEFAULT_LAYERS, ...rawPresets.map(i => i.layers), userConfig.layers)
const layers = Object.assign({}, DEFAULT_LAYERS, ...rawPresets.map(i => i.layers), config.layers)

function mergePresets<T extends 'rules' | 'variants' | 'extractors' | 'shortcuts' | 'preflights' | 'preprocess' | 'postprocess' | 'extendTheme' | 'safelist' | 'separators'>(key: T): Required<UserConfig<Theme>>[T] {
return uniq([
Expand Down
60 changes: 44 additions & 16 deletions packages/runtime/src/index.ts
Expand Up @@ -2,6 +2,11 @@ import type { GenerateResult, UnoGenerator, UserConfig, UserConfigDefaults } fro
import { createGenerator, isString, toArray } from '@unocss/core'
import { autoPrefixer, decodeHtml } from './utils'

export interface RuntimeGenerateResult extends GenerateResult {
getStyleElement(name: string): HTMLStyleElement | undefined
getStyleElements(): Map<string, HTMLStyleElement>
}

export interface RuntimeOptions {
/**
* Default config of UnoCSS
Expand Down Expand Up @@ -72,9 +77,9 @@ export interface RuntimeContext {
/**
* Manually run the update cycle.
*
* @returns {GenerateResult & { styleElement: HTMLStyleElement}}
* @returns {RuntimeGenerateResult}
*/
update: () => Promise<GenerateResult & { styleElement: HTMLStyleElement }>
update: () => Promise<RuntimeGenerateResult>

/**
* The UnoCSS version.
Expand All @@ -98,7 +103,8 @@ export default function init(inlineConfig: RuntimeOptions = {}) {
}

const defaultWindow = window
const defaultDocument = document
const defaultDocument = window.document
const html = () => defaultDocument.documentElement

const userConfig = defaultWindow.__unocss || {}
const runtimeOptions = Object.assign({}, inlineConfig, userConfig.runtime)
Expand All @@ -111,10 +117,10 @@ export default function init(inlineConfig: RuntimeOptions = {}) {

runtimeOptions.configResolved?.(userConfig, userConfigDefaults)
const uno = createGenerator(userConfig, userConfigDefaults)
const styleElements = new Map<string, HTMLStyleElement>()

let paused = true
let tokens = new Set<string>()
let styleElement: HTMLStyleElement | undefined
let inspector: RuntimeInspectorCallback | undefined

let _timer: number | undefined
Expand All @@ -141,22 +147,42 @@ export default function init(inlineConfig: RuntimeOptions = {}) {
})
}

function getStyleElement() {
function getStyleElement(layer: string, previousLayer?: string) {
let styleElement = styleElements.get(layer)

if (!styleElement) {
styleElement = defaultDocument.createElement('style')
defaultDocument.documentElement.prepend(styleElement)
styleElements.set(layer, styleElement)

if (previousLayer == null) {
html().prepend(styleElement)
}
else {
const previousStyle = getStyleElement(previousLayer)
const parentNode = previousStyle.parentNode
if (parentNode)
parentNode.insertBefore(styleElement, previousStyle.nextSibling)
else
html().prepend(styleElement)
}
}

return styleElement
}

async function updateStyle() {
const result = await uno.generate(tokens)
const styleElement = getStyleElement()
styleElement.innerHTML = result.css

result.layers.reduce((previous: string | undefined, current) => {
getStyleElement(current, previous).innerHTML = result.getLayer(current) ?? ''
return current
}, undefined)

tokens = result.matched
return {
...result,
styleElement,
getStyleElement: (layer: string) => styleElements.get(layer),
getStyleElements: () => styleElements,
}
}

Expand All @@ -169,10 +195,10 @@ export default function init(inlineConfig: RuntimeOptions = {}) {

async function extractAll() {
const body = defaultDocument.body
const html = body && body.outerHTML
if (html) {
await extract(`${html} ${decodeHtml(html)}`)
removeCloak(defaultDocument.documentElement)
const outerHTML = body && body.outerHTML
if (outerHTML) {
await extract(`${outerHTML} ${decodeHtml(outerHTML)}`)
removeCloak(html())
removeCloak(body)
}
}
Expand All @@ -184,8 +210,10 @@ export default function init(inlineConfig: RuntimeOptions = {}) {
if (mutation.target.nodeType !== 1)
return
const target = mutation.target as Element
if (target === styleElement)
return
for (const item of styleElements) {
if (target === item[1])
return
}
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(async (node) => {
if (node.nodeType !== 1)
Expand Down Expand Up @@ -217,7 +245,7 @@ export default function init(inlineConfig: RuntimeOptions = {}) {
function observe() {
if (observing)
return
const target = defaultDocument.documentElement || defaultDocument.body
const target = html() || defaultDocument.body
if (!target)
return
mutationObserver.observe(target, {
Expand Down

0 comments on commit 7f20f85

Please sign in to comment.