From 1075e79165a843bb38fc7aa24f82d892163fb575 Mon Sep 17 00:00:00 2001 From: MellowCo <799478052@qq.com> Date: Sat, 7 Dec 2024 08:48:37 +0800 Subject: [PATCH] feat: generate preflights on demand --- src/index.ts | 4 +- src/preflights.ts | 54 ++++++++++++++-------- src/rules/ring.ts | 4 +- src/rules/shadow.ts | 4 +- src/rules/transform.ts | 32 ++++++++----- test/assets/output/preset-mini/targets.css | 2 +- test/preflights.test.ts | 47 +++++++++++++++++++ 7 files changed, 111 insertions(+), 36 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2168f74..5f4df3d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -63,7 +63,7 @@ export interface PresetWeappOptions extends PresetOptions { * * @default true */ - preflight?: boolean + preflight?: boolean | 'on-demand' /** * 是否转换微信class @@ -198,7 +198,7 @@ export const presetWeapp = definePreset((options: PresetWeappOptions = {}) => { if (options.platform === 'uniapp' && options.isH5) uniAppVue2CssRpxTransform(css) }, - preflights: options.preflight ? preflights(options.isH5, options.platform) : [], + preflights: preflights(options), prefix: options.prefix, extractorDefault: options.arbitraryVariants === false ? undefined diff --git a/src/preflights.ts b/src/preflights.ts index fd50ea7..cb166e7 100644 --- a/src/preflights.ts +++ b/src/preflights.ts @@ -1,4 +1,5 @@ import type { Preflight, PreflightContext } from '@unocss/core' +import type { PresetWeappOptions } from '.' import type { Theme } from './theme' import { entriesToCss, toArray } from '@unocss/core' @@ -7,26 +8,41 @@ const taroPrefix = ['*,::before,::after'] const uniappPrefix = ['uni-page-body,::before,::after'] // const defaultPrefix = ['*,::before,::after', '::backdrop'] -export default function (isH5: boolean, platform: string): Preflight[] { - return [ - { - layer: 'preflights', - getCSS(ctx: PreflightContext) { - if (ctx.theme.preflightBase) { - const css = entriesToCss(Object.entries(ctx.theme.preflightBase)) - let preflightRoot = ctx.theme.preflightRoot +export default function (options: PresetWeappOptions): Preflight[] | undefined { + if (options.preflight) { + return [ + { + layer: 'preflights', + getCSS({ theme, generator }) { + if (theme.preflightBase) { + // let entries = entriesToCss(Object.entries(theme.preflightBase)) + let entries = Object.entries(theme.preflightBase) + if (options.preflight === 'on-demand') { + const keys = new Set(Array.from(generator.activatedRules).map(r => r[2]?.custom?.preflightKeys).filter(Boolean).flat()) + entries = entries.filter(([k]) => keys.has(k)) + } - if (!preflightRoot) { - if (isH5) - preflightRoot = platform === 'uniapp' ? uniappPrefix : taroPrefix - else - preflightRoot = wxPrefix - } + if (entries.length > 0) { + let css = entriesToCss(entries) + if (options.variablePrefix !== 'un-') { + css = css.replace(/--un-/g, `--${options.variablePrefix}`) + } + + let preflightRoot = theme.preflightRoot - const roots = toArray(preflightRoot) - return roots.map(root => `${root}{${css}}`).join('') - } + if (!preflightRoot) { + if (options.isH5) + preflightRoot = options.platform === 'uniapp' ? uniappPrefix : taroPrefix + else + preflightRoot = wxPrefix + } + + const roots = toArray(preflightRoot) + return roots.map(root => `${root}{${css}}`).join('') + } + } + }, }, - }, - ] + ] + } } diff --git a/src/rules/ring.ts b/src/rules/ring.ts index 4ea4f05..7ddd144 100644 --- a/src/rules/ring.ts +++ b/src/rules/ring.ts @@ -12,6 +12,8 @@ export const ringBase = { '--un-shadow': '0 0 rgb(0 0 0 / 0)', } +const preflightKeys = Object.keys(ringBase) + export const rings: Rule[] = [ // size [/^ring(?:-(.+))?$/, ([, d], { theme }) => { @@ -24,7 +26,7 @@ export const rings: Rule[] = [ 'box-shadow': 'var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow)', } } - }, { autocomplete: 'ring-$ringWidth' }], + }, { custom: { preflightKeys }, autocomplete: 'ring-$ringWidth' }], // size [/^ring-(?:width-|size-)(.+)$/, handleWidth, { autocomplete: 'ring-(width|size)-$lineWidth' }], diff --git a/src/rules/shadow.ts b/src/rules/shadow.ts index dc9e5bb..5c29ec4 100644 --- a/src/rules/shadow.ts +++ b/src/rules/shadow.ts @@ -11,6 +11,8 @@ export const boxShadowsBase = { '--un-shadow': '0 0 rgb(0 0 0 / 0)', } +const preflightKeys = Object.keys(boxShadowsBase) + export const boxShadows: Rule[] = [ [/^shadow(?:-(.+))?$/, (match, context) => { let [, d] = match @@ -27,7 +29,7 @@ export const boxShadows: Rule[] = [ } } return colorResolver('--un-shadow-color', 'shadow', 'shadowColor')(match, context) - }, { autocomplete: ['shadow-$colors', 'shadow-$boxShadow'] }], + }, { custom: { preflightKeys }, autocomplete: ['shadow-$colors', 'shadow-$boxShadow'] }], [/^shadow-op(?:acity)?-?(.+)$/, ([, opacity]) => ({ '--un-shadow-opacity': h.bracket.percent.cssvar(opacity) }), { autocomplete: 'shadow-(op|opacity)-' }], // inset diff --git a/src/rules/transform.ts b/src/rules/transform.ts index d4ec589..bd809cb 100644 --- a/src/rules/transform.ts +++ b/src/rules/transform.ts @@ -68,9 +68,15 @@ export const transformBase = { '--un-translate-z': 0, } +const preflightKeys = Object.keys(transformBase) + export const transforms: Rule[] = [ // origins - [/^(?:transform-)?origin-(.+)$/, ([, s]) => ({ 'transform-origin': positionMap[s] ?? h.bracket.cssvar(s) }), { autocomplete: [`transform-origin-(${Object.keys(positionMap).join('|')})`, `origin-(${Object.keys(positionMap).join('|')})`] }], + [ + /^(?:transform-)?origin-(.+)$/, + ([, s]) => ({ 'transform-origin': positionMap[s] ?? h.bracket.cssvar(s) }), + { autocomplete: [`transform-origin-(${Object.keys(positionMap).join('|')})`, `origin-(${Object.keys(positionMap).join('|')})`] }, + ], // perspectives [/^(?:transform-)?perspect(?:ive)?-(.+)$/, ([, s]) => { @@ -95,23 +101,25 @@ export const transforms: Rule[] = [ }], // modifiers - [/^(?:transform-)?translate-()(.+)$/, handleTranslate], - [/^(?:transform-)?translate-([xyz])-(.+)$/, handleTranslate], - [/^(?:transform-)?rotate-()(.+)$/, handleRotate], - [/^(?:transform-)?rotate-([xyz])-(.+)$/, handleRotate], - [/^(?:transform-)?skew-()(.+)$/, handleSkew], - [/^(?:transform-)?skew-([xy])-(.+)$/, handleSkew, { autocomplete: ['transform-skew-(x|y)-', 'skew-(x|y)-'] }], - [/^(?:transform-)?scale-()(.+)$/, handleScale], - [/^(?:transform-)?scale-([xyz])-(.+)$/, handleScale, { autocomplete: [`transform-(${transformValues.join('|')})-`, `transform-(${transformValues.join('|')})-(x|y|z)-`, `(${transformValues.join('|')})-`, `(${transformValues.join('|')})-(x|y|z)-`] }], + [/^(?:transform-)?translate-()(.+)$/, handleTranslate, { custom: { preflightKeys } }], + [/^(?:transform-)?translate-([xyz])-(.+)$/, handleTranslate, { custom: { preflightKeys } }], + [/^(?:transform-)?rotate-()(.+)$/, handleRotate, { custom: { preflightKeys } }], + [/^(?:transform-)?rotate-([xyz])-(.+)$/, handleRotate, { custom: { preflightKeys } }], + [/^(?:transform-)?skew-()(.+)$/, handleSkew, { custom: { preflightKeys } }], + [/^(?:transform-)?skew-([xy])-(.+)$/, handleSkew, { custom: { preflightKeys }, autocomplete: ['transform-skew-(x|y)-', 'skew-(x|y)-'] }], + [/^(?:transform-)?scale-()(.+)$/, handleScale, { custom: { preflightKeys } }], + [/^(?:transform-)?scale-([xyz])-(.+)$/, handleScale, { custom: { preflightKeys }, autocomplete: [`transform-(${transformValues.join('|')})-`, `transform-(${transformValues.join('|')})-(x|y|z)-`, `(${transformValues.join('|')})-`, `(${transformValues.join('|')})-(x|y|z)-`] }], // style [/^(?:transform-)?preserve-3d$/, () => ({ 'transform-style': 'preserve-3d' })], [/^(?:transform-)?preserve-flat$/, () => ({ 'transform-style': 'flat' })], // base - ['transform', { transform }], - ['transform-cpu', { transform: transformCpu }], - ['transform-gpu', { transform: transformGpu }], + ['transform', { transform }, { custom: { preflightKeys } }], + ['transform-cpu', { transform: transformCpu }, { + custom: { preflightKeys: ['--un-translate-x', '--un-translate-y', '--un-rotate', '--un-rotate-z', '--un-skew-x', '--un-skew-y', '--un-scale-x', '--un-scale-y'] }, + }], + ['transform-gpu', { transform: transformGpu }, { custom: { preflightKeys } }], ['transform-none', { transform: 'none' }], ...makeGlobalStaticRules('transform'), ] diff --git a/test/assets/output/preset-mini/targets.css b/test/assets/output/preset-mini/targets.css index 657adc2..3a9326e 100644 --- a/test/assets/output/preset-mini/targets.css +++ b/test/assets/output/preset-mini/targets.css @@ -1,5 +1,5 @@ /* layer: preflights */ -page,root-portal-content,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset:var(--un-empty,/*!*/ /*!*/);--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset:var(--un-empty,/*!*/ /*!*/);--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur:var(--un-empty,/*!*/ /*!*/);--un-brightness:var(--un-empty,/*!*/ /*!*/);--un-contrast:var(--un-empty,/*!*/ /*!*/);--un-drop-shadow:var(--un-empty,/*!*/ /*!*/);--un-grayscale:var(--un-empty,/*!*/ /*!*/);--un-hue-rotate:var(--un-empty,/*!*/ /*!*/);--un-invert:var(--un-empty,/*!*/ /*!*/);--un-saturate:var(--un-empty,/*!*/ /*!*/);--un-sepia:var(--un-empty,/*!*/ /*!*/);--un-backdrop-blur:var(--un-empty,/*!*/ /*!*/);--un-backdrop-brightness:var(--un-empty,/*!*/ /*!*/);--un-backdrop-contrast:var(--un-empty,/*!*/ /*!*/);--un-backdrop-grayscale:var(--un-empty,/*!*/ /*!*/);--un-backdrop-hue-rotate:var(--un-empty,/*!*/ /*!*/);--un-backdrop-invert:var(--un-empty,/*!*/ /*!*/);--un-backdrop-opacity:var(--un-empty,/*!*/ /*!*/);--un-backdrop-saturate:var(--un-empty,/*!*/ /*!*/);--un-backdrop-sepia:var(--un-empty,/*!*/ /*!*/);} +page,root-portal-content,::before,::after{--licl-rotate:0;--licl-rotate-x:0;--licl-rotate-y:0;--licl-rotate-z:0;--licl-scale-x:1;--licl-scale-y:1;--licl-scale-z:1;--licl-skew-x:0;--licl-skew-y:0;--licl-translate-x:0;--licl-translate-y:0;--licl-translate-z:0;--licl-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--licl-ring-shadow:0 0 rgb(0 0 0 / 0);--licl-shadow-inset:var(--licl-empty,/*!*/ /*!*/);--licl-shadow:0 0 rgb(0 0 0 / 0);--licl-ring-inset:var(--licl-empty,/*!*/ /*!*/);--licl-ring-offset-width:0px;--licl-ring-offset-color:#fff;--licl-ring-width:0px;--licl-ring-color:rgb(147 197 253 / 0.5);--licl-blur:var(--licl-empty,/*!*/ /*!*/);--licl-brightness:var(--licl-empty,/*!*/ /*!*/);--licl-contrast:var(--licl-empty,/*!*/ /*!*/);--licl-drop-shadow:var(--licl-empty,/*!*/ /*!*/);--licl-grayscale:var(--licl-empty,/*!*/ /*!*/);--licl-hue-rotate:var(--licl-empty,/*!*/ /*!*/);--licl-invert:var(--licl-empty,/*!*/ /*!*/);--licl-saturate:var(--licl-empty,/*!*/ /*!*/);--licl-sepia:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-blur:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-brightness:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-contrast:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-grayscale:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-hue-rotate:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-invert:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-opacity:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-saturate:var(--licl-empty,/*!*/ /*!*/);--licl-backdrop-sepia:var(--licl-empty,/*!*/ /*!*/);} /* layer: 1 */ .uno-layer-1\:translate-0{--licl-translate-x:0;--licl-translate-y:0;transform:translateX(var(--licl-translate-x)) translateY(var(--licl-translate-y)) translateZ(var(--licl-translate-z)) rotate(var(--licl-rotate)) rotateX(var(--licl-rotate-x)) rotateY(var(--licl-rotate-y)) rotateZ(var(--licl-rotate-z)) skewX(var(--licl-skew-x)) skewY(var(--licl-skew-y)) scaleX(var(--licl-scale-x)) scaleY(var(--licl-scale-y)) scaleZ(var(--licl-scale-z));} /* layer: default */ diff --git a/test/preflights.test.ts b/test/preflights.test.ts index 38a13df..90d6ed0 100644 --- a/test/preflights.test.ts +++ b/test/preflights.test.ts @@ -2,6 +2,53 @@ import { createGenerator } from '@unocss/core' import { describe, expect, it } from 'vitest' import presetWeapp from '../src' +describe('on demand generate preflights', () => { + it('default preflights', async () => { + const uno = await createGenerator({ + presets: [ + presetWeapp({ preflight: 'on-demand' }), + ], + }) + const { css: noPreflightCSS } = await uno.generate('text-red') + expect(noPreflightCSS).toMatchInlineSnapshot(` + "/* layer: default */ + .text-red{--un-text-opacity:1;color:rgb(248 113 113 / var(--un-text-opacity));}" + `) + + const { css: hasPreflightCSS } = await uno.generate('ring') + expect(hasPreflightCSS).toMatchInlineSnapshot(` + "/* layer: preflights */ + page,root-portal-content,::before,::after{--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset:var(--un-empty,/*!*/ /*!*/);--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);} + /* layer: default */ + .ring{--un-ring-width:3px;--un-ring-offset-shadow:var(--un-ring-inset) 0 0 0 var(--un-ring-offset-width) var(--un-ring-offset-color);--un-ring-shadow:var(--un-ring-inset) 0 0 0 calc(var(--un-ring-width) + var(--un-ring-offset-width)) var(--un-ring-color);box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}" + `) + }) + + it('custom depends', async () => { + const uno = await createGenerator({ + presets: [ + presetWeapp({ preflight: 'on-demand' }), + ], + rules: [ + [ + 'custom-rule', + { blur: 'var(--un-shadow)' }, + // depend on `--shadow` from presetMini + { custom: { preflightKeys: '--un-shadow' } }, + ], + ], + }) + const { css } = await uno.generate('custom-rule') + + expect(css).toMatchInlineSnapshot(` + "/* layer: preflights */ + page,root-portal-content,::before,::after{--un-shadow:0 0 rgb(0 0 0 / 0);} + /* layer: default */ + .custom-rule{blur:var(--un-shadow);}" + `) + }) +}) + describe('preflights', () => { it('original preflight', async () => { const uno = await createGenerator({