or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-cache-loader

Webpack loader that caches expensive loader results to disk or database storage for improved build performance

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

To install, run

npx @tessl/cli install tessl/npm-cache-loader@4.1.0

index.mddocs/

Cache Loader

Cache Loader is a webpack loader that caches the result of expensive loaders to disk or database storage. It improves build performance by avoiding redundant processing and provides configurable caching strategies with support for custom cache directories, cache keys, comparison functions, and database integration.

Package Information

  • Package Name: cache-loader
  • Package Type: npm
  • Language: JavaScript (ES6/Node.js)
  • Installation: npm install --save-dev cache-loader
  • Webpack Compatibility: Requires webpack ^4.0.0
  • Node.js Compatibility: Requires Node.js >= 8.9.0

Core Imports

ES Module:

import cacheLoader, { pitch } from "cache-loader";

CommonJS:

const cacheLoader = require("cache-loader");
// Or access the pitch function separately
const { pitch } = require("cache-loader");

Basic Usage

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['cache-loader', 'babel-loader'],
        include: path.resolve('src'),
      },
    ],
  },
};

Advanced configuration with options:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'cache-loader',
            options: {
              cacheDirectory: path.resolve('.cache'),
              cacheIdentifier: 'my-project-v1',
              readOnly: process.env.NODE_ENV === 'production',
            },
          },
          'babel-loader',
        ],
        include: path.resolve('src'),
      },
    ],
  },
};

Architecture

Cache Loader implements a two-phase caching strategy:

  • Pitch Phase: Executed before other loaders in the chain, attempts to read cached results
  • Normal Phase: Executed after other loaders, saves their results to cache
  • Cache Storage: Default filesystem-based with optional database integration (Redis, memcached)
  • Dependency Tracking: Monitors file changes to invalidate stale cache entries
  • Precision Timing: Configurable timestamp precision for cache comparison

Capabilities

Main Loader Function

The default export loader function processes and caches loader results after expensive operations complete.

/**
 * Main webpack loader function that caches results of subsequent loaders
 * @param {...any} args - Webpack loader arguments (source, map, meta)
 * @returns {void} - Uses webpack callback mechanism
 */
function loader(...args);

Usage:

// Automatically called by webpack when loader is configured
// No direct invocation needed

Pitch Function

The pitch function executes before other loaders and attempts to return cached results to skip expensive processing.

/**
 * Webpack loader pitch function that attempts to return cached results
 * @param {string} remainingRequest - Remaining loaders in the chain
 * @param {string} prevRequest - Previous loaders that have been processed
 * @param {object} data - Data object shared between pitch and normal phases
 * @returns {void} - Uses webpack callback mechanism
 */
function pitch(remainingRequest, prevRequest, data);

Raw Mode

The loader operates in raw mode to handle binary data correctly.

/**
 * Indicates that the loader works with raw/binary data
 */
const raw = true;

Configuration Options

All configuration options are optional and have sensible defaults:

cacheContext

/**
 * Override the default cache context to generate relative cache paths
 * @type {string}
 * @default "" (uses absolute paths)
 */
cacheContext: string;

Usage:

{
  loader: 'cache-loader',
  options: {
    cacheContext: path.resolve(__dirname, 'src'),
  },
}

cacheDirectory

/**
 * Directory where cache files are stored
 * @type {string}
 * @default findCacheDir({name: 'cache-loader'}) || os.tmpdir()
 */
cacheDirectory: string;

Usage:

{
  loader: 'cache-loader',
  options: {
    cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
  },
}

cacheIdentifier

/**
 * Cache invalidation identifier used to generate hashes
 * @type {string}
 * @default "cache-loader:{version} {process.env.NODE_ENV}"
 */
cacheIdentifier: string;

Usage:

{
  loader: 'cache-loader',
  options: {
    cacheIdentifier: `my-app-${process.env.NODE_ENV}-v1.2.0`,
  },
}

cacheKey

/**
 * Custom cache key generator function
 * @type {function}
 * @param {object} options - Loader options
 * @param {string} request - Current request string
 * @returns {string} Cache key path
 * @default Built-in function using MD5 hash
 */
cacheKey: (options, request) => string;

Usage:

function customCacheKey(options, request) {
  const crypto = require('crypto');
  const hash = crypto.createHash('sha256')
    .update(`${options.cacheIdentifier}\n${request}`)
    .digest('hex');
  return path.join(options.cacheDirectory, `${hash}.cache`);
}

{
  loader: 'cache-loader',
  options: {
    cacheKey: customCacheKey,
  },
}

compare

/**
 * Custom comparison function between cached dependency and current file
 * @type {function}
 * @param {object} stats - File stats from fs.stat()
 * @param {object} dep - Cached dependency information
 * @returns {boolean} True to use cached resource
 * @default Compares mtime timestamps
 */
compare: (stats, dep) => boolean;

Usage:

function customCompare(stats, dep) {
  // Custom logic for cache validity
  return stats.mtime.getTime() === dep.mtime && stats.size === dep.size;
}

{
  loader: 'cache-loader',
  options: {
    compare: customCompare,
  },
}

precision

/**
 * Round mtime by this number of milliseconds for comparison
 * @type {number}
 * @default 0
 */
precision: number;

Usage:

{
  loader: 'cache-loader',
  options: {
    precision: 1000, // Round to nearest second
  },
}

readOnly

/**
 * Make cache read-only (no writing/updating)
 * @type {boolean}
 * @default false
 */
readOnly: boolean;

Usage:

{
  loader: 'cache-loader',
  options: {
    readOnly: process.env.NODE_ENV === 'production',
  },
}

read

/**
 * Custom cache read function (e.g., for Redis/database)
 * @type {function}
 * @param {string} cacheKey - Cache key to read
 * @param {function} callback - Callback function(err, data)
 * @returns {void}
 * @default Built-in filesystem read
 */
read: (cacheKey, callback) => void;

Usage:

function redisRead(key, callback) {
  client.get(key, (err, result) => {
    if (err) return callback(err);
    if (!result) return callback(new Error(`Key ${key} not found`));
    
    try {
      const data = JSON.parse(result);
      callback(null, data);
    } catch (e) {
      callback(e);
    }
  });
}

{
  loader: 'cache-loader',
  options: {
    read: redisRead,
  },
}

write

/**
 * Custom cache write function (e.g., for Redis/database)
 * @type {function}
 * @param {string} cacheKey - Cache key to write
 * @param {any} data - Data to cache
 * @param {function} callback - Callback function(err)
 * @returns {void}
 * @default Built-in filesystem write
 */
write: (cacheKey, data, callback) => void;

Usage:

const BUILD_CACHE_TIMEOUT = 24 * 3600; // 1 day

function redisWrite(key, data, callback) {
  client.set(key, JSON.stringify(data), 'EX', BUILD_CACHE_TIMEOUT, callback);
}

{
  loader: 'cache-loader',
  options: {
    write: redisWrite,
  },
}

Database Integration Example

Complete Redis integration example:

// webpack.config.js
const redis = require('redis');
const crypto = require('crypto');

const client = redis.createClient();
const BUILD_CACHE_TIMEOUT = 24 * 3600; // 1 day

function digest(str) {
  return crypto.createHash('md5').update(str).digest('hex');
}

function cacheKey(options, request) {
  return `build:cache:${digest(request)}`;
}

function read(key, callback) {
  client.get(key, (err, result) => {
    if (err) return callback(err);
    if (!result) return callback(new Error(`Key ${key} not found`));
    
    try {
      const data = JSON.parse(result);
      callback(null, data);
    } catch (e) {
      callback(e);
    }
  });
}

function write(key, data, callback) {
  client.set(key, JSON.stringify(data), 'EX', BUILD_CACHE_TIMEOUT, callback);
}

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'cache-loader',
            options: { cacheKey, read, write },
          },
          'babel-loader',
        ],
        include: path.resolve('src'),
      },
    ],
  },
};

Types

CacheData

/**
 * Cached data structure stored by the loader
 */
interface CacheData {
  /** Remaining request string with cache context applied */
  remainingRequest: string;
  /** File dependencies with paths and modification times */
  dependencies: Array<{
    path: string;
    mtime: number;
  }>;
  /** Context dependencies with paths and modification times */
  contextDependencies: Array<{
    path: string;
    mtime: number;
  }>;
  /** Cached loader result arguments */
  result: any[];
}

LoaderOptions

/**
 * Configuration options for cache-loader
 */
interface LoaderOptions {
  cacheContext?: string;
  cacheKey?: (options: LoaderOptions, request: string) => string;
  cacheDirectory?: string;
  cacheIdentifier?: string;
  compare?: (stats: fs.Stats, dep: { path: string; mtime: number }) => boolean;
  precision?: number;
  read?: (cacheKey: string, callback: (err?: Error, data?: CacheData) => void) => void;
  readOnly?: boolean;
  write?: (cacheKey: string, data: CacheData, callback: (err?: Error) => void) => void;
}

Performance Considerations

  • Cache Overhead: Only use for expensive loaders as there's overhead for reading/writing cache files
  • File System: Default filesystem cache works well for most use cases
  • Database Integration: Use Redis or similar for distributed builds or when filesystem cache isn't suitable
  • Precision Setting: Higher precision values can improve cache hit rates on filesystems with imprecise timestamps
  • Read-Only Mode: Improves performance in production by skipping cache writes