Skip to content

Commit

Permalink
feat(svelte-scoped): Inject transformers in *CSS files (unocss#2690)
Browse files Browse the repository at this point in the history
Co-authored-by: Jacob Bowdoin <[email protected]>
Co-authored-by: jacob-8 <[email protected]>
  • Loading branch information
3 people authored Jun 22, 2023
1 parent 5b48356 commit a1a4504
Show file tree
Hide file tree
Showing 21 changed files with 278 additions and 14 deletions.
30 changes: 24 additions & 6 deletions docs/integrations/svelte-scoped.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,9 @@ export default defineConfig({
})
```

See [Config File](/guide/config-file) and [Config reference](/config/) for more details.
Extractors are not supported due to the differences in normal UnoCSS global usage and Svelte Scoped usage. Presets and Transformers are supported as described in the following sections. See [Config File](/guide/config-file) and [Config reference](/config/) for all other details.

::: info
Transformers and extractors are not supported due to the differences in normal UnoCSS global usage and Svelte Scoped usage.
:::

## Presets support
### Presets support

Do to the nature of having a few necessary styles in a global stylesheet and everything else contained in each component where needed, presets need to be handled on a case-by-case basis:

Expand All @@ -367,6 +363,28 @@ Do to the nature of having a few necessary styles in a global stylesheet and eve

For other presets, if they don't rely on traditional `class="..."` usage you will need to first preprocess those class names into the `class="..."` attribute. If they add presets like typography's `.prose` class then you will need to place the classes which trigger the preset additions into your safelist.

### Transformers support

Transformers are supported for your CSS files (css|postcss|sass|scss|less|stylus|styl). To use them, add the transformer into the `cssFileTransformers` option in your `vite.config.ts`:

```ts
// vite.config.ts
import transformerDirectives from '@unocss/transformer-directives'

export default defineConfig({
plugins: [
UnoCSS({
cssFileTransformers: [transformerDirectives()],
}),
sveltekit(),
],
})
```

::: info
Transformers are not supported in Svelte components due to how Svelte Scoped works.
:::

## Scoped utility classes unleash creativity

Some advice on when you might want to use scoped styles: If you have come to the point in a large project's life when every time you use a class like `.md:max-w-[50vw]` that you know is only used once you cringe as you feel the size of your global style sheet getting larger and larger, then give this package a try. Hesitancy to use exactly the class you need inhibits creativity. Sure, you could use `--at-apply: md:max-w-[50vw]` in the style block but that gets tedious and styles in context are useful. Furthermore, if you would like to include a great variety of icons in your project, you will begin to feel the weight of adding them to the global stylesheet. When each component bears the weight of its own styles and icons you can continue to expand your project without having to analyze the cost benefit of each new addition.
Expand Down
2 changes: 1 addition & 1 deletion examples/sveltekit-scoped/src/app.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
body {
margin: 0;
--at-apply: m-0 p-2 bg-red-100;
}

:root {
Expand Down
3 changes: 3 additions & 0 deletions examples/sveltekit-scoped/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'

import UnoCSS from '@unocss/svelte-scoped/vite'
import transformerDirectives from '@unocss/transformer-directives'

export default defineConfig({
plugins: [
UnoCSS({
injectReset: '@unocss/reset/tailwind.css',
cssFileTransformers: [transformerDirectives()],
}),
sveltekit(),
],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"size": "esno scripts/size.ts",
"stub": "pnpm -r --filter=./packages/* --parallel run stub",
"typecheck": "tsc --noEmit",
"test": "vitest",
"test": "vitest && pnpm -F svelte-scoped test:integration",
"test:update": "vitest -u",
"test:ci": "nr build && nr typecheck && nr lint && nr test"
},
Expand Down
6 changes: 6 additions & 0 deletions packages/svelte-scoped/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export default defineBuildConfig({
clean: true,
declaration: true,
externals: [
'@ampproject/remapping',
'@jridgewell/trace-mapping',
'@jridgewell/gen-mapping',
'@jridgewell/set-array',
'@jridgewell/sourcemap-codec',
'@jridgewell/resolve-uri',
'@unocss/core',
'@unocss/config',
'@unocss/preset-uno',
Expand Down
3 changes: 2 additions & 1 deletion packages/svelte-scoped/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
"scripts": {
"build": "unbuild",
"stub": "unbuild --stub",
"bench": "vitest bench"
"bench": "vitest bench",
"test:integration": "pnpm -r --filter=\"./test/fixtures/*\" test"
},
"dependencies": {
"@unocss/config": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte-scoped/src/_preprocess/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ export interface TransformApplyOptions {
export interface SvelteScopedContext {
uno: UnoGenerator<{}>
ready: Promise<UserConfig<{}>>
}
}
24 changes: 24 additions & 0 deletions packages/svelte-scoped/src/_vite/createCssTransformerPlugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Plugin } from 'vite'
import { type PluginOptions, type UnocssPluginContext, cssIdRE } from '@unocss/core'
import type { SvelteScopedContext } from '../preprocess'
import { applyTransformers } from '../../../shared-integration/src/transformers'

const svelteIdRE = /[&?]svelte/

export function createCssTransformerPlugins(ctx: SvelteScopedContext, cssTransformers: PluginOptions['transformers']): Plugin[] {
const enforces = ['default', 'pre', 'post'] as const
return enforces.map((enforce): Plugin => ({
name: `unocss:svelte-scoped-transformers:${enforce}`,
enforce: enforce === 'default' ? undefined : enforce,

async transform(code, id) {
if (!id.match(cssIdRE) || id.match(svelteIdRE))
return
ctx.uno.config.transformers = cssTransformers ?? []
return applyTransformers({
...ctx,
affectedModules: new Set<string>(),
} as UnocssPluginContext, code, id, enforce)
},
}))
}
7 changes: 7 additions & 0 deletions packages/svelte-scoped/src/_vite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ import type { SvelteScopedContext } from '../preprocess'
import type { UnocssSvelteScopedViteOptions } from './types'
import { PassPreprocessToSveltePlugin } from './passPreprocessToSveltePlugin'
import { GlobalStylesPlugin } from './globalStylesPlugin'
import { createCssTransformerPlugins } from './createCssTransformerPlugins'

export function UnocssSvelteScopedVite(options: UnocssSvelteScopedViteOptions = {}): Plugin[] {
const context = createSvelteScopedContext(options.configOrPath)

if (context.uno.config.transformers)
throw new Error('Due to the differences in normal UnoCSS global usage and Svelte Scoped usage, "config.transformers" will be ignored. You can still use transformers in CSS files with the "cssFileTransformers" option.')

const plugins: Plugin[] = [
GlobalStylesPlugin(context, options.injectReset),
]

if (!options.onlyGlobal)
plugins.push(PassPreprocessToSveltePlugin(options, context))

if (options.cssFileTransformers)
plugins.push(...createCssTransformerPlugins(context, options.cssFileTransformers))

return plugins
}

Expand Down
6 changes: 6 additions & 0 deletions packages/svelte-scoped/src/_vite/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { UnocssSveltePreprocessOptions } from '../preprocess'
import type { PluginOptions } from '@unocss/core'

export interface UnocssSvelteScopedViteOptions extends UnocssSveltePreprocessOptions {
/**
Expand Down Expand Up @@ -27,4 +28,9 @@ export interface UnocssSvelteScopedViteOptions extends UnocssSveltePreprocessOpt
* @default false
*/
onlyGlobal?: boolean
/**
* Process CSS files using UnoCSS transformers.
* @default undefined
*/
cssFileTransformers?: PluginOptions['transformers']
}
15 changes: 15 additions & 0 deletions packages/svelte-scoped/test/fixtures/vite/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "module",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "svelte-kit sync && vitest --run"
},
"devDependencies": {
"@sveltejs/kit": "^1.20.0",
"@unocss/svelte-scoped": "workspace:*",
"@unocss/transformer-directives": "workspace:*"
}
}
7 changes: 7 additions & 0 deletions packages/svelte-scoped/test/fixtures/vite/src/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.foo {
--at-apply: gap-2;
}

.bar {
--at-apply: bg-black;
}
12 changes: 12 additions & 0 deletions packages/svelte-scoped/test/fixtures/vite/src/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
%unocss-svelte-scoped.global%
%sveltekit.head%
</head>
<body>
%sveltekit.body%
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import '../app.css'
</script>

<div class="foo bar" />
1 change: 1 addition & 0 deletions packages/svelte-scoped/test/fixtures/vite/svelte.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default {}
27 changes: 27 additions & 0 deletions packages/svelte-scoped/test/fixtures/vite/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { readFile } from 'node:fs/promises'
import { describe, expect, it } from 'vitest'
import { build } from 'vite'
import fg from 'fast-glob'

async function getGlobContent(cwd: string, glob: string) {
return await fg(glob, { cwd, absolute: true })
.then(r => Promise.all(r.map(f => readFile(f, 'utf8'))))
.then(r => r.join('\n'))
}

describe('svelte-scoped-vite', () => {
it('vite', async () => {
await build({
logLevel: 'error',
build: {
sourcemap: true,
},
})

const css = await getGlobContent(process.cwd(), '.svelte-kit/**/*.css')

expect(css).not.toContain('--at-apply')
expect(css).toContain('gap:0.5rem')
expect(css).toContain('background-color:rgba(0,0,0')
}, 10000)
})
3 changes: 3 additions & 0 deletions packages/svelte-scoped/test/fixtures/vite/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "./.svelte-kit/tsconfig.json"
}
23 changes: 23 additions & 0 deletions packages/svelte-scoped/test/fixtures/vite/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineConfig } from 'vite'
import transformerDirectives from '@unocss/transformer-directives'
import { sveltekit } from '@sveltejs/kit/vite'
import UnoCSS from '@unocss/svelte-scoped/vite'
import presetUno from '@unocss/preset-uno'

export default defineConfig({
build: {
minify: false,
},
clearScreen: false,
plugins: [
UnoCSS({
configOrPath: {
presets: [
presetUno(),
],
},
cssFileTransformers: [transformerDirectives()],
}),
sveltekit(),
],
})
7 changes: 7 additions & 0 deletions packages/svelte-scoped/test/fixtures/vite/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineProject } from 'vitest/config'

export default defineProject({
test: {
name: 'svelte-scoped:integration',
},
})
4 changes: 3 additions & 1 deletion packages/svelte-scoped/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { defineProject } from 'vitest/config'
import { defaultExclude, defineProject } from 'vitest/config'

export default defineProject({
test: {
name: 'svelte-scoped:unit',
includeSource: ['src/**/*.ts'],
exclude: [...defaultExclude, 'test/fixtures/**'],
},
})
Loading

0 comments on commit a1a4504

Please sign in to comment.