or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

custom-minifiers.mdindex.mdminification-functions.mdplugin-configuration.md
tile.json

custom-minifiers.mddocs/

Custom Minifiers

Support for custom minification functions and advanced integration patterns. Create your own CSS minification implementations or extend existing ones with custom processing logic.

Capabilities

Custom Minifier Implementation

Define custom minification functions that integrate seamlessly with the plugin's processing pipeline.

/**
 * Basic minifier implementation function signature
 * @template T - Type of minifier options
 */
type BasicMinimizerImplementation<T> = (
  input: Input,
  sourceMap: RawSourceMap | undefined,
  minifyOptions: InferDefaultType<T>
) => Promise<MinimizedResult> | MinimizedResult;

/**
 * Helper interface for minifier functions
 */
interface MinimizeFunctionHelpers {
  /** Function to determine if the minifier supports worker threads */
  supportsWorkerThreads?: (() => boolean | undefined) | undefined;
}

/**
 * Complete minifier implementation with helpers
 */
type MinimizerImplementation<T> = T extends any[]
  ? { [P in keyof T]: BasicMinimizerImplementation<T[P]> & MinimizeFunctionHelpers; }
  : BasicMinimizerImplementation<T> & MinimizeFunctionHelpers;

Custom Minifier Configuration

Configure the plugin to use custom minification functions with typed options.

interface DefinedDefaultMinimizerAndOptions<T> {
  /** Custom minify function or array of functions */
  minify?: MinimizerImplementation<T> | undefined;
  /** Options for the minifier function(s) */
  minimizerOptions?: MinimizerOptions<T> | undefined;
}

type MinimizerOptions<T> = T extends any[]
  ? { [P in keyof T]?: InferDefaultType<T[P]> }
  : InferDefaultType<T>;

type InferDefaultType<T> = T extends infer U ? U : CustomOptions;
type CustomOptions = { [key: string]: any };

Single Custom Minifier

Create and use a single custom minification function.

Usage Examples:

// Define a custom minifier function
const customMinify = async (input, sourceMap, options) => {
  const [[filename, code]] = Object.entries(input);
  
  // Custom minification logic
  let minifiedCode = code
    .replace(/\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\//g, '') // Remove comments
    .replace(/\s+/g, ' ') // Collapse whitespace
    .trim();
  
  return {
    code: minifiedCode,
    map: sourceMap, // Pass through source map
    warnings: [],
    errors: [],
  };
};

// Indicate worker thread support
customMinify.supportsWorkerThreads = () => true;

// Use the custom minifier
new CssMinimizerPlugin({
  minify: customMinify,
  minimizerOptions: {
    preserveImportant: true,
    customOption: 'value',
  },
});

Multiple Custom Minifiers

Chain multiple custom minification functions for complex processing pipelines.

Usage Examples:

// Define multiple custom minifiers
const removeComments = async (input, sourceMap, options) => {
  const [[filename, code]] = Object.entries(input);
  const cleanCode = code.replace(/\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\//g, '');
  
  return {
    code: cleanCode,
    map: sourceMap,
    warnings: [],
    errors: [],
  };
};

const compressWhitespace = async (input, sourceMap, options) => {
  const [[filename, code]] = Object.entries(input);
  const compressedCode = code.replace(/\s+/g, ' ').trim();
  
  return {
    code: compressedCode,
    map: sourceMap,
    warnings: [],
    errors: [],
  };
};

// Set worker thread support
removeComments.supportsWorkerThreads = () => true;
compressWhitespace.supportsWorkerThreads = () => true;

// Use multiple custom minifiers
new CssMinimizerPlugin({
  minify: [removeComments, compressWhitespace],
  minimizerOptions: [
    { preserveSourceMaps: true },
    { aggressive: false },
  ],
});

Mixing Built-in and Custom Minifiers

Combine built-in minification functions with custom processing logic.

Usage Examples:

// Custom post-processing after cssnano
const customPostProcess = async (input, sourceMap, options) => {
  const [[filename, code]] = Object.entries(input);
  
  // Custom post-processing logic
  let processedCode = code;
  
  if (options.addBanner) {
    processedCode = `/* Generated by ${options.bannerText} */\n${processedCode}`;
  }
  
  if (options.addSourceInfo && !sourceMap) {
    processedCode += `\n/* Original: ${filename} */`;
  }
  
  return {
    code: processedCode,
    map: sourceMap,
    warnings: [],
    errors: [],
  };
};

customPostProcess.supportsWorkerThreads = () => true;

// Chain cssnano with custom post-processing
new CssMinimizerPlugin({
  minify: [
    CssMinimizerPlugin.cssnanoMinify,
    customPostProcess,
  ],
  minimizerOptions: [
    { preset: 'default' },
    {
      addBanner: true,
      bannerText: 'MyApp CSS Bundle',
      addSourceInfo: true,
    },
  ],
});

Error Handling in Custom Minifiers

Implement proper error handling and reporting in custom minification functions.

Usage Examples:

const robustCustomMinify = async (input, sourceMap, options) => {
  const [[filename, code]] = Object.entries(input);
  const warnings = [];
  const errors = [];
  
  try {
    // Attempt minification
    let minifiedCode = code;
    
    // Example: Validate CSS before processing
    if (!code.trim()) {
      warnings.push({
        message: 'Empty CSS file processed',
        plugin: 'custom-minify',
      });
      return { code: '', map: sourceMap, warnings, errors };
    }
    
    // Perform custom minification with error detection
    if (code.includes('invalid-css-construct')) {
      throw new Error('Invalid CSS construct detected');
    }
    
    minifiedCode = code.replace(/\s+/g, ' ').trim();
    
    // Check for potential issues
    if (minifiedCode.length > code.length * 0.9) {
      warnings.push({
        message: 'Minimal size reduction achieved',
        plugin: 'custom-minify',
        line: 1,
        column: 1,
      });
    }
    
    return {
      code: minifiedCode,
      map: sourceMap,
      warnings,
      errors,
    };
    
  } catch (error) {
    errors.push({
      message: error.message,
      stack: error.stack,
    });
    
    // Return original code on error
    return {
      code: code,
      map: sourceMap,
      warnings,
      errors,
    };
  }
};

robustCustomMinify.supportsWorkerThreads = () => true;

new CssMinimizerPlugin({
  minify: robustCustomMinify,
  minimizerOptions: {
    strict: false,
    fallbackToOriginal: true,
  },
});

Advanced Custom Minifier with Source Maps

Handle source map generation and merging in custom minification functions.

Usage Examples:

// Custom minifier with source map support
const sourceMapAwareMinify = async (input, sourceMap, options) => {
  const [[filename, code]] = Object.entries(input);
  
  // Simple minification tracking line/column changes
  const lines = code.split('\n');
  const minifiedLines = [];
  
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    const minifiedLine = line
      .replace(/\/\*.*?\*\//g, '') // Remove inline comments
      .replace(/\s+/g, ' ') // Collapse whitespace
      .trim();
    
    if (minifiedLine) {
      minifiedLines.push(minifiedLine);
    }
  }
  
  const minifiedCode = minifiedLines.join('\n');
  
  // Generate a simple source map (for demonstration)
  let generatedMap = undefined;
  
  if (sourceMap && options.generateSourceMap) {
    // In a real implementation, you would use a proper source map library
    // This is a simplified example
    generatedMap = {
      version: 3,
      sources: [filename],
      names: [],
      mappings: 'AAAA;AACA;AACA', // Simplified mapping
      sourcesContent: [code],
    };
  }
  
  return {
    code: minifiedCode,
    map: generatedMap || sourceMap,
    warnings: [],
    errors: [],
  };
};

sourceMapAwareMinify.supportsWorkerThreads = () => true;

new CssMinimizerPlugin({
  minify: sourceMapAwareMinify,
  minimizerOptions: {
    generateSourceMap: true,
    preserveOriginalNames: false,
  },
});

Types

interface Input {
  [file: string]: string;
}

interface MinimizedResult {
  code: string;
  map?: RawSourceMap | undefined;
  errors?: (string | Error | ErrorObject)[] | undefined;
  warnings?: (Warning | WarningObject)[] | undefined;
}

type RawSourceMap = import("@jridgewell/trace-mapping").EncodedSourceMap;

type Warning = (Error & {
  plugin?: string;
  text?: string;
  source?: string;
}) | string;

interface WarningObject {
  message: string;
  plugin?: string | undefined;
  text?: string | undefined;
  line?: number | undefined;
  column?: number | undefined;
}

interface ErrorObject {
  message: string;
  line?: number | undefined;
  column?: number | undefined;
  stack?: string | undefined;
}

type CustomOptions = { [key: string]: any };