Webpack plugin for dynamically generating in-memory virtual modules
npx @tessl/cli install tessl/npm-webpack-virtual-modules@0.6.0Webpack Virtual Modules is a plugin that enables dynamical generation of in-memory virtual modules for JavaScript builds created with webpack. When virtual modules are created, all parent virtual directories that lead to the module filename are automatically created too. This plugin supports webpack's watch mode, meaning any write to a virtual module is seen by webpack as if a real file stored on disk has changed.
npm install webpack-virtual-modules --save-devconst VirtualModulesPlugin = require("webpack-virtual-modules");For TypeScript with proper typing:
import VirtualModulesPlugin = require("webpack-virtual-modules");
import { Compiler } from "webpack";
// VirtualStats class is also available
const { VirtualStats } = VirtualModulesPlugin;
// Or with CommonJS require
const VirtualModulesPlugin = require("webpack-virtual-modules");
const { Compiler } = require("webpack"); // For type reference onlyDefine virtual modules at webpack configuration time:
const VirtualModulesPlugin = require("webpack-virtual-modules");
const virtualModules = new VirtualModulesPlugin({
'node_modules/module-foo.js': 'module.exports = { foo: "foo" };',
'node_modules/module-bar.js': 'module.exports = { bar: "bar" };'
});
// webpack.config.js
module.exports = {
plugins: [virtualModules],
// ...other config
};
// Use in your code
const moduleFoo = require('module-foo');
console.log(moduleFoo.foo);Create and modify virtual modules during the build process:
const VirtualModulesPlugin = require("webpack-virtual-modules");
const webpack = require("webpack");
const virtualModules = new VirtualModulesPlugin();
const compiler = webpack({
plugins: [virtualModules],
// ...other config
});
// Create virtual modules dynamically
compiler.hooks.compilation.tap('MyPlugin', function(compilation) {
virtualModules.writeModule('dynamic-module.js', `
module.exports = {
timestamp: ${Date.now()},
message: "Generated at build time"
};
`);
});Webpack Virtual Modules works by:
Creates a new VirtualModulesPlugin instance with optional static modules.
/**
* Creates a webpack plugin for virtual modules
* @param modules - Optional object mapping file paths to module contents
*/
constructor(modules?: Record<string, string>)
interface VirtualModulesPlugin {
new (modules?: Record<string, string>): VirtualModulesPlugin;
}Usage Example:
// Empty plugin for dynamic modules only
const virtualModules = new VirtualModulesPlugin();
// Plugin with static modules
const virtualModules = new VirtualModulesPlugin({
'src/config.js': 'export const API_URL = "https://api.example.com";',
'src/version.js': `export const VERSION = "${process.env.npm_package_version}";`
});Webpack plugin interface method that attaches necessary hooks to the webpack compiler.
/**
* Attaches the plugin to a webpack compiler
* @param compiler - The webpack compiler instance
*/
apply(compiler: Compiler): voidWrites or updates a virtual module dynamically during the build process.
/**
* Writes or updates a virtual module
* @param filePath - Path to virtual module (relative to webpack context)
* @param contents - String content of the virtual module
* @throws Error if plugin not initialized with webpack compiler
*/
writeModule(filePath: string, contents: string): voidUsage Examples:
// Write a simple module
virtualModules.writeModule('utils/constants.js', `
module.exports = {
BUILD_TIME: ${Date.now()},
NODE_ENV: "${process.env.NODE_ENV}"
};
`);
// Write a JSON module
virtualModules.writeModule('data/config.json', JSON.stringify({
apiUrl: process.env.API_URL,
features: ['feature1', 'feature2']
}));
// Update existing module
virtualModules.writeModule('cache/runtime-data.js', `
module.exports = { cache: ${JSON.stringify(runtimeCache)} };
`);Retrieves list of virtual modules based on optional filter criteria.
/**
* Retrieves virtual modules based on filter criteria
* @param filter - Filter type: 'all' | 'static' | 'dynamic' (defaults to 'all')
* @returns Object mapping file paths to module contents
*/
getModuleList(filter?: 'all' | 'static' | 'dynamic'): Record<string, string>Usage Examples:
// Get all virtual modules
const allModules = virtualModules.getModuleList();
// Get only static modules (defined at construction)
const staticModules = virtualModules.getModuleList('static');
// Get only dynamic modules (created via writeModule)
const dynamicModules = virtualModules.getModuleList('dynamic');
// Inspect module contents
Object.entries(allModules).forEach(([path, contents]) => {
console.log(`Virtual module: ${path}`);
console.log(`Content: ${contents.substring(0, 100)}...`);
});Mock filesystem stats object for virtual files, used internally by the plugin.
/**
* Creates virtual file stats object compatible with Node.js fs.Stats
* @param config - Stats configuration object with filesystem properties
*/
class VirtualStats {
constructor(config: VirtualStatsConfig);
// Properties set from config
dev: number;
nlink: number;
uid: number;
gid: number;
rdev: number;
blksize: number;
ino: number;
mode: number;
size: number;
blocks: number;
atime: Date;
mtime: Date;
ctime: Date;
birthtime: Date;
/** Check if represents a directory */
isDirectory(): boolean;
/** Check if represents a file */
isFile(): boolean;
/** Check if represents a block device */
isBlockDevice(): boolean;
/** Check if represents a character device */
isCharacterDevice(): boolean;
/** Check if represents a symbolic link */
isSymbolicLink(): boolean;
/** Check if represents a FIFO */
isFIFO(): boolean;
/** Check if represents a socket */
isSocket(): boolean;
}
interface VirtualStatsConfig {
dev: number;
nlink: number;
uid: number;
gid: number;
rdev: number;
blksize: number;
ino: number;
mode: number;
size: number;
blocks: number;
atime: Date;
mtime: Date;
ctime: Date;
birthtime: Date;
}The webpack Compiler type used by the apply method (from webpack types).
// Import from webpack for complete typing
import { Compiler, SyncHook, AsyncSeriesHook } from "webpack";
// Key properties used by VirtualModulesPlugin
interface Compiler {
hooks: {
afterEnvironment: SyncHook<[]>;
afterResolvers: SyncHook<[]>;
watchRun: AsyncSeriesHook<[Compiler]>;
};
inputFileSystem: any; // webpack's internal filesystem
context: string; // webpack context/working directory
fileTimestamps: Map<string, number | { safeTime: number; timestamp: number }>;
name?: string; // optional compiler name
}The plugin throws errors in the following scenarios:
writeModule() before the plugin has been applied to a webpack compiler// Proper error handling
try {
virtualModules.writeModule('test.js', 'module.exports = { test: true };');
} catch (error) {
if (error.message.includes('Plugin has not been initialized')) {
console.error('Plugin must be applied to webpack compiler first');
}
throw error;
}Virtual modules automatically integrate with webpack's watch mode. When a virtual module is updated via writeModule(), webpack will detect the change and trigger a recompilation, just as if a physical file had been modified.
// This will trigger webpack recompilation in watch mode
compiler.hooks.compilation.tap('UpdateVirtualModule', () => {
virtualModules.writeModule('runtime-config.js', `
module.exports = { timestamp: ${Date.now()} };
`);
});