or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-loader-runner

Runs webpack loaders programmatically with full context support and dependency tracking

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/loader-runner@4.3.x

To install, run

npx @tessl/cli install tessl/npm-loader-runner@4.3.0

index.mddocs/

Loader Runner

Loader Runner is a JavaScript library that provides a runtime execution engine for webpack loaders. It enables programmatic execution of webpack's transformation pipeline outside of webpack itself, offering a complete API for running sequences of loaders against resources with full context support, dependency tracking, and error handling.

Package Information

  • Package Name: loader-runner
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install loader-runner

Core Imports

import { runLoaders, getContext } from "loader-runner";

For CommonJS:

const { runLoaders, getContext } = require("loader-runner");

Basic Usage

import { runLoaders } from "loader-runner";
import fs from "fs";

// Execute a simple loader chain
runLoaders({
  resource: "/path/to/source.js",
  loaders: [
    "/path/to/babel-loader.js",
    "/path/to/eslint-loader.js"
  ],
  context: { minimize: true },
  readResource: fs.readFile.bind(fs)
}, (err, result) => {
  if (err) {
    console.error("Loader execution failed:", err);
    return;
  }
  
  console.log("Transformed code:", result.result.toString());
  console.log("Dependencies:", result.fileDependencies);
});

Architecture

Loader Runner implements webpack's loader execution model with these key components:

  • Loader Chain Execution: Manages the two-phase loader execution (pitching and normal phases)
  • Context Management: Provides comprehensive loader context with dependency tracking
  • Module Loading: Handles both CommonJS and ES module loaders with retry logic
  • Error Handling: Robust error handling with proper stack traces and loader isolation
  • Dependency Tracking: Tracks file, context, and missing dependencies for caching systems

Capabilities

Loader Execution

Main function to execute sequences of loaders against resources with comprehensive result metadata.

/**
 * Execute a sequence of loaders against a resource
 * @param {LoaderOptions} options - Configuration options for loader execution
 * @param {function} callback - Completion callback with signature (err, result)
 */
function runLoaders(options, callback);

interface LoaderOptions {
  /** Absolute path to the resource file (optionally including query string) */
  resource?: string;
  /** Array of loader paths or loader configuration objects */
  loaders?: (string | LoaderConfig)[];
  /** Additional loader context used as base context */
  context?: object;
  /** Custom function to process the resource */
  processResource?: (loaderContext: LoaderContext, resourcePath: string, callback: Function) => void;
  /** Custom function to read the resource (defaults to fs.readFile) */
  readResource?: (path: string, callback: Function) => void;
}

interface LoaderConfig {
  /** Absolute path to the loader */
  loader: string;
  /** Loader options object */
  options?: object;
  /** Loader identifier for option serialization */
  ident?: string;
  /** Fragment identifier */
  fragment?: string;
  /** Loader type (module or commonjs) */
  type?: string;
}

interface LoaderResult {
  /** The transformation result (array of results from loader chain) */
  result?: any[];
  /** The raw resource as Buffer (useful for SourceMaps) */
  resourceBuffer?: Buffer;
  /** Whether the result is cacheable */
  cacheable: boolean;
  /** Array of file paths the result depends on */
  fileDependencies: string[];
  /** Array of directory paths the result depends on */
  contextDependencies: string[];
  /** Array of missing file paths the result depends on */
  missingDependencies: string[];
}

Usage Examples:

import { runLoaders } from "loader-runner";
import fs from "fs";

// Simple loader execution
runLoaders({
  resource: "/project/src/index.js",
  loaders: ["/node_modules/babel-loader/lib/index.js"]
}, (err, result) => {
  if (!err) {
    console.log("Result:", result.result.toString());
  }
});

// Complex loader chain with options
runLoaders({
  resource: "/project/src/styles.scss?theme=dark",
  loaders: [
    {
      loader: "/node_modules/sass-loader/dist/cjs.js",
      options: { sourceMap: true }
    },
    {
      loader: "/node_modules/css-loader/dist/cjs.js", 
      options: { modules: true }
    }
  ],
  context: { 
    mode: "production",
    minimize: true 
  },
  readResource: fs.readFile.bind(fs)
}, (err, result) => {
  if (!err) {
    console.log("CSS:", result.result.toString());
    console.log("Dependencies:", result.fileDependencies);
    console.log("Cacheable:", result.cacheable);
  }
});

// Custom resource processing
runLoaders({
  resource: "/project/data.json",
  loaders: ["/custom/json-transformer.js"],
  processResource: (loaderContext, resourcePath, callback) => {
    // Custom resource reading logic
    fs.readFile(resourcePath, 'utf8', (err, data) => {
      if (err) return callback(err);
      
      // Add dependency tracking
      loaderContext.addDependency(resourcePath);
      
      // Custom processing
      const processed = JSON.parse(data);
      callback(null, JSON.stringify(processed, null, 2));
    });
  }
}, (err, result) => {
  // Handle result
});

Context Utilities

Utility function to extract directory context from resource paths.

/**
 * Extract the directory context from a resource path
 * @param {string} resource - Resource path with optional query and fragment
 * @returns {string} Directory path of the resource
 */
function getContext(resource);

Usage Examples:

import { getContext } from "loader-runner";

// Extract directory from resource path
const context = getContext("/project/src/components/Button.jsx?inline");
console.log(context); // "/project/src/components"

// Handle paths with complex queries
const context2 = getContext("/assets/image.png?width=200&height=100#section");
console.log(context2); // "/assets"

// Root directory handling
const context3 = getContext("/index.js");
console.log(context3); // "/"

Loader Context

The loader context object provided to loaders during execution contains comprehensive information and utilities:

Context Properties

interface LoaderContext {
  /** Directory of the resource being processed */
  context: string;
  /** Full resource path with query and fragment */
  resource: string;
  /** Path to the resource file */
  resourcePath: string;
  /** Query string portion of the resource */
  resourceQuery: string;
  /** Fragment portion of the resource */
  resourceFragment: string;
  /** Full loader chain request string */
  request: string;
  /** Remaining loaders in the chain */
  remainingRequest: string;
  /** Current loader and remaining chain */
  currentRequest: string;
  /** Previously executed loaders */
  previousRequest: string;
  /** Current loader index in the chain */
  loaderIndex: number;
  /** Current loader options/query */
  query: object | string;
  /** Shared data object for pitch/normal phases */
  data: object;
  /** Array of all loaders in the chain */
  loaders: LoaderObject[];
  /** Async callback function (null until async() is called) */
  async: Function | null;
  /** Callback function (null until async() is called) */
  callback: Function | null;
}

Context Methods

interface LoaderContextMethods {
  /**
   * Make loader execution asynchronous
   * @returns {function} Async callback function
   */
  async(): Function;
  
  /**
   * Async callback function for returning results
   * @param {Error} err - Error if execution failed
   * @param {*} result - Transformation result
   * @param {object} sourceMap - Optional source map
   * @param {object} meta - Optional metadata
   */
  callback(err?: Error, result?: any, sourceMap?: object, meta?: object): void;
  
  /**
   * Mark the result as cacheable or non-cacheable
   * @param {boolean} flag - Cacheable flag (defaults to true)
   */
  cacheable(flag?: boolean): void;
  
  /**
   * Add a file dependency for caching
   * @param {string} file - File path to add as dependency
   */
  addDependency(file: string): void;
  
  /**
   * Alias for addDependency (legacy compatibility)
   * @param {string} file - File path to add as dependency
   */
  dependency(file: string): void;
  
  /**
   * Add a directory dependency for caching
   * @param {string} context - Directory path to watch
   */
  addContextDependency(context: string): void;
  
  /**
   * Add a missing file dependency for caching
   * @param {string} missing - Missing file path
   */
  addMissingDependency(missing: string): void;
  
  /**
   * Get current file dependencies
   * @returns {string[]} Array of file dependencies
   */
  getDependencies(): string[];
  
  /**
   * Get current context dependencies
   * @returns {string[]} Array of context dependencies
   */
  getContextDependencies(): string[];
  
  /**
   * Get current missing dependencies
   * @returns {string[]} Array of missing dependencies
   */
  getMissingDependencies(): string[];
  
  /**
   * Clear all dependencies and reset cacheable flag
   */
  clearDependencies(): void;
}

Loader Object Structure

Internal structure representing individual loaders in the execution chain:

interface LoaderObject {
  /** Absolute path to the loader file */
  path: string;
  /** Query string from the loader request */
  query: string;
  /** Fragment from the loader request */
  fragment: string;
  /** Parsed options object */
  options: object;
  /** Loader identifier for options */
  ident: string;
  /** Normal phase loader function */
  normal: Function;
  /** Pitch phase loader function */
  pitch: Function;
  /** Whether loader expects raw Buffer input */
  raw: boolean;
  /** Shared data object for pitch/normal communication */
  data: object;
  /** Whether pitch phase has been executed */
  pitchExecuted: boolean;
  /** Whether normal phase has been executed */
  normalExecuted: boolean;
  /** Full loader request string */
  request: string;
  /** Loader type (module or commonjs) */
  type?: string;
}

Error Handling

Loader Runner provides comprehensive error handling with proper error propagation. Errors can occur during loader loading, resource processing, or loader execution:

Common Error Scenarios:

runLoaders({
  resource: "/path/to/file.js",
  loaders: ["/invalid/loader.js"]
}, (err, result) => {
  if (err) {
    console.log("Execution error:", err.message);
    // Errors may include loader loading failures, 
    // resource processing errors, or loader execution errors
  }
});

Advanced Usage

Custom Resource Processing

runLoaders({
  resource: "/project/template.html",
  loaders: ["/custom/template-loader.js"],
  processResource: (loaderContext, resourcePath, callback) => {
    // Custom resource processing with caching control
    loaderContext.addDependency(resourcePath);
    
    // Custom read logic (e.g., from memory, network, etc.)
    customReadFunction(resourcePath, (err, content) => {
      if (err) return callback(err);
      callback(null, content);
    });
  }
}, callback);

Loader Chain Optimization

// Conditional loader application
const loaders = [];
if (process.env.NODE_ENV === 'development') {
  loaders.push('/dev/source-map-loader.js');
}
loaders.push('/transpiler/babel-loader.js');

runLoaders({
  resource: "/src/app.js",
  loaders: loaders,
  context: { mode: process.env.NODE_ENV }
}, callback);

Dependency Management

runLoaders(options, (err, result) => {
  if (!err && result) {
    // Use dependency information for build optimization
    console.log("Watch these files:", result.fileDependencies);
    console.log("Watch these directories:", result.contextDependencies);
    console.log("Cache valid:", result.cacheable);
    
    // Implement custom caching based on dependencies
    if (result.cacheable) {
      cache.set(cacheKey, result.result, result.fileDependencies);
    }
  }
});