CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-loader-runner

Runs webpack loaders programmatically with full context support and dependency tracking

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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);
    }
  }
});
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/loader-runner@4.3.x
Publish Source
CLI
Badge
tessl/npm-loader-runner badge