Vite plugin for copying static files to build output with development server support
npx @tessl/cli install tessl/npm-vite-plugin-static-copy@2.3.0Vite plugin for copying static files to build output with development server support. It serves as an alternative to rollup-plugin-copy specifically optimized for Vite workflows, providing faster dev server startup by serving files directly during development without copying.
npm install -D vite-plugin-static-copyimport { viteStaticCopy } from "vite-plugin-static-copy";For type imports:
import type { ViteStaticCopyOptions, Target, RenameFunc, TransformFunc } from "vite-plugin-static-copy";
import type { WatchOptions } from "chokidar";import { defineConfig } from "vite";
import { viteStaticCopy } from "vite-plugin-static-copy";
export default defineConfig({
plugins: [
viteStaticCopy({
targets: [
{
src: "bin/example.wasm",
dest: "wasm-files"
}
]
})
]
});The above configuration will copy bin/example.wasm to dist/wasm-files/example.wasm during build, and serve it directly at /wasm-files/example.wasm during development.
Creates and returns an array of Vite plugins for static file copying.
/**
* Creates Vite plugins for static file copying with dev server support
* @param options - Configuration options for the plugin
* @returns Array of two Vite plugins (serve plugin and build plugin)
*/
function viteStaticCopy(options: ViteStaticCopyOptions): Plugin[];Usage Examples:
// Basic usage
viteStaticCopy({
targets: [
{ src: "assets/*.wasm", dest: "wasm" }
]
});
// Advanced configuration
viteStaticCopy({
targets: [
{
src: "data/**/*.json",
dest: "api-data",
rename: (name, ext) => `${name}.processed${ext}`
}
],
structured: true,
silent: false,
watch: {
reloadPageOnChange: true
}
});interface ViteStaticCopyOptions {
/** Array of targets to copy (required) */
targets: Target[];
/** Preserve the directory structure (default: false) */
structured?: boolean;
/** Suppress console output and ignore validation errors (default: false) */
silent?: boolean;
/** Watch configuration for development */
watch?: {
/** Chokidar watch options (from 'chokidar' package) */
options?: WatchOptions;
/** Reloads page on file change (default: false) */
reloadPageOnChange?: boolean;
};
/** Rollup hook to use during build (default: 'writeBundle') */
hook?: string;
}interface Target {
/** Source path or glob pattern(s) (required) */
src: string | string[];
/** Destination directory (required) */
dest: string;
/** Rename pattern or function */
rename?: string | RenameFunc;
/** File transformation options */
transform?: TransformOption;
/** Should timestamps on copied files be preserved? (default: false) */
preserveTimestamps?: boolean;
/** Whether to dereference symlinks (default: true) */
dereference?: boolean;
/** Whether to overwrite existing files (default: true) */
overwrite?: boolean | "error";
}/**
* Function to rename files during copying
* @param fileName - Base filename without extension
* @param fileExtension - File extension including the dot
* @param fullPath - Full path to the source file
* @returns New filename (can be Promise)
*/
type RenameFunc = (
fileName: string,
fileExtension: string,
fullPath: string
) => string | Promise<string>;
/**
* Function to transform file content during copying
* @param content - File content as string or Buffer
* @param filename - Absolute path to the file
* @returns Transformed content, or null to skip copying
*/
type TransformFunc<T extends string | Buffer> = (
content: T,
filename: string
) => T | null | Promise<T | null>;
/** Transform option can be a function or configuration object */
type TransformOption = TransformFunc<string> | TransformOptionObject;
type TransformOptionObject =
| {
encoding: Exclude<BufferEncoding, "binary">;
handler: TransformFunc<string>;
}
| {
encoding: "buffer";
handler: TransformFunc<Buffer>;
};/**
* Chokidar watch options interface (from 'chokidar' package)
* Used in the watch.options property of ViteStaticCopyOptions
*/
interface WatchOptions {
/** Indicates whether the process should continue to run as long as files are being watched */
persistent?: boolean;
/** Indicates whether to watch files that don't have read permissions */
ignored?: string | RegExp | ((path: string) => boolean);
/** If set to true then the strings passed to .watch() are treated as literal path names */
ignoreInitial?: boolean;
/** When false, only the symlinks themselves will be watched for changes instead of following the link references */
followSymlinks?: boolean;
/** The base directory from which watch paths are to be derived */
cwd?: string;
/** If set to true then the strings passed to .watch() are treated as glob patterns */
disableGlobbing?: boolean;
/** Whether to use fs.watchFile (backed by polling), or fs.watch */
usePolling?: boolean;
/** Interval of file system polling, in ms */
interval?: number;
/** Interval of file system polling for binary files, in ms */
binaryInterval?: number;
/** Indicates whether to watch dotfiles */
ignorePermissionErrors?: boolean;
/** Indicates how many levels of subdirectories will be traversed */
depth?: number;
/** Path to chokidar binary on Windows */
awaitWriteFinish?: boolean | { stabilityThreshold?: number; pollInterval?: number };
}Transform Usage Examples:
// String-based transformation
{
src: "config/*.json",
dest: "configs",
transform: (content, filename) => {
const config = JSON.parse(content);
config.buildTime = new Date().toISOString();
return JSON.stringify(config, null, 2);
}
}
// Buffer-based transformation
{
src: "images/*.png",
dest: "processed-images",
transform: {
encoding: "buffer",
handler: (buffer, filename) => {
// Process image buffer
return processImage(buffer);
}
}
}
// Conditional copying (return null to skip)
{
src: "docs/*.md",
dest: "documentation",
transform: (content, filename) => {
if (content.includes("DRAFT")) return null;
return content.replace(/DRAFT/g, "");
}
}viteStaticCopy({
targets: [
{
src: "wasm/*.wasm",
dest: "wasm-modules"
}
]
});viteStaticCopy({
targets: [
{
src: ["assets/images/*", "assets/fonts/*"],
dest: "static"
}
]
});viteStaticCopy({
targets: [
{
src: "public-data/**/*",
dest: "data"
}
],
structured: true // Preserves directory structure
});viteStaticCopy({
targets: [
{
src: "content/*.json",
dest: "api"
}
],
watch: {
reloadPageOnChange: true
}
});