@@ -8,6 +8,7 @@ import fs from 'node:fs/promises'
88import path from 'path'
99import postcss from 'postcss'
1010import postcssImport from 'postcss-import'
11+ import { sveltePreprocess } from 'svelte-preprocess'
1112import type { Plugin , ResolvedConfig , Rollup , Update , ViteDevServer } from 'vite'
1213
1314export default function tailwindcss ( ) : Plugin [ ] {
@@ -60,9 +61,14 @@ export default function tailwindcss(): Plugin[] {
6061 function invalidateAllRoots ( isSSR : boolean ) {
6162 for ( let server of servers ) {
6263 let updates : Update [ ] = [ ]
63- for ( let id of roots . keys ( ) ) {
64+ for ( let [ id , root ] of roots . entries ( ) ) {
6465 let module = server . moduleGraph . getModuleById ( id )
6566 if ( ! module ) {
67+ // The module for this root might not exist yet
68+ if ( root . builtBeforeTransform ) {
69+ return
70+ }
71+
6672 // Note: Removing this during SSR is not safe and will produce
6773 // inconsistent results based on the timing of the removal and
6874 // the order / timing of transforms.
@@ -133,6 +139,7 @@ export default function tailwindcss(): Plugin[] {
133139 }
134140
135141 return [
142+ svelteProcessor ( roots ) ,
136143 {
137144 // Step 1: Scan source files for candidates
138145 name : '@tailwindcss/vite:scan' ,
@@ -184,6 +191,19 @@ export default function tailwindcss(): Plugin[] {
184191
185192 let root = roots . get ( id )
186193
194+ if ( root . builtBeforeTransform ) {
195+ root . builtBeforeTransform . forEach ( ( file ) => this . addWatchFile ( file ) )
196+ root . builtBeforeTransform = undefined
197+ // When a root was built before this transform hook, the candidate
198+ // list might be outdated already by the time the transform hook is
199+ // called.
200+ //
201+ // This requires us to build the CSS file again. However, we do not
202+ // expect dependencies to have changed, so we can avoid a full
203+ // rebuild.
204+ root . requiresRebuild = false
205+ }
206+
187207 if ( ! options ?. ssr ) {
188208 // Wait until all other files have been processed, so we can extract
189209 // all candidates before generating CSS. This must not be called
@@ -215,6 +235,18 @@ export default function tailwindcss(): Plugin[] {
215235
216236 let root = roots . get ( id )
217237
238+ if ( root . builtBeforeTransform ) {
239+ root . builtBeforeTransform . forEach ( ( file ) => this . addWatchFile ( file ) )
240+ root . builtBeforeTransform = undefined
241+ // When a root was built before this transform hook, the candidate
242+ // list might be outdated already by the time the transform hook is
243+ // called.
244+ //
245+ // Since we already do a second render pass in build mode, we don't
246+ // need to do any more work here.
247+ return
248+ }
249+
218250 // We do a first pass to generate valid CSS for the downstream plugins.
219251 // However, since not all candidates are guaranteed to be extracted by
220252 // this time, we have to re-run a transform for the root later.
@@ -261,11 +293,13 @@ function getExtension(id: string) {
261293}
262294
263295function isPotentialCssRootFile ( id : string ) {
296+ if ( id . includes ( '/.vite/' ) ) return
264297 let extension = getExtension ( id )
265298 let isCssFile =
266299 extension === 'css' ||
267300 ( extension === 'vue' && id . includes ( '&lang.css' ) ) ||
268- ( extension === 'astro' && id . includes ( '&lang.css' ) )
301+ ( extension === 'astro' && id . includes ( '&lang.css' ) ) ||
302+ ( extension === 'svelte' && id . includes ( '&lang.css' ) )
269303 return isCssFile
270304}
271305
@@ -336,6 +370,14 @@ class Root {
336370 // `renderStart` hook.
337371 public lastContent : string = ''
338372
373+ // When set, indicates that the root was built before the Vite transform hook
374+ // was being called. This can happen in scenarios like when preprocessing
375+ // `<style>` tags for Svelte components.
376+ //
377+ // It can be set to a list of dependencies that will be added whenever the
378+ // next `transform` hook is being called.
379+ public builtBeforeTransform : string [ ] | undefined
380+
339381 // The lazily-initialized Tailwind compiler components. These are persisted
340382 // throughout rebuilds but will be re-initialized if the rebuild strategy is
341383 // set to `full`.
@@ -451,3 +493,55 @@ class Root {
451493 return this . compiler . build ( [ ...this . getSharedCandidates ( ) , ...this . candidates ] )
452494 }
453495}
496+
497+ // Register a plugin that can hook into the Svelte preprocessor if svelte is
498+ // enabled. This allows us to transform CSS in `<style>` tags and create a
499+ // stricter version of CSS that passes the Svelte compiler.
500+ //
501+ // Note that these files will undergo a second pass through the vite transpiler
502+ // later. This is necessary to compute `@tailwind utilities;` with the right
503+ // candidate list.
504+ //
505+ // In practice, it is not recommended to use `@tailwind utilities;` inside
506+ // Svelte components. Use an external `.css` file instead.
507+ function svelteProcessor ( roots : DefaultMap < string , Root > ) {
508+ return {
509+ name : '@tailwindcss/svelte' ,
510+ api : {
511+ sveltePreprocess : sveltePreprocess ( {
512+ aliases : [
513+ [ 'postcss' , 'tailwindcss' ] ,
514+ [ 'css' , 'tailwindcss' ] ,
515+ ] ,
516+ async tailwindcss ( {
517+ content,
518+ attributes,
519+ filename,
520+ } : {
521+ content : string
522+ attributes : Record < string , string >
523+ filename ?: string
524+ } ) {
525+ if ( ! filename ) return
526+ let id = filename + '?svelte&type=style&lang.css'
527+
528+ let root = roots . get ( id )
529+ // Mark this root as being built before the Vite transform hook is
530+ // called. We capture all eventually added dependencies so that we can
531+ // connect them to the vite module graph later, when the transform
532+ // hook is called.
533+ root . builtBeforeTransform = [ ]
534+ let generated = await root . generate ( content , ( file ) =>
535+ root ?. builtBeforeTransform ?. push ( file ) ,
536+ )
537+
538+ if ( ! generated ) {
539+ roots . delete ( id )
540+ return { code : content , attributes }
541+ }
542+ return { code : generated , attributes }
543+ } ,
544+ } ) ,
545+ } ,
546+ }
547+ }
0 commit comments