bootstarted / css-split-webpack-plugin

Split your CSS for stupid browsers using webpack and postcss.
78 stars 22 forks source link

compiler.plugin is not a function,in wepack5 #32

Open arieltlm opened 3 years ago

arieltlm commented 3 years ago

compiler.plugin is not a function, compiler.plugin api was removed in webpack@5

finalcat commented 3 years ago
import CSSSplitWebpackPlugin from 'css-split-webpack-plugin';

import { RawSource } from 'webpack-sources';

const isCSS = name => (/\.css$/).test(name);

const strip = str => str.replace(/\/$/, '');

export default class CSSSplitWebpackPluginFix extends CSSSplitWebpackPlugin {

    constructor(options) {
        super(options);
    }

    apply = compiler => {
        if (this.options.defer) {
            compiler.hooks.emit.tapAsync('CSSSplitWebpackPlugin', (compilation, callback) => {
                return this.chunksMapping(compilation, compilation.chunks, callback);
            });
        } else {
            compiler.hooks.thisCompilation.tap('CSSSplitWebpackPlugin', compilation => {
                compilation.hooks.optimizeChunkAssets.tapAsync(
                    'CSSSplitWebpackPlugin',
                    (chunks, callback) => this.chunksMapping(compilation, chunks, callback)
                );
            });
        }
    }

    chunksMapping(compilation, chunks, done) {
        const { assets } = compilation;
        const publicPath = strip(compilation.options.output.publicPath || './');
        const promises = chunks.map(chunk => {
            const input = chunk.files.filter(isCSS);
            const items = input.map(name => this.file(name, assets[name]));

            return Promise.all(items).then(entries => {
                entries.forEach(entry => {
                    if (entry.chunks.length === 1) {
                        return;
                    }
                    // Inject the new files into the chunk.
                    entry.chunks.forEach(file => {
                        assets[file.name] = file;
                        chunk.files.add(file.name);
                    });
                    const content = entry.chunks.map(file => {
                        return `@import "${publicPath}/${file._name}";`;
                    }).join('\n');
                    const imports = this.options.imports({
                        ...entry,
                        content,
                    });

                    if (!this.options.preserve) {
                        chunk.files.delete(entry.file);
                        delete assets[entry.file];
                    }
                    if (imports) {
                        assets[imports] = new RawSource(content);
                        chunk.files.add(imports);
                    }
                });
                return Promise.resolve();
            });
        });

        Promise.all(promises).then(() => {
            done();
        }, done);
    }
}
aleen42 commented 3 years ago

It is unnecessary to implement chunksMapping again, and here is my workaround:

const name = 'CSSSplitWebpackPluginFix';
const CSSSplitWebpackPlugin = require('css-split-webpack-plugin').default;

module.exports = class CSSSplitWebpackPluginFix extends CSSSplitWebpackPlugin {

    apply(compiler) {
        return super.apply(this.patchCompiler(compiler));
    }

    chunksMapping(compilation, chunks, done) {
        return super.chunksMapping(compilation, this.patchChunks(chunks), done);
    }

    patchCompiler(compiler) {
        // break 1: since that `compiler.plugin` has been removed
        compiler.plugin = (hookName, hooker) => {
            ({
                'emit'             : () => compiler.hooks['emit'].tapAsync(name, hooker),
                'this-compilation' : () => compiler.hooks['thisCompilation'].tap(name, compilation =>
                    hooker(this.patchCompilation(compilation))),
            })[hookName]();
        };
        return compiler;
    }

    patchCompilation(compilation) {
        // break 2: since that `compilation.plugin` has been removed
        compilation.plugin = (hookName, hooker) => {
            ({
                'optimize-chunk-assets' : () => compilation.hooks['processAssets'].tapAsync({
                    name, stage : compilation.constructor['PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT'],
                }, (assets, done) => hooker(compilation['chunks'], done)),
            })[hookName]();
        };
        return compilation;
    }

    patchChunks(chunks) {
        // break 3: since that `chunk.files` has been changed from Array to Set
        chunks.forEach(chunk => { chunk.files = Array.from(chunk.files) });
        return chunks;
    }
};