CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-postcss-url

PostCSS plugin to rebase, inline, or copy assets from url() declarations with support for multiple transformation modes and advanced filtering options.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

processing-modes.mddocs/

Processing Modes

PostCSS URL provides four distinct processing modes for transforming CSS url() declarations: rebase, inline, copy, and custom. Each mode optimizes assets differently based on deployment needs and performance requirements.

Capabilities

Rebase Mode

Adjusts relative URLs based on the difference between source and target file locations. This is the default mode when no options are specified.

/**
 * Rebase mode configuration
 * @param {RebaseOptions} options - Rebase-specific options
 */
interface RebaseOptions {
  url: 'rebase';
  /** Optional assets path for URL adjustment */
  assetsPath?: string;
}

Usage Example:

const postcss = require('postcss');
const postcssUrl = require('postcss-url');

// Rebase URLs based on file locations
const result = await postcss()
  .use(postcssUrl({
    url: 'rebase'
  }))
  .process(css, {
    from: 'src/styles/components/button.css',
    to: 'dist/styles/main.css'
  });

// Before: background: url('../images/icon.png');
// After:  background: url('../src/styles/images/icon.png');

With Assets Path:

// Rebase with custom assets directory
const result = await postcss()
  .use(postcssUrl({
    url: 'rebase',
    assetsPath: 'static/assets'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

Inline Mode

Converts asset files to data URIs (base64 or URI-encoded) and embeds them directly in the CSS. Ideal for small assets to reduce HTTP requests.

/**
 * Inline mode configuration
 * @param {InlineOptions} options - Inline-specific options
 */
interface InlineOptions {
  url: 'inline';
  /** Base path(s) to search for assets */
  basePath?: string | string[];
  /** Encoding type for data URI */
  encodeType?: 'base64' | 'encodeURI' | 'encodeURIComponent';
  /** Maximum file size in KB to inline */
  maxSize?: number;
  /** Fallback mode when file exceeds maxSize */
  fallback?: 'copy' | 'rebase' | ((asset: Asset, dir: Dir, options: any) => string);
  /** Include URI fragment in data URI */
  includeUriFragment?: boolean;
  /** Suppress SVG fragment warnings */
  ignoreFragmentWarning?: boolean;
  /** Optimize SVG encoding for better compression */
  optimizeSvgEncode?: boolean;
}

Basic Inline Example:

// Inline all assets as data URIs
const result = await postcss()
  .use(postcssUrl({
    url: 'inline'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Before: background: url('./icon.png');
// After:  background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...');

Advanced Inline Example:

// Inline with size limits and fallback
const result = await postcss()
  .use(postcssUrl({
    url: 'inline',
    maxSize: 10,          // 10KB limit
    fallback: 'copy',     // Copy large files
    encodeType: 'base64', // Use base64 encoding
    basePath: ['src/assets', 'node_modules/icons']
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

SVG Optimization Example:

// Optimized SVG inlining
const result = await postcss()
  .use(postcssUrl({
    url: 'inline',
    encodeType: 'encodeURIComponent',
    optimizeSvgEncode: true,      // Optimize SVG encoding
    ignoreFragmentWarning: true   // Suppress fragment warnings
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// SVG files are URI-encoded with optimizations
// Before: background: url('./icon.svg');
// After:  background: url('data:image/svg+xml,%3Csvg xmlns%3D...');

MIME Type Detection:

The inline mode automatically detects file types using the mime package and applies appropriate encoding:

// Automatic MIME type detection and encoding selection
const result = await postcss()
  .use(postcssUrl({
    url: 'inline'
    // SVG files automatically use 'encodeURIComponent' 
    // Other files automatically use 'base64'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// PNG: data:image/png;base64,iVBORw0KGgoAAAANSUh...
// SVG: data:image/svg+xml,%3Csvg xmlns%3D%22http...
// JPEG: data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...

Copy Mode

Copies asset files to a specified directory and updates URLs to point to the copied files. Supports hash-based renaming for cache busting.

/**
 * Copy mode configuration
 * @param {CopyOptions} options - Copy-specific options
 */
interface CopyOptions {
  url: 'copy';
  /** Directory to copy assets (relative to 'to' option or absolute) */
  assetsPath: string;
  /** Base path(s) to search for assets */
  basePath?: string | string[];
  /** Use content hash for filenames */
  useHash?: boolean;
  /** Hash generation options */
  hashOptions?: HashOptions;
}

interface HashOptions {
  /** Hash algorithm or custom function */
  method?: 'xxhash32' | 'xxhash64' | string | ((content: Buffer) => string);
  /** Number of characters to keep from hash */
  shrink?: number;
  /** Prepend original filename to hash */
  append?: boolean;
}

// Default hash options
const defaultHashOptions = {
  method: 'xxhash32',
  shrink: 8,
  append: false
};

Basic Copy Example:

// Copy assets to specified directory
const result = await postcss()
  .use(postcssUrl({
    url: 'copy',
    assetsPath: 'static/images'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Before: background: url('./icon.png');
// After:  background: url('static/images/icon.png');
// File copied: dist/static/images/icon.png

Hash-based Copy Example:

// Copy with hash-based filenames
const result = await postcss()
  .use(postcssUrl({
    url: 'copy',
    assetsPath: 'assets',
    useHash: true,
    hashOptions: {
      method: 'xxhash32',
      shrink: 8,
      append: true  // Prepend original filename
    }
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Before: background: url('./icon.png');
// After:  background: url('assets/icon_a1b2c3d4.png');
// File copied: dist/assets/icon_a1b2c3d4.png

Crypto Hash Support:

The plugin supports Node.js crypto module hash algorithms as well as xxhash:

// Using crypto hash algorithms
const result = await postcss()
  .use(postcssUrl({
    url: 'copy',
    assetsPath: 'assets',
    useHash: true,
    hashOptions: {
      method: 'sha256',    // Any Node.js crypto hash: md5, sha1, sha256, etc.
      shrink: 16,          // Keep first 16 characters
      append: false
    }
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Before: background: url('./icon.png');
// After:  background: url('assets/9f86d081884c7d65.png');

Custom Mode

Allows custom transformation functions for advanced URL processing scenarios like CDN integration or dynamic path generation.

/**
 * Custom mode configuration
 * @param {CustomOptions} options - Custom function options
 */
interface CustomOptions {
  url: (asset: Asset, dir: Dir, options: CustomOptions, decl: any, warn: Function, result: any, addDependency: Function) => string | Promise<string>;
  /** Allow processing with subsequent options */
  multi?: boolean;
  /** Custom option properties */
  [key: string]: any;
}

interface CustomFunctionParameters {
  /** Asset information object */
  asset: Asset;
  /** Directory information object */
  dir: Dir;
  /** Current option configuration */
  options: CustomOptions;
  /** PostCSS declaration object */
  decl: any;
  /** Warning function for reporting issues */
  warn: (message: string) => void;
  /** PostCSS result object */
  result: any;
  /** Function to add file dependencies */
  addDependency: (filePath: string) => void;
}

CDN Transform Example:

// Transform URLs to CDN paths
const result = await postcss()
  .use(postcssUrl({
    url: (asset, dir, options) => {
      if (asset.url.startsWith('/images/')) {
        return `https://cdn.example.com${asset.url}`;
      }
      return asset.url; // Return unchanged
    }
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Before: background: url('/images/hero.jpg');
// After:  background: url('https://cdn.example.com/images/hero.jpg');

Multi-processing Example:

// Custom function with multi-option processing
const result = await postcss()
  .use(postcssUrl([
    {
      url: (asset, dir, options, decl, warn) => {
        // Log all processed assets
        console.log(`Processing: ${asset.url}`);
        return asset.url; // Pass through unchanged
      },
      multi: true // Allow subsequent processing
    },
    {
      url: 'copy',
      assetsPath: 'assets'
    }
  ]))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

Async Custom Function Example:

// Async custom transformation
const result = await postcss()
  .use(postcssUrl({
    url: async (asset, dir, options) => {
      // Async processing (e.g., API calls, file system operations)
      const optimizedUrl = await optimizeAssetUrl(asset.url);
      return optimizedUrl;
    }
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

async function optimizeAssetUrl(url) {
  // Custom async logic
  return url;
}

Mode Selection

The plugin automatically selects the appropriate processing mode based on configuration and provides fallback handling for error conditions.

interface ModeSelection {
  /** Default mode when no options specified */
  default: 'rebase';
  /** Available built-in modes */
  modes: ['rebase', 'inline', 'copy'];
  /** Custom function detection */
  custom: 'typeof options.url === "function"';
  /** Error handling for invalid modes */
  validation: 'throws Error for unknown modes';
}

Install with Tessl CLI

npx tessl i tessl/npm-postcss-url

docs

asset-filtering.md

encoding-optimization.md

index.md

plugin-options.md

processing-modes.md

tile.json