Skip to content

Commit

Permalink
feat(vite): attributify support for svelte-scoped mode (#2260)
Browse files Browse the repository at this point in the history
  • Loading branch information
sibbng committed Feb 28, 2023
1 parent e181a17 commit a9a7c2e
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 18 deletions.
64 changes: 46 additions & 18 deletions 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'

Expand Down Expand Up @@ -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(/(<style[^>]*>)/, `$1${styles}`),
}
}
return { code: `${code}\n<style>${styles}</style>` }
}
else {
return
}
}

const originalShortcuts = uno.config.shortcuts
const shortcuts: Record<string, string[]> = {}
const toGenerate = new Set<string>()
Expand Down Expand Up @@ -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())

Expand All @@ -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)
}
}

Expand All @@ -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)
}

Expand All @@ -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 })

Expand Down
58 changes: 58 additions & 0 deletions test/__snapshots__/svelte-scoped.test.ts.snap
@@ -1,5 +1,63 @@
// Vitest Snapshot v1

exports[`svelte-scoped > attributify 1`] = `
"<div
class:uno-otwx4w={true}
class:uno-g3ok09={true}
class:uno-ut4lmc={true}
class:uno-99czie={true}
class:uno-il0ztn={true}
/>
<style>
:global(.uno-g3ok09) {
--un-bg-opacity: 1;
background-color: rgba(248, 113, 113, var(--un-bg-opacity));
}
:global(.uno-il0ztn:hover, .uno-ut4lmc:hover) {
--un-bg-opacity: 1;
background-color: rgba(96, 165, 250, var(--un-bg-opacity));
}
:global(.uno-otwx4w) {
background-attachment: fixed;
}
:global(.uno-99czie:hover) {
--un-text-opacity: 1;
color: rgba(255, 255, 255, var(--un-text-opacity));
}
</style>
"
`;

exports[`svelte-scoped > attributify 2`] = `
"<div
class:_bg-red_7dkb0w={true}
class:_bg-fixed_7dkb0w={true}
class:_hover:bg-blue_7dkb0w={true}
class:_hover-bg-blue_7dkb0w={true}
class:_hover-text-white_7dkb0w={true}
/>
<style>
:global(._bg-red_7dkb0w) {
--un-bg-opacity: 1;
background-color: rgba(248, 113, 113, var(--un-bg-opacity));
}
:global(._hover-bg-blue_7dkb0w:hover, ._hover\\\\:bg-blue_7dkb0w:hover) {
--un-bg-opacity: 1;
background-color: rgba(96, 165, 250, var(--un-bg-opacity));
}
:global(._bg-fixed_7dkb0w) {
background-attachment: fixed;
}
:global(._hover-text-white_7dkb0w:hover) {
--un-text-opacity: 1;
color: rgba(255, 255, 255, var(--un-text-opacity));
}
</style>
"
`;

exports[`svelte-scoped > everything 1`] = `
"<div class=\\"uno-o8l1as\\" />
<div class:logo class=\\"foo bar\\" />
Expand Down
10 changes: 10 additions & 0 deletions test/svelte-scoped.test.ts
Expand Up @@ -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: {
Expand Down Expand Up @@ -335,6 +337,14 @@ describe('svelte-scoped', () => {
`)
})

test('attributify', async () => {
const code = `
<div bg="red fixed hover:blue" hover="bg-blue text-white" />
`.trim()
expect(await transform(code)).toMatchSnapshot()
expect(await transform(code, { combine: false })).toMatchSnapshot()
})

test('everything', async () => {
const code = `
<div class="bg-red-500 sm:text-xl dark:hover:bg-green-500 transform scale-5" />
Expand Down

0 comments on commit a9a7c2e

Please sign in to comment.