Inject CSS at the top of chunk files in Vite library mode using import statements, supporting multiple entries.
npx @tessl/cli install tessl/npm-vite-plugin-lib-inject-css@2.2.0Vite Plugin Lib Inject CSS automatically injects CSS import statements at the top of generated JavaScript chunk files in Vite library mode builds. It solves the common problem of associating CSS styles with component libraries, enabling automatic style imports without manual intervention while maintaining SSR compatibility.
npm install vite-plugin-lib-inject-css -Dimport { libInjectCss } from 'vite-plugin-lib-inject-css';For CommonJS:
const { libInjectCss } = require('vite-plugin-lib-inject-css');import { defineConfig } from 'vite';
import { libInjectCss } from 'vite-plugin-lib-inject-css';
export default defineConfig({
plugins: [
libInjectCss(),
],
build: {
lib: {
entry: 'src/index.ts',
formats: ['es']
}
}
});The plugin works by:
cssCodeSplit: true, ssrEmitAssets: true, hoistTransitiveImports: false)@ast-grep/napi to parse JavaScript and find optimal injection points after imports and expression statementschunk.viteMetadata.importedCss to identify CSS files associated with each chunkimport './style.css'; for ES modules, require('./style.css'); for CommonJS) at the top of chunksmagic-stringCreates a Vite plugin instance that handles CSS injection in library builds.
/**
* Inject css at the top of each generated chunk file, only works with library mode.
* @returns Vite Plugin object configured for CSS injection
*/
function libInjectCss(): Plugin;Usage Examples:
import { defineConfig } from 'vite';
import { libInjectCss } from 'vite-plugin-lib-inject-css';
// Basic single-entry configuration
export default defineConfig({
plugins: [libInjectCss()],
build: {
lib: {
entry: 'src/index.ts',
formats: ['es']
}
}
});
// Multi-entry configuration
export default defineConfig({
plugins: [libInjectCss()],
build: {
lib: {
formats: ['es', 'cjs'],
entry: {
index: 'src/index.ts',
button: 'src/components/button/index.ts',
select: 'src/components/select/index.ts',
}
},
rollupOptions: {
output: {
chunkFileNames: 'chunks/[name].[hash].js',
assetFileNames: 'assets/[name][extname]',
entryFileNames: '[name].js',
},
},
}
});The plugin automatically configures several Vite options:
// Automatically set by the plugin
build: {
cssCodeSplit: true // Required for CSS injection to work
}// Automatically set by the plugin
build: {
ssrEmitAssets: true // Required for SSR builds
}// Automatically set by the plugin (can be overridden)
rollupOptions: {
output: {
hoistTransitiveImports: false // Prevents tree-shaking issues
}
}interface Plugin {
name: string;
apply: 'build';
enforce: 'post';
config(config: UserConfig): Partial<UserConfig>;
configResolved(config: ResolvedConfig): void;
options(): void;
generateBundle(opts: OutputOptions, bundle: OutputBundle): void;
}
// From Vite types
interface ResolvedConfig {
build: {
lib?: LibBuildOptions;
ssr?: boolean;
ssrEmitAssets?: boolean;
sourcemap?: boolean;
};
command: 'build' | 'serve';
}
interface OutputOptions {
format: 'es' | 'cjs' | 'umd' | 'iife';
}
interface OutputBundle {
[fileName: string]: OutputAsset | OutputChunk;
}
interface UserConfig {
build?: {
lib?: LibBuildOptions;
cssCodeSplit?: boolean;
ssrEmitAssets?: boolean;
rollupOptions?: {
output?: {
hoistTransitiveImports?: boolean;
chunkFileNames?: string;
assetFileNames?: string;
entryFileNames?: string;
};
};
};
}
interface LibBuildOptions {
entry: string | string[] | { [key: string]: string };
formats?: ('es' | 'cjs' | 'umd' | 'iife')[];
}
interface OutputAsset {
type: 'asset';
fileName: string;
source: string | Uint8Array;
}
interface OutputChunk {
type: 'chunk';
fileName: string;
code: string;
map?: any;
viteMetadata?: {
importedCss: Set<string>;
};
}The plugin has specific operating requirements:
build.lib configured)command === 'build')cssCodeSplit: true (automatically set)ssrEmitAssets: true (automatically set)The plugin provides console warnings for common configuration issues:
The plugin generates different import formats based on the output format:
// For ES modules (format: 'es')
import './assets/component.css';
// ... rest of chunk code
// For CommonJS (format: 'cjs')
require('./assets/component.css');
// ... rest of chunk codeCSS file paths are automatically calculated as relative paths from the chunk location to the CSS file location.