html-bundler-webpack-plugin
Version:
Generates complete single-page or multi-page website from source assets. Built-in support for Markdown, Eta, EJS, Handlebars, Nunjucks, Pug. Alternative to html-webpack-plugin.
472 lines (418 loc) • 15.7 kB
TypeScript
import { Compiler, Compilation, LoaderContext, WebpackPluginInstance, PathData, AssetInfo } from 'webpack';
import { Options as MinifyOptions } from 'html-minifier-terser';
import { AsyncSeriesHook, AsyncSeriesWaterfallHook, SyncWaterfallHook } from 'tapable';
declare class HtmlBundlerPlugin implements WebpackPluginInstance {
/**
* The path to the template loader.
*/
static loader: string;
static option: HtmlBundlerPlugin.OptionPluginInterface;
static getHooks(compilation: Compilation): HtmlBundlerPlugin.Hooks;
constructor(options?: HtmlBundlerPlugin.PluginOptions);
apply(compiler: Compiler): void;
}
declare namespace HtmlBundlerPlugin {
export interface OptionPluginInterface {
isEnabled: () => boolean;
isProduction: () => boolean;
isMinify: () => boolean;
isVerbose: () => boolean;
isEntry: (resource: string) => boolean;
isScript: (resource: string) => boolean;
isStyle: (resource: string) => boolean;
/**
* Resolve undefined|true|false|''|'auto' value depend on current Webpack mode dev/prod.
*
* @param {boolean|string|undefined} value The value one of the values: true, false, 'auto'.
* @param {boolean} autoValue Returns the autoValue in prod mode when value is 'auto'.
* @param {boolean|string} defaultValue Returns default value when value is undefined.
* @return {boolean}
*/
toBool: (value: boolean | string | undefined, autoValue: boolean, defaultValue: boolean | string) => boolean;
get: () => PluginOptions;
getWebpackOutputPath: () => string;
getPublicPath: () => string;
}
export interface PluginOptions {
// callbacks
beforePreprocessor?: BeforePreprocessor;
preprocessor?: Preprocessor;
preprocessorOptions?: Object;
// postprocess of rendered template, called after js template was compiled into html
postprocess?: Postprocess;
beforeEmit?: BeforeEmit;
afterEmit?: AfterEmit;
// enable the plugin, used for debugging
enabled?: boolean;
// match of entry template files
test?: RegExp;
/**
* The key is route to output file w/o an extension, value is a template source file.
* If the value is string, it should be an absolute or relative path to templates.
* If the entry is undefined, then must be defined the Webpack entry option.
*/
entry?: EntryObject | Array<EntryDescription> | string;
entryFilter?: EntryFilter;
// defaults is options.output.path
outputPath?: string;
// html output filename
filename?: FilenameTemplate;
js?: JsOptions;
css?: CssOptions;
svg?: SvgOptions;
/**
* The references to LoaderOptions.
* It's syntactic "sugar" to avoid the complicated structure of options.
*/
data?: { [key: string]: any } | string;
// generates preload link tags for assets
preload?: Preload;
minify?: 'auto' | boolean | MinifyOptions;
minifyOptions?: MinifyOptions;
/**
* The stage to render final HTML in the `processAssets` Webpack hook.
*/
renderStage?: null | number;
/**
* Whether comments should be extracted to a separate file.
* If the file foo.js contains the license banner, then the comments will be stored to foo.js.LICENSE.txt.
* This option enables/disable storing of *.LICENSE.txt file.
* For more flexibility use terser-webpack-plugin https://webpack.js.org/plugins/terser-webpack-plugin/#extractcomments.
*/
extractComments?: boolean;
integrity?: 'auto' | boolean | IntegrityOptions;
// paths and files to watch file changes
watchFiles?: WatchFiles;
/**
* Whether in serve/watch mode should be added hot-update.js file in html.
* Use it only if you don't have a referenced source file of a script in html.
* If you already have a js file, this setting should be false as Webpack automatically injects the hot update code into the compiled js file.
*/
hotUpdate?: boolean;
verbose?: 'auto' | boolean;
/**
* The reference to LoaderOptions.
* Use this option here to avoid definition of the rule for the template loader module.
*/
loaderOptions?: LoaderOptions;
/**
* The reference to LoaderOptions.sources.
*/
sources?: Sources;
/**
* The router options.
*/
router?: Router;
/**
* The undocumented experimental options for private usage or testing only.
*/
experiments?: { [key: string]: any };
}
export interface LoaderOptions {
root?: string | boolean;
context?: string;
beforePreprocessor?: BeforePreprocessor;
preprocessor?: Preprocessor;
preprocessorOptions?: Object;
sources?: Sources;
/**
* Global data passed in all templates.
*/
data?: Data;
}
export interface Hooks {
beforePreprocessor: AsyncSeriesWaterfallHook<
[content: string, loaderContext: BundlerPluginLoaderContext, callback?: (error: Error | null) => void]
>;
preprocessor: AsyncSeriesWaterfallHook<
[content: string, loaderContext: BundlerPluginLoaderContext, callback?: (error: Error | null) => void]
>;
// TODO: implement afterPreprocessor when will be required the feature
//afterPreprocessor: AsyncSeriesWaterfallHook<[content: string, loaderContext: BundlerPluginLoaderContext]>;
/**
* Called after resolve of a source attribute defined by loaderOptions.source.
* Return a string to override the resolved value of the attribute.
*/
resolveSource: SyncWaterfallHook<
[
source: string,
info: { type: string; tag: string; attribute: string; value: string; resolvedFile: string; issuer: string },
]
>;
postprocess: AsyncSeriesWaterfallHook<[content: string, info: TemplateInfo]>;
beforeEmit: AsyncSeriesWaterfallHook<[content: string, entry: CompileEntry]>;
afterEmit: AsyncSeriesHook<[entries: CompileEntries]>;
integrityHashes: AsyncSeriesHook<
[
// the map of the output asset filename to its integrity hash
hashes: Map<string, string>,
]
>;
}
}
/**
* Context of bundler loader.
*/
type BundlerPluginLoaderContext = LoaderContext<Object> & { data: { [key: string]: any } | string };
/**
* Multiple entry bundles are created. The key is the entry name. The value can be a string or an entry description object.
*/
type EntryObject = {
/**
* An entry point with name.
* If the value is string, it should be an absolute or relative path to a JSON/JS file.
*/
[name: string]: EntryDescription | string;
};
/**
* An object with entry point description.
*/
type EntryDescription = {
/**
* Template file, relative of context or absolute.
*/
import: string;
/**
* The output filename template.
*/
filename?: FilenameTemplate;
/**
* The data passed to the imported template.
*/
data?: Data;
};
/**
* The template data passed to the preprocessor as an object, or the path to a file that exports the data as an object.
* If the value is string, it should be an absolute or relative path to a JSON/JS file.
*/
type Data = { [key: string]: any } | string;
type JsOptions = {
// undocumented, for debugging
enabled?: boolean;
// undocumented, for debugging
test?: RegExp;
// The output filename of extracted JavaScript. Defaults `[name].js`.
filename?: FilenameTemplate;
// The output filename of non-initial chunk file. Defaults `[id].js`.
chunkFilename?: FilenameTemplate;
// The output directory for an asset. Defaults, `webpack.output.path`.
outputPath?: string;
// Whether the compiled JavaScript should be inlined. Defaults, `false`.
inline?: 'auto' | boolean | JsInlineOptions;
};
/**
* An object with js inline options.
* When the chunk or the source filter(s) is/are defined, then apply the filer.
*/
type JsInlineOptions = {
// Regards the chunk or the source filters only if `enabled` is true or `auto` evaluates to true.
enabled?: 'auto' | boolean;
// Inlines the single chunk when output chunk filename matches a regular expression(s).
chunk?: RegExp | Array<RegExp>;
// Inlines all chunks when source filename matches a regular expression(s).
source?: RegExp | Array<RegExp>;
// Filter function to keep/remove attributes for inlined script tag. If undefined, all attributes will be removed.
attributeFilter?: (props: {
attribute: string;
value: string;
attributes: { [attributeName: string]: string };
}) => boolean | void;
};
type CssOptions = {
// undocumented
enabled?: boolean;
// RegEx to match style files.
test?: RegExp;
// The output filename of extracted CSS.
filename?: FilenameTemplate;
// The output filename of non-initial chunk file, e.g., a style file imported in JavaScript.
chunkFilename?: FilenameTemplate;
// The output directory for an asset. Defaults, `webpack.output.path`.
outputPath?: string;
// Whether the compiled JavaScript should be inlined. Defaults, `false`.
inline?: 'auto' | boolean;
// Inject CSS into the DOM and enable HMR. Works only for styles imported in JavaScript files.
// Note:
// - `devServer.hot` option must be enabled (defaults)
// - `devServer.watchFiles.paths` option must contains files excluding CSS/SCSS, e.g. `['src/**/*.(html)']`
hot?: boolean;
};
type SvgOptions = {
enabled?: boolean;
// RegEx to match SVG files.
// Defaults `/\.svg/i`.
test?: RegExp;
inline?: {
// Enable inline SVG by replacing <img> with <svg>, only in HTML.
// Equivalent to query: `?inline=embed` | `?embed`.
// Defaults `false`.
embed?: boolean;
// Data URL encoding, overrides `generator.dataUrl.encoding` option.
// Equivalent to query: `?inline=base64` | `?inline=escape`.
// Defaults the `generator.dataUrl.encoding` option, if undefined then `base64`.
encoding?: 'base64' | false;
};
};
type IntegrityOptions = {
enabled?: 'auto' | boolean;
hashFunctions?: HashFunctions | Array<HashFunctions>;
};
type HashFunctions = 'sha256' | 'sha384' | 'sha512';
/**
* Specifies the filename template of output files on disk.
* You must **not** specify an absolute path here, but the path may contain folders separated by '/'!
* The specified path is joined with the value of the 'output.path' option to determine the location on the disk.
*/
type FilenameTemplate = string | ((pathData: PathData, assetInfo?: AssetInfo) => string);
type BeforePreprocessor = false | ((content: string, loaderContext: BundlerPluginLoaderContext) => string | undefined);
type Preprocessor =
| false
| 'eta'
| 'ejs'
| 'handlebars'
| 'nunjucks'
| 'pug'
| 'tempura'
| 'twig'
| ((content: string, loaderContext: BundlerPluginLoaderContext) => string | Promise<any> | undefined);
/**
* Called after the template has been compiled into html string, but not yet finalized,
* before the split chunks and inline assets are injected.
*/
type Postprocess = (content: string, templateInfo: TemplateInfo, compilation: Compilation) => string | undefined;
/**
* The object is argument of the hooks and callbacks.
*/
type TemplateInfo = {
// the entry name
name: string;
// the output asset filename relative to output path
assetFile: string;
// the source file without a query
sourceFile: string;
// the source file including a query
resource: string;
// output path of assetFile
outputPath: string;
};
/**
* Called after the processAssets hook had finished without an error, before emitting.
* At this stage, all resources are processed with plugins and have final output filenames.
* This hook is called for each compiled template defined in the entry option.
*/
type BeforeEmit = (content: string, entry: CompileEntry, compilation: Compilation) => string | undefined;
type AfterEmit = (entries: CompileEntries, compilation: Compilation) => Promise<void> | void;
/**
* The object is argument of hooks and callbacks.
*/
type CompileEntry = TemplateInfo & {
// assets used in html
assets: Array<CompileAsset>;
};
type CompileEntries = Array<CompileEntry>;
type CompileAsset = AssetScript | AssetStyle | AssetResource;
// TODO: define types AssetScript, AssetStyle, AssetResource
type AssetScript = {};
type AssetStyle = {};
type AssetResource = {};
type Preload = Array<{
test: RegExp;
filter?: PreloadFilter;
as?: string;
rel?: string;
type?: string;
attributes?: { [attributeName: string]: string | boolean };
}>;
type Sources =
| boolean
| Array<{
tag?: string;
attributes?: Array<string>;
// called before resolving
filter?: (props: {
tag: string;
attribute: string;
// original value string
value: string;
// parsed value, useful for srcset attribute with many filenames
parsedValue: Array<string>;
attributes: { [attributeName: string]: string };
resourcePath: string;
// Return:
// - false - doesn't resolve it (skip)
// - true | void - the source will be resolved automatically,
}) => boolean | void;
}>;
type Router =
| boolean
| {
enabled: boolean;
// replaces matched source route with its output URL, defaults is <PluginOption>.test depends on used preprocessor
test: RegExp | Array<RegExp>;
// Note: if a route contains a query or a segment (e.g. index.html#contact), the rewriteIndex is ignored!
// if rewriteIndex is `false` (defaults), do nothing
// if rewriteIndex is a string:
// - if publicPath is `auto` replaces "index.html" with the specified string, should be on of: ".", ""
// - if publicPath is not empty, replaces "index.html" with "" (empty string) regardless of specified value
rewriteIndex: false | string;
// called after the resolving of output filenames
resolve: (props: {
sourceRoute: string; // resolved href value (absolute path)
outputRoute: string; // output URL regards publicPath, e.g. by auto publicPath is relative to outputFile
sourceFile: string; // absolute path to the template file
outputFile: string; // output HTML filename relative to the dist/ directory
// Return:
// - void - do nothing
// - any string - the returned string replaces the value
}) => void | string;
}
// disallow `undefined` value for optional properties
| {
enabled?: never;
test?: never;
rewriteIndex?: never;
resolve?: never;
};
type WatchFiles = {
paths?: Array<string>;
includes?: Array<RegExp>;
excludes?: Array<RegExp>;
};
// Private types: used in source code only
type ResolverType =
| 'default'
| 'script'
| 'style'
// used in a preprocessor for the resolving of including partials
| 'include';
/**
* Advanced custom filter defined by user in option.
* Abstract type for EntryFilter, PreloadFilter.
*/
// type AdvancedFilter =
// | RegExp
// | Array<RegExp>
// | { includes?: Array<RegExp>; excludes?: Array<RegExp> }
// | ((value: unknown) => void | boolean);
type EntryFilter =
| RegExp
| Array<RegExp>
| { includes?: Array<RegExp>; excludes?: Array<RegExp> }
| ((file: string) => void | boolean);
/**
* Preload custom filter defined by user in option.
*/
type PreloadFilter =
| RegExp
| Array<RegExp>
| { includes?: Array<RegExp>; excludes?: Array<RegExp> }
| ((asset: { sourceFiles: Array<string>; outputFile: string }) => void | boolean);
/**
* Normalized advanced filter, ready for inner apply.
*/
type NormalizedAdvancedFilter = {
includes: Array<RegExp>;
excludes: Array<RegExp>;
fn: ((value: unknown) => void | boolean) | undefined;
};
export = HtmlBundlerPlugin;