vite_plugin_fuz_css.ts

Vite plugin for fuz_css CSS generation.

Uses Vite's transform hook to extract CSS classes from source files as they're processed, including node_modules dependencies. In bundled mode (the default) it also resolves the base reset and theme variables, emitting only the rules, elements, and variables the source actually uses - including the var(--*) references of imported CSS files, like a dependency's stylesheet. Generates CSS on-demand via virtual module with HMR support.

In dev it eagerly pre-scans project sources at server startup so the first served CSS is complete (see prescan), and extracts everything else as the dev server transforms it - a pre-bundled dependency by way of the source files its sourcemap lists, or as its bundled code when those can't be read. CSS that changes while a page is still loading can't be hot-updated into it, because the virtual module hasn't evaluated yet to accept the update. So each client reports when it evaluates the module, and the server answers with an update if that client holds code a refetch would replace.

In build the generated CSS depends on every module's transform, so the virtual module loads as a placeholder rule and the build finishes it afterwards. renderChunk renders the CSS and restates the placeholder with a hash of it, so the stylesheet Vite emits - and each chunk whose name depends on that stylesheet's - gets a filename that changes when the generated CSS does. generateBundle then splices the CSS in at the placeholder's position (see css_placeholder_splice.ts), in whichever of two passes the stylesheets exist for. A code-split build has them all by then, so the first pass - at the default order, ahead of every other plugin - finishes them and those plugins' generateBundle hooks read the generated CSS. With build.cssCodeSplit: false (the build.lib default) Vite emits the single stylesheet in its own generateBundle, so a second pass at order: 'post' finishes it. An environment that emits no assets, like a plain build.ssr, may have no stylesheet to finish.

The build's passes and the dev server's evaluation report each need a different position in the plugin order, so vite_plugin_fuz_css returns several plugin objects - pass its result to plugins as is. List it after any plugin that rewrites CSS in its transform hook: the build restates the placeholder from the text its build-only object saw, so what a plugin listed later does to the virtual module's CSS (wrapping it in a layer, say) applies in dev and is lost in build. PostCSS and lightningcss always apply.

@example

// vite.config.ts import {defineConfig} from 'vite'; import jsx from 'acorn-jsx'; import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; export default defineConfig({ plugins: [ vite_plugin_fuz_css({ acorn_plugins: [jsx()], }), ], });
view source

Declarations
#

4 declarations

FUZ_CSS_BANNER
#

vite_plugin_fuz_css.ts view source

"/* generated by vite_plugin_fuz_css */" import {FUZ_CSS_BANNER} from '@fuzdev/fuz_css/vite_plugin_fuz_css.js';

Banner comment at the top and bottom of generated CSS output.

to_extraction_id
#

vite_plugin_fuz_css.ts view source

internal

Exported for its tests.

(id: string): string import {to_extraction_id} from '@fuzdev/fuz_css/vite_plugin_fuz_css.js';

Converts a Vite module id to the id its extraction is keyed by, dropping a query made only of Vite's cache-busting parameters - the v version hash the dev server gives a dependency's URL and the t HMR timestamp. Such an id is the file itself, so it's filtered, parsed, cached, and deleted as the plain path, sharing one entry with the same file's SSR transform and pre-scan.

Any other query is kept, because it names a different module than the file: Foo.svelte?svelte&type=style&lang.css is the component's CSS, and ?raw, ?url, and ?worker ids carry a generated wrapper rather than the file's source.

id

a resolved module id, as transform receives it

type string

returns

string

the id without its query when that query only busts a cache, otherwise id unchanged

vite_plugin_fuz_css
#

vite_plugin_fuz_css.ts view source

(options?: VitePluginFuzCssOptions): Plugin<any>[] import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.js';

Creates the fuz_css Vite plugin.

Extracts CSS classes from source files during Vite's transform phase and generates optimized CSS via the virtual:fuz.css virtual module.

options

default {}

returns

Plugin<any>[]

the plugin as several objects sharing state, to pass to Vite's plugins together: an enforce: 'pre' one that does the extraction, serves the virtual module, and splices the build's CSS ahead of other plugins, a build-only one positioned after Vite's CSS processing that completes the build's stylesheets, and a serve-only enforce: 'post' one that adds the evaluation report to the virtual module's client code

VitePluginFuzCssOptions
#

vite_plugin_fuz_css.ts view source

VitePluginFuzCssOptions import type {VitePluginFuzCssOptions} from '@fuzdev/fuz_css/vite_plugin_fuz_css.js';

Options for the fuz_css Vite plugin. Extends the shared base options with Vite-specific options.

inheritance

prescan?

Dev-only eager pre-scan of source files at server startup.

Extraction state accumulates from Vite's transform hook, so without a pre-scan the first CSS served on a cold start includes only the files the module graph happened to visit first, and the rest arrives as corrective HMR updates while the page loads. The pre-scan extracts every file passing filter_file under the given directories, plus the Vite root's index.html, before the virtual module is first served, so the initial CSS is complete. Warm starts are cheap via the extraction cache.

true scans src under the Vite root; false disables; an array gives custom directories (resolved against the Vite root unless absolute). The root index.html is included whichever directories are given: the dev server serves it without running transform on it, so nothing else would extract it. Pre-scanned files are kept current - one the watcher reports added or changed is extracted again, whether or not any module imports it.

node_modules dependencies are never pre-scanned, and neither are project files outside the scanned directories. They're extracted when the dev server first transforms them, which is during the first load of a page that uses them, so that page's CSS is completed by an update a moment after it's first served rather than up front (or by the next reload, with server.hmr or server.ws off).

What dev and build extract differs at the edges:

  • dev only: files under the scanned directories that no module imports (SvelteKit's src/app.html among them), including ones imported only with ?raw - the pre-scan reads them from disk, where a build extracts just the modules it transforms; and a pre-bundled dependency that's extracted from its bundled code while filter_file rejects the files it ships (the default filter skips .mjs), which a build doesn't extract at all
  • build only: an HTML entry outside the scanned directories other than the root index.html; a module of a pre-bundled dependency that the pre-bundler dropped as unused, such as one holding nothing but a @fuz-classes comment; and the comment hints and static Svelte class attributes of a pre-bundled dependency whose sourcemap is missing or names files that aren't installed, which is extracted from its bundled code instead

type boolean | string[]

default true

Depends on
#