Skip to content

Commit

Permalink
Vite: Support Tailwind in Svelte <style> blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
philipp-spiess committed Aug 8, 2024
1 parent d55852a commit e56dd06
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 3 deletions.
76 changes: 76 additions & 0 deletions integrations/vite/svelte.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { expect } from 'vitest'
import { candidate, html, json, test, ts } from '../utils'

test(
'production build',
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"svelte": "^4.2.18",
"tailwindcss": "workspace:^"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@tailwindcss/vite": "workspace:^",
"vite": "^5.3.5"
}
}
`,
'vite.config.ts': ts`
import { defineConfig } from 'vite'
import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
svelte({
preprocess: [vitePreprocess()],
}),
tailwindcss(),
],
})
`,
'index.html': html`
<!doctype html>
<html>
<body>
<div id="app"></div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>
`,
'src/main.ts': ts`
import App from './App.svelte'
const app = new App({
target: document.body,
})
`,
'src/App.svelte': html`
<script>
let name = 'world'
</script>
<h1 class="foo underline">Hello {name}!</h1>
<style global>
@import 'tailwindcss/utilities';
@import 'tailwindcss/theme' theme(reference);
.foo {
@apply text-red-500;
}
</style>
`,
},
},
async ({ fs, exec }) => {
await exec('pnpm vite build')

let files = await fs.glob('dist/**/*.css')
expect(files).toHaveLength(1)

await fs.expectFileToContain(files[0][0], [candidate`underline`, candidate`foo`])
},
)
5 changes: 3 additions & 2 deletions packages/@tailwindcss-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@
"@tailwindcss/oxide": "workspace:^",
"lightningcss": "^1.25.1",
"postcss-load-config": "^6.0.1",
"svelte-preprocess": "^6.0.2",
"tailwindcss": "workspace:^"
},
"devDependencies": {
"@types/node": "catalog:",
"vite": "catalog:",
"internal-postcss-fix-relative-paths": "workspace:^"
"internal-postcss-fix-relative-paths": "workspace:^",
"vite": "catalog:"
},
"peerDependencies": {
"vite": "^5.2.0"
Expand Down
48 changes: 47 additions & 1 deletion packages/@tailwindcss-vite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fixRelativePathsPlugin, { normalizePath } from 'internal-postcss-fix-rela
import { Features, transform } from 'lightningcss'
import path from 'path'
import postcssrc from 'postcss-load-config'
import { sveltePreprocess } from 'svelte-preprocess'
import { compile } from 'tailwindcss'
import type { Plugin, ResolvedConfig, Rollup, Update, ViteDevServer } from 'vite'

Expand Down Expand Up @@ -173,6 +174,7 @@ export default function tailwindcss(): Plugin[] {
}

return [
svelteProcessor(),
{
// Step 1: Scan source files for candidates
name: '@tailwindcss/vite:scan',
Expand Down Expand Up @@ -347,7 +349,11 @@ function getExtension(id: string) {

function isTailwindCssFile(id: string, src: string) {
if (id.includes('/.vite/')) return
return getExtension(id) === 'css' && src.includes('@tailwind')
const extension = getExtension(id)
const isTailwind =
(extension === 'css' && src.includes('@tailwind')) ||
(extension === 'svelte' && id.endsWith('&lang.css'))
return isTailwind
}

function optimizeCss(
Expand All @@ -373,3 +379,43 @@ function optimizeCss(
errorRecovery: true,
}).code.toString()
}

// Register a plugin that can hook into the Svelte preprocessor if svelte is
// enabled. This allows us to transform CSS in `<style>` tags and create a
// stricter version of CSS that passes the Svelte compiler.
//
// Note that these files will undergo a second pass through the vite transpiler
// later. This is necessary to compute `@tailwind utilities;` with the right
// candidate list.
//
// In practice, it is not recommended to use `@tailwind utilities;` inside
// Svelte components. Use an external `.css` file instead.
function svelteProcessor() {
return {
name: '@tailwindcss/svelte',
api: {
sveltePreprocess: sveltePreprocess({
aliases: [
['postcss', 'tailwindcss'],
['css', 'tailwindcss'],
],
tailwindcss({
content,
attributes,
}: {
content: string
attributes: Record<string, string>
}) {
let compiled = compile(
content.replace('@tailwind ', '@--skip-tailwind-directives '),
{},
).build([])
return Promise.resolve({
code: compiled.replace('@--skip-tailwind-directives ', '@tailwind '),
attributes,
})
},
}),
},
}
}
Loading

0 comments on commit e56dd06

Please sign in to comment.