or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

asset-processing.mdcontent-processing.mdindex.mdminification.mdruntime-utilities.md
tile.json

content-processing.mddocs/

Content Processing

HTML Loader provides preprocessor and postprocessor hooks for custom HTML transformations, enabling integration with template engines and other processing tools.

Capabilities

Preprocessor Function

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;
    }
  }
}

Postprocessor Function

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:

  • Import statements for assets
  • HTML content as a string (potentially minified)
  • Export statement

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;
    }
  }
}

Template Engine Integration

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;
      }
    }
  }
}

Error Handling

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;
      }
    }
  }
}

Processing Order

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:

  • Preprocessor: Template compilation, conditional content, variable substitution, HTML transformation
  • Postprocessor: JavaScript code modification, adding runtime utilities, changing module format

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 */ }
    }
  ]
}