Minify and mangle WGSL shaders in Vite projects.
This plugin can process plain .wgsl files and WGSL strings embedded in TypeScript files. It removes comments and unnecessary whitespace, then safely renames a small set of local symbols.
pnpm i vite-plugin-wgsl --save-dev
Add the plugin to your Vite config:
// vite.config.ts
import { defineConfig } from 'vite';
import wgsl from 'vite-plugin-wgsl';
export default defineConfig({
plugins: [
wgsl({
include: ['src/**/*.wgsl', 'src/**/*.ts'],
}),
],
});Files ending in .wgsl are loaded as default-exported strings.
// src/shaders/triangle.wgsl
fn helper(color: vec4f) -> vec4f {
let result = color.rgb;
return vec4f(result, color.a);
}
@fragment
fn main(@location(0) color: vec4f) -> @location(0) vec4f {
return helper(color);
}import shader from './shaders/triangle.wgsl';
device.createShaderModule({ code: shader });For .ts, .tsx, .mts, and .cts files, the plugin parses the file with the typescript compiler API and only transforms string literals assigned to identifiers with the configured shader prefix.
The default prefix is VITE_SHADER_.
const VITE_SHADER_BLIT = `
fn helper(color: vec4f) -> vec4f {
let result = color.rgb;
return vec4f(result, color.a);
}
@fragment
fn main(@location(0) color: vec4f) -> @location(0) vec4f {
return helper(color);
}
`;These forms are supported:
const VITE_SHADER_MAIN = `...`;
export const VITE_SHADER_MAIN: string = `...`;
let VITE_SHADER_MAIN;
VITE_SHADER_MAIN = `...`;These are intentionally left unchanged:
const OTHER = `...`;
const VITE_SHADER_DYNAMIC = `fn main() { ${generatedBody} }`;
const VITE_SHADER_OBJECT = { source: `...` };Interpolated template literals are skipped because the plugin cannot safely parse the final shader source at build time.
interface VitePluginWgslOptions {
apply?: 'serve' | 'build' | ((config, env) => boolean);
include: string | RegExp | Array<string | RegExp>;
shaderPrefix?: string;
renameFunctions?: boolean;
renameParams?: boolean;
renameLocals?: boolean;
minify?: boolean;
}Optional. Passed through to Vite's plugin apply field.
Use it to control whether the plugin runs during dev server, production build, or a custom condition:
wgsl({
apply: 'build',
include: ['src/**/*.wgsl', 'src/**/*.ts'],
});wgsl({
apply: (_config, env) => env.command === 'build',
include: ['src/**/*.wgsl', 'src/**/*.ts'],
});Required. Selects which files the plugin should process.
wgsl({
include: ['src/**/*.wgsl', 'src/**/*.ts'],
});String patterns support *, **, ?, and simple brace alternatives like src/**/*.{ts,wgsl}. RegExp patterns are also accepted.
Defaults to VITE_SHADER_.
Use this when your embedded shader constants use a different naming convention:
wgsl({
include: 'src/**/*.ts',
shaderPrefix: 'SHADER_',
});All rename passes are enabled by default.
wgsl({
include: 'src/**/*.wgsl',
renameFunctions: true,
renameParams: true,
renameLocals: true,
});You can disable any pass:
wgsl({
include: 'src/**/*.wgsl',
renameFunctions: false,
});Defaults to true.
Set minify: false to keep original whitespace/comments while still applying enabled renames.
wgsl({
include: 'src/**/*.wgsl',
minify: false,
});This plugin is a conservative local mangler, not a full WGSL compiler.
It can rename:
- helper function names
- function parameters
- function-local
let,var, andconstdeclarations
It does not rename:
main@group,@binding,@location,@builtin, and other attribute contents- struct field names
- global resource names
- type names
- swizzles and field access after
., such as.x,.xy,.rgb,.a - WGSL keywords and reserved words
- WGSL built-in function and type names
You can use the minifier without Vite:
import { mangleWgsl } from 'vite-plugin-wgsl';
const output = mangleWgsl(source, {
renameFunctions: true,
renameParams: true,
renameLocals: true,
minify: true,
});You can also transform TypeScript source strings directly:
import { mangleTypescriptShaderStrings } from 'vite-plugin-wgsl';
const output = mangleTypescriptShaderStrings(sourceCode, {}, 'VITE_SHADER_', 'shader.ts');npm testThe test command builds the package, runs the Node test suite, checks formatting, and runs ESLint.
npm run build
npm run lintThis package deliberately avoids several harder WGSL transformations for now:
- struct type renaming
- struct field renaming
- type alias renaming
- global resource renaming
overriderenaming- custom preprocessor syntax
- interpolated shader template literals
The goal is a safe first pass for common local shader code, with predictable behavior.