Webpack loader for processing HTML files with asset handling and minification capabilities
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
HTML Loader provides preprocessor and postprocessor hooks for custom HTML transformations, enabling integration with template engines and other processing tools.
Process HTML content before html-loader handles asset imports and minification.
/**
* Preprocessor function for custom HTML transformations before processing
* @param content - Raw HTML content as a string
* @param loaderContext - Webpack loader context object
* @returns Processed HTML content (must be valid HTML)
*/
type PreprocessorFunction = (
content: string,
loaderContext: LoaderContext
) => string | Promise<string>;
interface LoaderContext {
/** Path to the current file being processed */
resourcePath: string;
/** Directory context for the loader */
context: string;
/** Webpack mode (development/production) */
mode?: string;
/** Function to emit error messages */
emitError: (error: Error) => void;
/** Get loader options */
getOptions: (schema?: object) => any;
}Usage Examples:
// Handlebars template processing
const Handlebars = require("handlebars");
{
test: /\.hbs$/i,
loader: "html-loader",
options: {
preprocessor: (content, loaderContext) => {
let result;
try {
result = Handlebars.compile(content)({
title: "My App",
version: "1.0.0"
});
} catch (error) {
loaderContext.emitError(error);
return content;
}
return result;
}
}
}
// Async preprocessing with PostHTML
const posthtml = require("posthtml");
const posthtmlWebp = require("posthtml-webp");
{
test: /\.html$/i,
loader: "html-loader",
options: {
preprocessor: async (content, loaderContext) => {
try {
const result = await posthtml()
.use(posthtmlWebp())
.process(content);
return result.html;
} catch (error) {
loaderContext.emitError(error);
return content;
}
}
}
}
// Environment-based processing
{
test: /\.html$/i,
loader: "html-loader",
options: {
preprocessor: (content, loaderContext) => {
const isDevelopment = loaderContext.mode === 'development';
// Remove analytics scripts in development
if (isDevelopment) {
content = content.replace(
/<script[^>]*google-analytics[^>]*>[\s\S]*?<\/script>/gi,
''
);
}
return content;
}
}
}Process the generated JavaScript module code after html-loader has handled asset imports and minification.
/**
* Postprocessor function for custom transformations of generated module code
* @param content - Generated JavaScript module code as a string
* @param loaderContext - Webpack loader context object
* @returns Processed JavaScript module code
*/
type PostprocessorFunction = (
content: string,
loaderContext: LoaderContext
) => string | Promise<string>;Generated Code Context:
The postprocessor receives the final JavaScript module code that html-loader generates, which includes:
Usage Examples:
// Template literal transformation
{
test: /\.html$/i,
loader: "html-loader",
options: {
postprocessor: (content, loaderContext) => {
// Detect if template literals are supported
const isTemplateLiteralSupported = content[0] === "`";
// Transform <%= %> syntax to template literal or string concatenation
return content
.replace(/<%=/g, isTemplateLiteralSupported ? `\${` : '" +')
.replace(/%>/g, isTemplateLiteralSupported ? "}" : '+ "');
}
}
}
// Add runtime transformations
{
test: /\.html$/i,
loader: "html-loader",
options: {
postprocessor: (content, loaderContext) => {
// Add custom runtime processing
const runtimeCode = `
// Custom runtime transformation
function processHtml(html) {
return html.replace(/{{(\w+)}}/g, (match, key) => {
return window.templateVars && window.templateVars[key] || match;
});
}
`;
// Wrap the exported HTML in the processing function
return content.replace(
/export default (.*);$/,
`${runtimeCode}\nexport default processHtml($1);`
);
}
}
}
// Async postprocessing
{
test: /\.html$/i,
loader: "html-loader",
options: {
postprocessor: async (content, loaderContext) => {
const processedContent = await someAsyncTransformation(content);
return processedContent;
}
}
}Common patterns for integrating template engines with html-loader.
/**
* Template engine integration patterns
*/Handlebars Integration:
const Handlebars = require("handlebars");
// Register helpers
Handlebars.registerHelper('json', function(context) {
return JSON.stringify(context);
});
{
test: /\.hbs$/i,
loader: "html-loader",
options: {
preprocessor: (content, loaderContext) => {
const template = Handlebars.compile(content);
// Get data from various sources
const templateData = {
// From loader context
filename: path.basename(loaderContext.resourcePath),
// From environment
isDev: process.env.NODE_ENV === 'development',
// From package.json
version: require('./package.json').version
};
try {
return template(templateData);
} catch (error) {
loaderContext.emitError(new Error(`Handlebars error: ${error.message}`));
return content;
}
}
}
}Mustache Integration:
const Mustache = require("mustache");
{
test: /\.mustache$/i,
loader: "html-loader",
options: {
preprocessor: (content, loaderContext) => {
const view = {
title: "My Application",
items: ['Item 1', 'Item 2', 'Item 3']
};
return Mustache.render(content, view);
}
}
}EJS Integration:
const ejs = require("ejs");
{
test: /\.ejs$/i,
loader: "html-loader",
options: {
preprocessor: async (content, loaderContext) => {
try {
return await ejs.render(content, {
filename: loaderContext.resourcePath,
title: "My App",
env: process.env.NODE_ENV
});
} catch (error) {
loaderContext.emitError(error);
return content;
}
}
}
}Best practices for error handling in preprocessor and postprocessor functions.
/**
* Error handling patterns for content processing
*/Error Handling Examples:
{
test: /\.html$/i,
loader: "html-loader",
options: {
preprocessor: (content, loaderContext) => {
try {
// Your processing logic here
return processContent(content);
} catch (error) {
// Emit error to webpack
loaderContext.emitError(
new Error(`Preprocessor error in ${loaderContext.resourcePath}: ${error.message}`)
);
// Return original content to prevent build failure
return content;
}
},
postprocessor: (content, loaderContext) => {
try {
return transformGeneratedCode(content);
} catch (error) {
// Log error with context
const contextualError = new Error(
`Postprocessor error in ${loaderContext.resourcePath}: ${error.message}\n` +
`Generated code: ${content.substring(0, 200)}...`
);
loaderContext.emitError(contextualError);
return content;
}
}
}
}
// Async error handling
{
test: /\.html$/i,
loader: "html-loader",
options: {
preprocessor: async (content, loaderContext) => {
try {
const result = await asyncProcessing(content);
return result;
} catch (error) {
// Emit async errors properly
await loaderContext.emitError(error);
return content;
}
}
}
}Understanding the order of operations in html-loader.
/**
* Processing order in html-loader:
* 1. Preprocessor (if configured)
* 2. Asset/source processing (src, href, etc.)
* 3. HTML minification (if enabled)
* 4. JavaScript code generation
* 5. Postprocessor (if configured)
* 6. Module export
*/Timing Considerations:
// Preprocessor: Modify HTML before any html-loader processing
preprocessor: (content) => {
// Content is raw HTML
// Perfect for template compilation, variable substitution
return processedHtml;
},
// Postprocessor: Modify generated JavaScript
postprocessor: (content) => {
// Content is generated JavaScript module code
// Perfect for code transformations, additional exports
return processedJavaScript;
}When to Use Each:
Integration with Other Loaders:
// Multiple loaders with html-loader preprocessing
{
test: /\.html$/i,
use: [
{
loader: "html-loader",
options: {
preprocessor: (content) => {
// html-loader preprocessing
return content;
}
}
}
]
},
// Separate preprocessing loader + html-loader
{
test: /\.template$/i,
use: [
"html-loader",
{
loader: "template-loader", // Custom loader
options: { /* template options */ }
}
]
}