UNPKG

@regru/webpack-babel-multi-target-plugin

Version:

A Webpack plugin that works with Babel to allow deployment of ES2015 builds targeted to modern browsers, with an ES5 fallback for legacy browsers.

140 lines 6.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const babel_target_1 = require("./babel-target"); const plugin_name_1 = require("./plugin.name"); // While CSS modules aren't duplicated by targeting the way code modules are, since they are referenced by targeted // modules, they end up getting duplicated. Without intervention, we'd end up with one CSS file per target, which each // file containing the exact same content. To fix this, we remove CSS modules from the targeted and move them into their // own (non-targeted) chunks. /** * @internalapi */ class NormalizeCssChunksPlugin { constructor(targets) { this.targets = targets; } apply(compiler) { compiler.hooks.compilation.tap(plugin_name_1.PLUGIN_NAME, (compilation) => { if (compilation.name) { return; } compilation.hooks.optimizeChunksBasic.tap(plugin_name_1.PLUGIN_NAME, this.extractCssChunks.bind(this, compilation)); compilation.hooks.optimizeChunkAssets.tap(plugin_name_1.PLUGIN_NAME, this.cleanCssChunks.bind(this, compilation)); }); } extractCssChunks(compilation, chunks) { const cssModules = {}; let hasUntaggedTarget = false; // first, find the CSS modules and remove them from their targeted chunks chunks.forEach(chunk => { // if `isGeneratedForBabelTargets` is present, we've already processed this chunk // the `optimizeChunksBasic` hook can get called more than once if (chunk.isGeneratedForBabelTargets) { return; } const target = babel_target_1.BabelTarget.findTarget(chunk); if (!target) { // can probably skip these? maybe? } // don't mess with a chunk if it's not tagged with the target key if (target && !target.tagAssetsWithKey) { hasUntaggedTarget = true; return; } // get the original (untagged) name of the entry module so we can correctly // attribute any contained CSS modules to the entry const name = this.findEntryName(chunk); // track the original entry names to use later if (!cssModules[name]) { cssModules[name] = new Set(); } chunk.modulesIterable.forEach(module => { if (module.constructor.name !== 'CssModule') { return; } chunk.removeModule(module); // don't duplicate modules - we should only have one per imported/required CSS/SCSS/etc file cssModules[name].add(module); }); }); if (hasUntaggedTarget) { // untagged targets keep their CSS modules, so we don't need to create a fake one below return; } // create chunks for the extracted modules Object.keys(cssModules).forEach(name => { const modules = cssModules[name]; const cssGroup = compilation.addChunkInGroup(name); const cssChunk = cssGroup.chunks[cssGroup.chunks.length - 1]; // HACK ALERT! fool HtmlWebpackPlugin into thinking this is an actual Entrypoint chunk so it // will include its assets by default (assuming the user hasn't filtered the name of the chunk) // somewhat relevant: BabelMultiTargetHtmlUpdater.mapChunkNames cssGroup.isInitial = () => true; cssChunk.hasRuntime = () => false; cssChunk.isInitial = () => true; cssChunk.isGeneratedForBabelTargets = true; modules.forEach(module => cssChunk.addModule(module)); }); } findEntryName(chunk) { const entry = this.findEntryModule(chunk); if (entry) { return entry.reasons[0].dependency.originalName; } throw new Error(`Could not determine entry module for chunk ${chunk.name}`); } findEntryModule(chunk) { if (chunk.entryModule) { return chunk.entryModule; } // sure, fine, make me work for it... for (const group of chunk.groupsIterable) { for (const groupParent of group.parentsIterable) { for (const chunk of groupParent.chunks) { if (chunk.hasEntryModule()) { return chunk.entryModule; } } } } // sure, fine, make me REALLY work for it... for (const module of chunk.modulesIterable) { const entry = this.getEntryFromModule(module); if (entry) { return entry; } } } getEntryFromModule(module) { for (const reason of module.reasons) { const babelTarget = reason.dependency.babelTarget || babel_target_1.BabelTarget.getTargetFromTag(reason.dependency.request, this.targets); if (babelTarget) { return module; } const depEntry = this.getEntryFromModule(reason.dependency.originModule || reason.dependency.module); if (depEntry) { return depEntry; } } } // The extract process in extractCssChunks causes a small JavaScript loader file to get generated. Since the file // gets loaded by HtmlWebpackPlugin, we don't want this file cluttering up the assets, so it gets removed. cleanCssChunks(compilation, chunks) { chunks.forEach(chunk => { if (!chunk.isGeneratedForBabelTargets) { return; } chunk.files = chunk.files.reduce((result, file) => { if (file.endsWith('.js')) { delete compilation.assets[file]; } else { result.push(file); } return result; }, []); }); } } exports.NormalizeCssChunksPlugin = NormalizeCssChunksPlugin; //# sourceMappingURL=normalize.css.chunks.plugin.js.map