Vite plugin for copying static files to build output with development server support
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Vite 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
}
});