Support for custom minification functions and advanced integration patterns. Create your own CSS minification implementations or extend existing ones with custom processing logic.
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;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 };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',
},
});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 },
],
});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,
},
],
});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,
},
});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,
},
});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 };