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

asset-filtering.mddocs/

Asset Filtering

PostCSS URL provides comprehensive asset filtering capabilities using minimatch patterns, regular expressions, and custom functions to selectively process CSS url() declarations based on file paths, types, or custom criteria.

Capabilities

Minimatch Pattern Filtering

Use glob-style patterns to filter assets based on file paths. Supports all minimatch features including wildcards, character ranges, and path traversal.

/**
 * Minimatch pattern filtering
 * @param {string} pattern - Glob pattern for matching file paths
 */
interface MinimatchFilter {
  filter: string;
  /** Pattern examples */
  patterns: {
    /** Match all PNG files */
    allPng: '**/*.png';
    /** Match files in specific directory */
    specificDir: 'assets/icons/*';
    /** Match multiple extensions */
    multiExt: '**/*.{jpg,jpeg,png,gif}';
    /** Match with character ranges */
    charRange: 'image[0-9].png';
    /** Exclude patterns with negation */
    exclude: '!**/temp/**';
  };
}

Usage Examples:

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

// Match all PNG files
const result = await postcss()
  .use(postcssUrl({
    filter: '**/*.png',
    url: 'inline'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Match files in specific directory
const iconResult = await postcss()
  .use(postcssUrl({
    filter: 'assets/icons/*.svg',
    url: 'inline',
    maxSize: 5
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Match multiple file types
const imageResult = await postcss()
  .use(postcssUrl({
    filter: '**/*.{jpg,jpeg,png,gif,webp}',
    url: 'copy',
    assetsPath: 'images'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

Regular Expression Filtering

Use JavaScript regular expressions for complex pattern matching with full RegExp feature support.

/**
 * Regular expression filtering
 * @param {RegExp} pattern - Regular expression for matching file paths
 */
interface RegexFilter {
  filter: RegExp;
  /** Common regex patterns */
  patterns: {
    /** Match image files */
    images: /\.(png|jpe?g|gif|svg|webp)$/i;
    /** Match absolute paths */
    absolute: /^\/[^/]/;
    /** Match CDN URLs */
    cdn: /^https?:\/\/cdn\./;
    /** Match version suffixes */
    versioned: /\?v=\d+/;
    /** Match hash fragments */
    fragments: /#[\w-]+$/;
  };
}

Usage Examples:

// Match image files with case-insensitive extension check
const imageResult = await postcss()
  .use(postcssUrl({
    filter: /\.(png|jpe?g|gif|svg|webp)$/i,
    url: 'copy',
    assetsPath: 'assets'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Match absolute paths starting with specific prefix
const cdnResult = await postcss()
  .use(postcssUrl({
    filter: /^\/cdn\//,
    url: (asset) => `https://cdn.example.com${asset.url}`
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Match URLs with version parameters
const versionedResult = await postcss()
  .use(postcssUrl({
    filter: /\?v=\d+/,
    url: 'rebase'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

Custom Function Filtering

Use custom JavaScript functions for complex filtering logic based on full asset information and context.

/**
 * Custom function filtering
 * @param {FilterFunction} fn - Custom filter function
 */
interface CustomFilter {
  filter: (asset: Asset) => boolean;
}

type FilterFunction = (asset: Asset) => boolean;

interface Asset {
  /** Original URL from CSS */
  url: string;
  /** URL pathname without search/hash */
  pathname: string;
  /** Absolute path to asset file */
  absolutePath: string;
  /** Relative path from source */
  relativePath: string;
  /** Query string from URL */
  search: string;
  /** Hash fragment from URL */
  hash: string;
}

Usage Examples:

// Filter based on file size (requires filesystem access)
const fs = require('fs');

const sizeResult = await postcss()
  .use(postcssUrl({
    filter: (asset) => {
      try {
        const stats = fs.statSync(asset.absolutePath);
        return stats.size < 10000; // Files smaller than 10KB
      } catch (e) {
        return false;
      }
    },
    url: 'inline'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Filter based on URL characteristics
const complexResult = await postcss()
  .use(postcssUrl({
    filter: (asset) => {
      // Only process local assets with no query parameters
      return !asset.url.startsWith('http') && 
             !asset.search && 
             asset.pathname.includes('/local/');
    },
    url: 'copy',
    assetsPath: 'assets'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Filter based on relative path structure
const pathResult = await postcss()
  .use(postcssUrl({
    filter: (asset) => {
      // Process only assets in specific subdirectories
      const pathParts = asset.relativePath.split('/');
      return pathParts.includes('icons') || pathParts.includes('images');
    },
    url: 'inline',
    maxSize: 8
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

Multiple Filter Processing

Combine multiple filters in an options array for complex, multi-stage asset processing with different rules for different asset types.

/**
 * Multiple filter configuration
 * @param {FilterOptions[]} options - Array of filter-specific options
 */
interface MultipleFilters {
  options: FilterOptions[];
  /** Processing order */
  order: 'first-match' | 'multi-option';
  /** Fallback handling */
  fallback: 'default-option' | 'no-processing';
}

interface FilterOptions {
  filter?: string | RegExp | ((asset: Asset) => boolean);
  url: ProcessingMode;
  [key: string]: any;
}

Usage Examples:

// Complex multi-filter processing
const multiResult = await postcss()
  .use(postcssUrl([
    // Inline small SVG icons
    {
      filter: 'assets/icons/*.svg',
      url: 'inline',
      maxSize: 5,
      encodeType: 'encodeURIComponent',
      optimizeSvgEncode: true
    },
    // Copy large images with hash names
    {
      filter: /\.(png|jpe?g|gif)$/i,
      url: 'copy',
      assetsPath: 'images',
      useHash: true
    },
    // CDN transform for external assets
    {
      filter: (asset) => asset.url.startsWith('/cdn/'),
      url: (asset) => `https://cdn.example.com${asset.url}`
    },
    // Default rebase for everything else
    {
      url: 'rebase'
    }
  ]))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

// Conditional processing based on environment
const env = process.env.NODE_ENV;
const envResult = await postcss()
  .use(postcssUrl([
    {
      filter: '**/*.{png,jpg,gif}',
      url: env === 'production' ? 'copy' : 'rebase',
      assetsPath: env === 'production' ? 'assets' : undefined,
      useHash: env === 'production'
    },
    {
      filter: '**/*.svg',
      url: 'inline',
      maxSize: env === 'production' ? 10 : 50
    }
  ]))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

Filter Matching Behavior

Understanding how filters are evaluated and matched against assets for predictable processing results.

interface FilterMatching {
  /** Path resolution for pattern matching */
  pathResolution: {
    /** Patterns matched against relative path from process.cwd() */
    minimatch: 'path.relative(process.cwd(), asset.absolutePath)';
    /** Regex tested against same relative path */
    regex: 'regex.test(relativePath)';
    /** Function receives full Asset object */
    custom: 'function(asset)';
  };
  
  /** Match priority in multi-option arrays */
  priority: 'first-match-wins';
  
  /** Default behavior when no filter matches */
  noMatch: 'skip-processing';
  
  /** Error handling for invalid patterns */
  errorHandling: 'continues-processing';
}

Debugging Filter Matches:

// Debug filter matching
const debugResult = await postcss()
  .use(postcssUrl({
    filter: (asset) => {
      const match = asset.relativePath.includes('icons');
      console.log(`Asset: ${asset.url}, Match: ${match}`);
      return match;
    },
    url: 'inline'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

No Filter Processing

When no filter is specified, all valid assets are processed according to the specified mode, but certain URL types are automatically ignored by the plugin's built-in validation.

/**
 * No filter configuration processes all valid assets
 */
interface NoFilter {
  /** No filter property means process all valid assets */
  filter: undefined;
  /** Built-in URL validation automatically skips certain patterns */
  validation: 'skips-invalid-urls';
  /** Automatically ignored URL patterns */
  ignored: {
    /** Data URIs */
    dataUris: 'data:*';
    /** Absolute URLs with protocol */
    protocols: 'http://, https://, ftp://, etc.';
    /** Protocol-relative URLs */
    protocolRelative: '//*';
    /** Hash-only URLs */
    hashOnly: '#*';
    /** URL-encoded hash URLs */
    encodedHash: '%23*';
    /** Absolute paths without basePath option */
    absolutePaths: '/* (when basePath not specified)';
    /** Tilde paths without basePath option */
    tildePaths: '~* (when basePath not specified)';
  };
}

Usage Example:

// Process all valid assets without filtering
const allResult = await postcss()
  .use(postcssUrl({
    // No filter = process all assets
    url: 'rebase'
  }))
  .process(css, { from: 'src/main.css', to: 'dist/main.css' });

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