diff --git a/package.json b/package.json index a52b261a01..4f55d8dd2c 100644 --- a/package.json +++ b/package.json @@ -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:*", @@ -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", diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index df502a3506..f60bf25278 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -47,7 +47,7 @@ export function resolveConfig( ...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(key: T): Required>[T] { return uniq([ diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index ab54f243a6..20299d7718 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -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 +} + export interface RuntimeOptions { /** * Default config of UnoCSS @@ -72,9 +77,9 @@ export interface RuntimeContext { /** * Manually run the update cycle. * - * @returns {GenerateResult & { styleElement: HTMLStyleElement}} + * @returns {RuntimeGenerateResult} */ - update: () => Promise + update: () => Promise /** * The UnoCSS version. @@ -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) @@ -111,10 +117,10 @@ export default function init(inlineConfig: RuntimeOptions = {}) { runtimeOptions.configResolved?.(userConfig, userConfigDefaults) const uno = createGenerator(userConfig, userConfigDefaults) + const styleElements = new Map() let paused = true let tokens = new Set() - let styleElement: HTMLStyleElement | undefined let inspector: RuntimeInspectorCallback | undefined let _timer: number | undefined @@ -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, } } @@ -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) } } @@ -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) @@ -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, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc3eaa16d9..9253333ccc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,6 +52,7 @@ importers: '@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:* @@ -70,6 +71,7 @@ importers: 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 @@ -141,6 +143,7 @@ importers: '@unocss/preset-web-fonts': link:packages/preset-web-fonts '@unocss/preset-wind': link:packages/preset-wind '@unocss/reset': link:packages/reset + '@unocss/runtime': link:packages/runtime '@unocss/transformer-directives': link:packages/transformer-directives '@unocss/transformer-variant-group': link:packages/transformer-variant-group '@unocss/vite': link:packages/vite @@ -159,6 +162,7 @@ importers: 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 @@ -174,7 +178,7 @@ importers: simple-git-hooks: 2.8.1 splitpanes: 3.1.5 terser: 5.16.3 - tsup: 6.6.3_typescript@4.9.5 + tsup: 6.6.3_uujdqti2krmttzhqvubwnsmcci typescript: 4.9.5 unbuild: 0.8.11 unocss: link:packages/unocss @@ -183,7 +187,7 @@ importers: vite: 4.1.1_d7kjmwg5lyp5bmymljws654d5q vite-plugin-inspect: 0.7.15_rollup@3.15.0+vite@4.1.1 vite-plugin-pages: 0.28.0_vite@4.1.1 - vitest: 0.28.5_alhnkaxul7chizha5zipzdiwku + vitest: 0.28.5_dbdalx4lizqcgea4l357du3m34 vue: 3.2.45 vue-router: 4.1.6_vue@3.2.45 @@ -274,20 +278,20 @@ importers: '@unocss/autocomplete': link:../packages/autocomplete '@unocss/nuxt': link:../packages/nuxt '@unocss/shared-docs': link:../packages/shared-docs - '@vueuse/nuxt': 9.12.0_f3ihav77uh5kt43saqmj5dlhti + '@vueuse/nuxt': 9.12.0_nuxt@3.2.0+vue@3.2.47 esno: 0.16.3 fs-extra: 11.1.0 fuse.js: 6.6.2 js-yaml: 4.1.0 knitwork: 1.0.0 markdown-it-link-attributes: 4.0.1 - nuxt: 3.2.0_rollup@3.15.0 + nuxt: 3.2.0 p-limit: 4.0.0 postcss-nested: 6.0.0_postcss@8.4.21 prettier: 2.8.4 shiki: 0.14.1 theme-vitesse: 0.6.2 - vite-plugin-vue-markdown: 0.22.4_rollup@3.15.0+vite@4.1.1 + vite-plugin-vue-markdown: 0.22.4_vite@4.1.1 packages/astro: specifiers: @@ -3160,7 +3164,6 @@ packages: transitivePeerDependencies: - rollup - supports-color - dev: false /@nuxt/kit/3.2.0_rollup@3.15.0: resolution: {integrity: sha512-Otb1S/08tDxbpeQYLMynjr2TX7ssU1ynbWDpVzFzLBdfHkGWHXpIhJr+0u3LdnPUBw6C/xPXe7fd7RuXI9avoA==} @@ -3232,6 +3235,35 @@ packages: - supports-color dev: true + /@nuxt/telemetry/2.1.9: + resolution: {integrity: sha512-mUyDqmB8GUJwTHVnwxuapeUHDSsUycOt+ZsA7GB6F8MOBJiVhQl/EeEAWoO2TUs0BPp2SlY9uO6eQihvxyLRqQ==} + hasBin: true + dependencies: + '@nuxt/kit': 3.2.0 + chalk: 5.2.0 + ci-info: 3.8.0 + consola: 2.15.3 + create-require: 1.1.1 + defu: 6.1.2 + destr: 1.2.2 + dotenv: 16.0.3 + fs-extra: 10.1.0 + git-url-parse: 13.1.0 + inquirer: 9.1.4 + is-docker: 3.0.0 + jiti: 1.17.0 + mri: 1.2.0 + nanoid: 4.0.0 + node-fetch: 3.3.0 + ofetch: 1.0.0 + parse-git-config: 3.0.0 + rc9: 2.0.0 + std-env: 3.3.2 + transitivePeerDependencies: + - rollup + - supports-color + dev: true + /@nuxt/telemetry/2.1.9_rollup@3.15.0: resolution: {integrity: sha512-mUyDqmB8GUJwTHVnwxuapeUHDSsUycOt+ZsA7GB6F8MOBJiVhQl/EeEAWoO2TUs0BPp2SlY9uO6eQihvxyLRqQ==} hasBin: true @@ -3676,6 +3708,11 @@ packages: resolution: {integrity: sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==} dev: true + /@tootallnate/once/2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true + /@trysound/sax/0.2.0: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -5283,16 +5320,16 @@ packages: resolution: {integrity: sha512-9oJ9MM9lFLlmvxXUqsR1wLt1uF7EVbP5iYaHJYqk+G2PbMjY6EXvZeTjbdO89HgoF5cI6z49o2zT/jD9SVoNpQ==} dev: true - /@vueuse/nuxt/9.12.0_f3ihav77uh5kt43saqmj5dlhti: + /@vueuse/nuxt/9.12.0_nuxt@3.2.0+vue@3.2.47: resolution: {integrity: sha512-zT7ieMmJgyB+hpQ6aG2HiyvNFHm5D/s2Z7fQjWcxuDI9Xawr7ECWBkSinxwrmtZ7+0lacXak+VMFpbx/z/zp2Q==} peerDependencies: nuxt: ^3.0.0 dependencies: - '@nuxt/kit': 3.2.0_rollup@3.15.0 + '@nuxt/kit': 3.2.0 '@vueuse/core': 9.12.0_vue@3.2.47 '@vueuse/metadata': 9.12.0 local-pkg: 0.4.3 - nuxt: 3.2.0_rollup@3.15.0 + nuxt: 3.2.0 vue-demi: 0.13.11_vue@3.2.47 transitivePeerDependencies: - '@vue/composition-api' @@ -5595,6 +5632,10 @@ packages: dev: true optional: true + /abab/2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + dev: true + /abbrev/1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: true @@ -5607,6 +5648,13 @@ packages: negotiator: 0.6.3 dev: true + /acorn-globals/7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + dependencies: + acorn: 8.8.2 + acorn-walk: 8.2.0 + dev: true + /acorn-import-assertions/1.8.0_acorn@8.8.2: resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==} peerDependencies: @@ -8105,6 +8153,21 @@ packages: css-tree: 1.1.3 dev: true + /cssom/0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + dev: true + + /cssom/0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + dev: true + + /cssstyle/2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + dependencies: + cssom: 0.3.8 + dev: true + /csstype/2.6.21: resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} dev: true @@ -8132,6 +8195,15 @@ packages: engines: {node: '>= 12'} dev: true + /data-urls/3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + dev: true + /de-indent/1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} dev: true @@ -8211,6 +8283,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decimal.js/10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: true + /decode-named-character-reference/1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} dependencies: @@ -8550,6 +8626,13 @@ packages: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} dev: true + /domexception/4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + dependencies: + webidl-conversions: 7.0.0 + dev: true + /domhandler/4.3.1: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} engines: {node: '>= 4'} @@ -9345,6 +9428,19 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} + /escodegen/2.0.0: + resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + /eslint-import-resolver-node/0.3.7: resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} dependencies: @@ -10330,6 +10426,15 @@ packages: mime-types: 2.1.35 dev: true + /form-data/4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + /formdata-polyfill/4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -11051,6 +11156,13 @@ packages: resolution: {integrity: sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==} dev: true + /html-encoding-sniffer/3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + dependencies: + whatwg-encoding: 2.0.0 + dev: true + /html-entities/1.4.0: resolution: {integrity: sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==} dev: true @@ -11183,6 +11295,17 @@ packages: resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} dev: true + /http-proxy-agent/5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /http-proxy-middleware/0.19.1_tmpgdztspuwvsxzgjkhoqk7duq: resolution: {integrity: sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==} engines: {node: '>=4.0.0'} @@ -11303,6 +11426,13 @@ packages: safer-buffer: 2.1.2 dev: true + /iconv-lite/0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + /icss-utils/4.1.1: resolution: {integrity: sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==} engines: {node: '>= 6'} @@ -11874,6 +12004,10 @@ packages: isobject: 3.0.1 dev: true + /is-potential-custom-element-name/1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true + /is-primitive/3.0.1: resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==} engines: {node: '>=0.10.0'} @@ -12103,6 +12237,47 @@ packages: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} dev: true + /jsdom/21.1.0: + resolution: {integrity: sha512-m0lzlP7qOtthD918nenK3hdItSd2I+V3W9IrBcB36sqDwG+KnUs66IF5GY7laGWUnlM9vTsD0W1QwSEBYWWcJg==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.6 + acorn: 8.8.2 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.4.3 + domexception: 4.0.0 + escodegen: 2.0.0 + form-data: 4.0.0 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.2 + parse5: 7.1.2 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.2 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.11.0 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /jsesc/0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -12284,6 +12459,14 @@ packages: readable-stream: 2.3.7 dev: true + /levn/0.3.0: + resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + type-check: 0.3.2 + dev: true + /levn/0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -13856,17 +14039,17 @@ packages: fast-glob: 3.2.12 dev: true - /nuxt/3.2.0_npiaaxxgryejiamr3x3qn7ulzq: + /nuxt/3.2.0: resolution: {integrity: sha512-8jAYyjU1Ht+MXPLLDIdIUmV56KiI0g7KusKwzvqn+vlzyCNtSHg2W/VBCGw5QWplb/MXruogcMl2sDenlQRZFg==} engines: {node: ^14.16.0 || ^16.10.0 || ^17.0.0 || ^18.0.0 || ^19.0.0} hasBin: true dependencies: '@nuxt/devalue': 2.0.0 - '@nuxt/kit': 3.2.0_rollup@3.15.0 - '@nuxt/schema': 3.2.0_rollup@3.15.0 - '@nuxt/telemetry': 2.1.9_rollup@3.15.0 + '@nuxt/kit': 3.2.0 + '@nuxt/schema': 3.2.0 + '@nuxt/telemetry': 2.1.9 '@nuxt/ui-templates': 1.1.1 - '@nuxt/vite-builder': 3.2.0_2mg2fqmwmab4ct4swh5ykvaaze + '@nuxt/vite-builder': 3.2.0_vue@3.2.47 '@unhead/ssr': 1.0.22 '@vue/reactivity': 3.2.47 '@vue/shared': 3.2.47 @@ -13898,7 +14081,7 @@ packages: unctx: 2.1.2 unenv: 1.1.1 unhead: 1.0.22 - unimport: 2.2.4_rollup@3.15.0 + unimport: 2.2.4 unplugin: 1.1.0 untyped: 1.2.2 vue: 3.2.47 @@ -13928,7 +14111,7 @@ packages: - vue-tsc dev: true - /nuxt/3.2.0_rollup@3.15.0: + /nuxt/3.2.0_npiaaxxgryejiamr3x3qn7ulzq: resolution: {integrity: sha512-8jAYyjU1Ht+MXPLLDIdIUmV56KiI0g7KusKwzvqn+vlzyCNtSHg2W/VBCGw5QWplb/MXruogcMl2sDenlQRZFg==} engines: {node: ^14.16.0 || ^16.10.0 || ^17.0.0 || ^18.0.0 || ^19.0.0} hasBin: true @@ -13938,7 +14121,7 @@ packages: '@nuxt/schema': 3.2.0_rollup@3.15.0 '@nuxt/telemetry': 2.1.9_rollup@3.15.0 '@nuxt/ui-templates': 1.1.1 - '@nuxt/vite-builder': 3.2.0_vue@3.2.47 + '@nuxt/vite-builder': 3.2.0_2mg2fqmwmab4ct4swh5ykvaaze '@unhead/ssr': 1.0.22 '@vue/reactivity': 3.2.47 '@vue/shared': 3.2.47 @@ -14000,6 +14183,10 @@ packages: - vue-tsc dev: true + /nwsapi/2.2.2: + resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==} + dev: true + /oauth-sign/0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} dev: true @@ -14162,6 +14349,18 @@ packages: is-wsl: 1.1.0 dev: true + /optionator/0.8.3: + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.3 + dev: true + /optionator/0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} @@ -14427,6 +14626,12 @@ packages: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} dev: true + /parse5/7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.4.0 + dev: true + /parseurl/1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -15442,6 +15647,11 @@ packages: which-pm: 2.0.0 dev: true + /prelude-ls/1.1.2: + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} + engines: {node: '>= 0.8.0'} + dev: true + /prelude-ls/1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -16427,6 +16637,13 @@ packages: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} dev: true + /saxes/6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: true + /schema-utils/1.0.0: resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==} engines: {node: '>= 4'} @@ -17439,6 +17656,10 @@ packages: stable: 0.1.8 dev: true + /symbol-tree/3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true + /synckit/0.8.5: resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} engines: {node: ^14.18.0 || >=16.0.0} @@ -17748,6 +17969,16 @@ packages: punycode: 2.1.1 dev: true + /tough-cookie/4.1.2: + resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.1.1 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: true + /tr46/0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true @@ -17758,6 +17989,13 @@ packages: punycode: 2.1.1 dev: true + /tr46/3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.1.1 + dev: true + /tree-kill/1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -17850,7 +18088,7 @@ packages: - ts-node dev: true - /tsup/6.6.3_typescript@4.9.5: + /tsup/6.6.3_uujdqti2krmttzhqvubwnsmcci: resolution: {integrity: sha512-OLx/jFllYlVeZQ7sCHBuRVEQBBa1tFbouoc/gbYakyipjVQdWy/iQOvmExUA/ewap9iQ7tbJf9pW0PgcEFfJcQ==} engines: {node: '>=14.18'} hasBin: true @@ -17874,7 +18112,8 @@ packages: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 3.1.4 + postcss: 8.4.21 + postcss-load-config: 3.1.4_postcss@8.4.21 resolve-from: 5.0.0 rollup: 3.15.0 source-map: 0.8.0-beta.0 @@ -17920,6 +18159,13 @@ packages: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} dev: true + /type-check/0.3.2: + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + dev: true + /type-check/0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -18253,6 +18499,11 @@ packages: engines: {node: '>= 4.0.0'} dev: true + /universalify/0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} @@ -18753,7 +19004,7 @@ packages: - supports-color dev: true - /vite-plugin-vue-markdown/0.22.4_rollup@3.15.0+vite@4.1.1: + /vite-plugin-vue-markdown/0.22.4_vite@4.1.1: resolution: {integrity: sha512-+kDzqGI5Lq0Wa6M4EJtV7cqOIHgGp6g4jcazXljQKLl9Og4fRs6FuGAPa2HiJ44Z9es85LAPiPbTjwrZEGEUKA==} peerDependencies: vite: ^2.0.0 || ^3.0.0-0 || ^4.0.0 @@ -18762,7 +19013,7 @@ packages: '@mdit-vue/plugin-component': 0.11.2 '@mdit-vue/plugin-frontmatter': 0.11.1 '@mdit-vue/types': 0.11.0 - '@rollup/pluginutils': 5.0.2_rollup@3.15.0 + '@rollup/pluginutils': 5.0.2 '@types/markdown-it': 12.2.3 markdown-it: 13.0.1 vite: 4.1.1 @@ -18863,7 +19114,7 @@ packages: vite: 4.1.1 dev: true - /vitest/0.28.5_alhnkaxul7chizha5zipzdiwku: + /vitest/0.28.5_dbdalx4lizqcgea4l357du3m34: resolution: {integrity: sha512-pyCQ+wcAOX7mKMcBNkzDwEHRGqQvHUl0XnoHR+3Pb1hytAHISgSxv9h0gUiSiYtISXUU3rMrKiKzFYDrI6ZIHA==} engines: {node: '>=v14.16.0'} hasBin: true @@ -18898,6 +19149,7 @@ packages: cac: 6.7.14 chai: 4.3.7 debug: 4.3.4 + jsdom: 21.1.0 local-pkg: 0.4.3 pathe: 1.1.0 picocolors: 1.0.0 @@ -19349,6 +19601,13 @@ packages: resolution: {integrity: sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==} dev: true + /w3c-xmlserializer/4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + dependencies: + xml-name-validator: 4.0.0 + dev: true + /watchpack-chokidar2/2.0.1: resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==} requiresBuild: true @@ -19416,6 +19675,11 @@ packages: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} dev: true + /webidl-conversions/7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + /webpack-bundle-analyzer/3.9.0: resolution: {integrity: sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==} engines: {node: '>= 6.14.4'} @@ -19727,10 +19991,30 @@ packages: engines: {node: '>=0.8.0'} dev: true + /whatwg-encoding/2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: true + /whatwg-fetch/3.6.2: resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==} dev: true + /whatwg-mimetype/3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: true + + /whatwg-url/11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: true + /whatwg-url/5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: @@ -19965,6 +20249,10 @@ packages: engines: {node: '>=12'} dev: true + /xmlchars/2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: true + /xtend/4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} diff --git a/test/runtime-dom.test.ts b/test/runtime-dom.test.ts new file mode 100644 index 0000000000..6da8d6fb5a --- /dev/null +++ b/test/runtime-dom.test.ts @@ -0,0 +1,73 @@ +// @vitest-environment jsdom + +import { afterEach, describe, expect, test } from 'vitest' +import initUnocssRuntime from '@unocss/runtime' +import presetUno from '@unocss/preset-uno' + +describe('runtime dom manipulation', () => { + afterEach(() => { + window.document.documentElement.innerHTML = '' + }) + + function initRuntime(options?: Object) { + initUnocssRuntime({ + defaults: { + presets: [ + presetUno(), + ], + ...options, + }, + ready() { + return false + }, + }) + + return window.__unocss_runtime + } + + test('runtime generates multiple styles', async () => { + const runtime = initRuntime() + + await runtime?.extract('container mt0') + const result = await runtime?.update() + const layers = [...(result?.getStyleElements().keys() ?? [])] + + layers.forEach((layer) => { + const expected = `/* layer: ${layer} */` + expect(result?.css).toContain(expected) + + const style = result?.getStyleElement(layer) + expect(style?.tagName).equals('STYLE') + expect(style?.innerHTML).toContain(expected) + }) + }) + + test('runtime can retrieve styles ordered by layer', async () => { + const runtime = initRuntime({ + layers: { + pre: -10, + default: 10, + ammend: 20, + preflights: 100, + }, + }) + + await runtime?.extract('uno-layer-pre:pt-0 uno-layer-[ammend]:pb-0 p-0') + const result = await runtime?.update() + + const layers = [...(result?.getStyleElements().keys() ?? [])] + expect(layers).toMatchObject(['pre', 'default', 'ammend', 'preflights']) + }) + + test('runtime styles is placed in order', async () => { + const runtime = initRuntime() + + await runtime?.extract('ring-red') + const result = await runtime?.update() + + const doc = window.document + expect(doc.documentElement.firstElementChild).toEqual(result?.getStyleElement('preflights')) + expect(doc.documentElement.firstElementChild?.nextElementSibling).toEqual(result?.getStyleElement('default')) + expect(doc.documentElement.firstElementChild?.nextElementSibling?.nextElementSibling).toEqual(doc.head) + }) +})