Minimal, unopinionated static site generator powered by webpack
npx @tessl/cli install tessl/npm-static-site-generator-webpack-plugin@3.4.0Static Site Generator Webpack Plugin is a minimal, unopinionated static site generator powered by webpack. It enables static site generation by executing a custom render function at build time to generate HTML files for specified paths, supporting both manual path specification and automatic site crawling.
npm install --save-dev static-site-generator-webpack-pluginconst StaticSiteGeneratorPlugin = require('static-site-generator-webpack-plugin');For ES modules:
import StaticSiteGeneratorPlugin from 'static-site-generator-webpack-plugin';// webpack.config.js
const StaticSiteGeneratorPlugin = require('static-site-generator-webpack-plugin');
module.exports = {
entry: './index.js',
output: {
filename: 'index.js',
path: 'dist',
libraryTarget: 'umd' // Required for Node.js execution
},
plugins: [
new StaticSiteGeneratorPlugin({
paths: ['/hello/', '/world/'],
locals: {
greet: 'Hello'
}
})
]
};
// index.js - render function
module.exports = function render(locals) {
return '<html>' + locals.greet + ' from ' + locals.path + '</html>';
};
// ES module default export pattern
export default function render(locals) {
return `<html>${locals.greet} from ${locals.path}</html>`;
};The plugin integrates into webpack's compilation process through several key components:
thisCompilation and optimizeAssets phasesCreates a webpack plugin instance with configuration options for static site generation.
/**
* Static Site Generator Webpack Plugin constructor
* @param {Object} options - Configuration options
*/
function StaticSiteGeneratorWebpackPlugin(options);
interface StaticSiteGeneratorOptions {
/** Webpack entry chunk name (defaults to first chunk if not specified) */
entry?: string;
/** Array of paths OR single path to render (defaults to ['/'] if not provided) */
paths?: string[] | string;
/** Custom properties merged into locals object passed to render function */
locals?: object;
/** Object that exists in global scope when executing render function */
globals?: object;
/** Enables automatic crawling of relative links and iframes */
crawl?: boolean;
}Usage Examples:
// Basic configuration with multiple paths
new StaticSiteGeneratorPlugin({
paths: ['/about/', '/contact/', '/blog/'],
locals: {
siteName: 'My Website',
version: '1.0.0'
}
});
// Single path configuration (automatically converted to array)
new StaticSiteGeneratorPlugin({
paths: '/', // Equivalent to paths: ['/']
locals: { data: 'value' }
});
// Crawling mode
new StaticSiteGeneratorPlugin({
crawl: true,
paths: ['/'], // Entry point for crawling
locals: { navigation: menuData }
});
// Custom entry point and globals
new StaticSiteGeneratorPlugin({
entry: 'main',
paths: ['/'],
globals: { window: {} }, // For browser-dependent libraries
locals: { config: appConfig }
});The entry file must export a render function that accepts locals and returns HTML. The plugin supports three execution patterns:
/** Synchronous render function */
function render(locals: RenderLocals): string;
/** Callback-based render function */
function render(locals: RenderLocals, callback: (error: Error | null, html: string) => void): void;
/** Promise-based render function */
function render(locals: RenderLocals): Promise<string>;
/** Multi-path render function returning object with path-to-HTML mapping */
function render(locals: RenderLocals): string | Promise<string> | { [path: string]: string };
interface RenderLocals {
/** The path currently being rendered */
path: string;
/** Object containing all webpack assets by chunk name */
assets: { [chunkName: string]: string };
/** Advanced webpack compilation statistics */
webpackStats: object;
/** Custom properties from plugin configuration */
[key: string]: any;
}Usage Examples:
// Synchronous rendering
module.exports = function render(locals) {
return `<html><body><h1>Welcome to ${locals.path}</h1></body></html>`;
};
// Async rendering with callbacks
module.exports = function render(locals, callback) {
setTimeout(() => {
const html = generatePage(locals.path, locals.data);
callback(null, html);
}, 100);
};
// Promise-based rendering
module.exports = function render(locals) {
return fetchPageData(locals.path)
.then(data => renderTemplate(data))
.then(html => html);
};
// Multi-path rendering
module.exports = function render(locals) {
return {
'/': '<html>Home Page</html>',
'/about': '<html>About Page</html>',
'/contact': '<html>Contact Page</html>'
};
};
// Using assets in render function
module.exports = function render(locals) {
const jsFiles = Object.values(locals.assets).filter(file => file.endsWith('.js'));
const cssFiles = Object.values(locals.assets).filter(file => file.endsWith('.css'));
return `
<html>
<head>
${cssFiles.map(css => `<link rel="stylesheet" href="${css}">`).join('\\n')}
</head>
<body>
<h1>Page: ${locals.path}</h1>
${jsFiles.map(js => `<script src="${js}"></script>`).join('\\n')}
</body>
</html>
`;
};
// Note: Asset paths in locals.assets automatically include webpack's
// output.publicPath if configured, so no manual path resolution neededAutomatic discovery of additional paths by parsing HTML for relative links and iframe sources.
When crawl: true is enabled, the plugin:
<a href="..."> and <iframe src="..."> tagsCrawling Rules:
/path to /path/index.html if no file extension//example.com)http://, https://, ftp://)/about) and document-relative (../contact) links// Enable crawling with entry points
new StaticSiteGeneratorPlugin({
crawl: true,
paths: ['/', '/blog/'], // Multiple crawl entry points
locals: { siteData: data }
});Generate custom file names by providing paths ending in .html.
// Configuration for custom file names
new StaticSiteGeneratorPlugin({
paths: [
'/index.html',
'/news.html',
'/about.html'
]
});Note: Custom file names may break compatibility with client-side routing if using frameworks like React Router.
Support for legacy constructor signature with positional arguments.
/**
* Legacy constructor signature (deprecated)
* @param {string} entry - Entry chunk name
* @param {string[]} paths - Paths to render
* @param {object} locals - Local variables
* @param {object} globals - Global variables
*/
function StaticSiteGeneratorWebpackPlugin(entry, paths, locals, globals);Usage Example:
// Legacy usage (deprecated)
new StaticSiteGeneratorPlugin(
'main', // entry
['/'], // paths
{ data: 'value' }, // locals
{ window: {} } // globals
);The plugin handles several error conditions:
Common error messages:
Source file not found: "entryName" - Entry chunk not found in webpack outputExport from "entryName" must be a function that returns an HTML string. Is output.libraryTarget in the configuration set to "umd"? - Entry file doesn't export a function or webpack output configuration is incorrectoutput.libraryTarget to 'umd' or 'commonjs' for Node.js executionoptimize-assets phase// Required webpack configuration
module.exports = {
output: {
libraryTarget: 'umd' // Essential for Node.js execution
},
plugins: [
new StaticSiteGeneratorPlugin(options)
]
};