From a9a7c2ed43efdac5193ff08a2803c55e4ae4386d Mon Sep 17 00:00:00 2001 From: sibbng Date: Wed, 1 Mar 2023 01:28:16 +0300 Subject: [PATCH] feat(vite): attributify support for svelte-scoped mode (#2260) --- .../vite/src/modes/svelte-scoped/transform.ts | 64 +++++++++++++------ test/__snapshots__/svelte-scoped.test.ts.snap | 58 +++++++++++++++++ test/svelte-scoped.test.ts | 10 +++ 3 files changed, 114 insertions(+), 18 deletions(-) diff --git a/packages/vite/src/modes/svelte-scoped/transform.ts b/packages/vite/src/modes/svelte-scoped/transform.ts index d5a95fc405..9d47824bbf 100644 --- a/packages/vite/src/modes/svelte-scoped/transform.ts +++ b/packages/vite/src/modes/svelte-scoped/transform.ts @@ -1,5 +1,5 @@ import MagicString from 'magic-string' -import { type UnoGenerator, expandVariantGroup } from '@unocss/core' +import { type UnoGenerator, attributifyRE, escapeRegExp, expandVariantGroup } from '@unocss/core' import { wrapSelectorsWithGlobal } from './wrap-global' import { hash } from './hash' @@ -48,20 +48,6 @@ export async function transformSvelteSFC(code: string, id: string, uno: UnoGener const classDirectives = [...code.matchAll(classesDirectivesRE)] const classDirectivesShorthand = [...code.matchAll(classDirectivesShorthandRE)] - if (!classes.length && !classDirectives.length && !classDirectivesShorthand.length) { - if (preflights || safelist) { - if (alreadyHasStyles) { - return { - code: code.replace(/(]*>)/, `$1${styles}`), - } - } - return { code: `${code}\n` } - } - else { - return - } - } - const originalShortcuts = uno.config.shortcuts const shortcuts: Record = {} const toGenerate = new Set() @@ -117,7 +103,7 @@ export async function transformSvelteSFC(code: string, id: string, uno: UnoGener const className = queueCompiledClass(known) return [className, ...replacements].join(' ') } - + const processedMap = new Set() for (const match of classes) { let body = expandVariantGroup(match[2].trim()) @@ -130,8 +116,10 @@ export async function transformSvelteSFC(code: string, id: string, uno: UnoGener const replacement = await sortKnownAndUnknownClasses(body) if (replacement) { - const start = match.index! - s.overwrite(start + 7, start + match[0].length - 1, replacement) + const start = match.index! + 7 + const end = match.index! + match[0].length - 1 + processedMap.add(start) + s.overwrite(start, end, replacement) } } @@ -142,6 +130,7 @@ export async function transformSvelteSFC(code: string, id: string, uno: UnoGener continue const className = queueCompiledClass([token]) const start = match.index! + 'class:'.length + processedMap.add(start) s.overwrite(start, start + match[1].length, className) } @@ -152,9 +141,48 @@ export async function transformSvelteSFC(code: string, id: string, uno: UnoGener continue const className = queueCompiledClass([token]) const start = match.index! + 'class:'.length + processedMap.add(start) s.overwrite(start, start + match[1].length, `${className}={${token}}`) } + const { matched } = await uno.generate(code, { preflights: false, safelist: false, minify: true }) + + for (const token of matched) { + const match = token.match(attributifyRE) + if (match) { + const [,name, value] = match + if (!value) { + let start = 0 + code.split(/([\s"'`;*]|:\(|\)"|\)\s)/g).forEach((i) => { + const end = start + i.length + if (i === name && !processedMap.has(start)) { + const className = queueCompiledClass([name]) + s.appendLeft(start, `class:${className}={true} `) + s.overwrite(start, end, '') + } + start = end + }) + } + else { + const regex = new RegExp(`(${escapeRegExp(name)}=)(['"])[^\\2]*?${escapeRegExp(value)}[^\\2]*?\\2`, 'g') + for (const match of code.matchAll(regex)) { + const escaped = match[1] + const body = match[0].slice(escaped.length) + let bodyIndex = body.match(`[\\b\\s'"]${escapeRegExp(value)}[\\b\\s'"]`)?.index ?? -1 + if (body[bodyIndex]?.match(/[\s'"]/)) + bodyIndex++ + if (bodyIndex < 0) + return + const [,base] = await uno.matchVariants(value) + const variants = value.replace(base, '') + const className = queueCompiledClass([`${variants + name}-${base}`]) + s.appendLeft(match.index!, `class:${className}={true} `) + s.overwrite(match.index!, match.index! + match[0].length, '') + } + } + } + } + uno.config.shortcuts = [...originalShortcuts, ...Object.entries(shortcuts)] const { css } = await uno.generate(toGenerate, { preflights: false, safelist: false, minify: true }) diff --git a/test/__snapshots__/svelte-scoped.test.ts.snap b/test/__snapshots__/svelte-scoped.test.ts.snap index d70c5374ea..b3f5bf9a15 100644 --- a/test/__snapshots__/svelte-scoped.test.ts.snap +++ b/test/__snapshots__/svelte-scoped.test.ts.snap @@ -1,5 +1,63 @@ // Vitest Snapshot v1 +exports[`svelte-scoped > attributify 1`] = ` +"
+ + +" +`; + +exports[`svelte-scoped > attributify 2`] = ` +"
+ + +" +`; + exports[`svelte-scoped > everything 1`] = ` "
diff --git a/test/svelte-scoped.test.ts b/test/svelte-scoped.test.ts index 8214abb1b8..7d6e1f6bfe 100644 --- a/test/svelte-scoped.test.ts +++ b/test/svelte-scoped.test.ts @@ -6,12 +6,14 @@ import { format as prettier } from 'prettier' // @ts-expect-error missing types import prettierSvelte from 'prettier-plugin-svelte' +import presetAttributify from '@unocss/preset-attributify' import { transformSvelteSFC } from '../packages/vite/src/modes/svelte-scoped' describe('svelte-scoped', () => { const uno = createGenerator({ presets: [ presetUno(), + presetAttributify(), presetIcons({ prefix: 'i-', extraProperties: { @@ -335,6 +337,14 @@ describe('svelte-scoped', () => { `) }) + test('attributify', async () => { + const code = ` +
+ `.trim() + expect(await transform(code)).toMatchSnapshot() + expect(await transform(code, { combine: false })).toMatchSnapshot() + }) + test('everything', async () => { const code = `