| /* |
| MIT License http://www.opensource.org/licenses/mit-license.php |
| Author Tobias Koppers @sokra |
| */ |
| "use strict"; |
| |
| const mm = require("micromatch"); |
| const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency"); |
| const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency"); |
| const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency"); |
| |
| /** @typedef {import("../Module")} Module */ |
| /** @typedef {import("../Dependency")} Dependency */ |
| |
| /** |
| * @typedef {Object} ExportInModule |
| * @property {Module} module the module |
| * @property {string} exportName the name of the export |
| */ |
| |
| class SideEffectsFlagPlugin { |
| apply(compiler) { |
| compiler.hooks.normalModuleFactory.tap("SideEffectsFlagPlugin", nmf => { |
| nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => { |
| const resolveData = data.resourceResolveData; |
| if ( |
| resolveData && |
| resolveData.descriptionFileData && |
| resolveData.relativePath |
| ) { |
| const sideEffects = resolveData.descriptionFileData.sideEffects; |
| const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects( |
| resolveData.relativePath, |
| sideEffects |
| ); |
| if (!hasSideEffects) { |
| module.factoryMeta.sideEffectFree = true; |
| } |
| } |
| |
| return module; |
| }); |
| nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => { |
| if (data.settings.sideEffects === false) { |
| module.factoryMeta.sideEffectFree = true; |
| } else if (data.settings.sideEffects === true) { |
| module.factoryMeta.sideEffectFree = false; |
| } |
| }); |
| }); |
| compiler.hooks.compilation.tap("SideEffectsFlagPlugin", compilation => { |
| compilation.hooks.optimizeDependencies.tap( |
| "SideEffectsFlagPlugin", |
| modules => { |
| /** @type {Map<Module, Map<string, ExportInModule>>} */ |
| const reexportMaps = new Map(); |
| |
| // Capture reexports of sideEffectFree modules |
| for (const module of modules) { |
| /** @type {Dependency[]} */ |
| const removeDependencies = []; |
| for (const dep of module.dependencies) { |
| if (dep instanceof HarmonyImportSideEffectDependency) { |
| if (dep.module && dep.module.factoryMeta.sideEffectFree) { |
| removeDependencies.push(dep); |
| } |
| } else if ( |
| dep instanceof HarmonyExportImportedSpecifierDependency |
| ) { |
| if (module.factoryMeta.sideEffectFree) { |
| const mode = dep.getMode(true); |
| if (mode.type === "safe-reexport") { |
| let map = reexportMaps.get(module); |
| if (!map) { |
| reexportMaps.set(module, (map = new Map())); |
| } |
| for (const pair of mode.map) { |
| map.set(pair[0], { |
| module: mode.module, |
| exportName: pair[1] |
| }); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Flatten reexports |
| for (const map of reexportMaps.values()) { |
| for (const pair of map) { |
| let mapping = pair[1]; |
| while (mapping) { |
| const innerMap = reexportMaps.get(mapping.module); |
| if (!innerMap) break; |
| const newMapping = innerMap.get(mapping.exportName); |
| if (newMapping) { |
| map.set(pair[0], newMapping); |
| } |
| mapping = newMapping; |
| } |
| } |
| } |
| |
| // Update imports along the reexports from sideEffectFree modules |
| for (const pair of reexportMaps) { |
| const module = pair[0]; |
| const map = pair[1]; |
| let newReasons = undefined; |
| for (let i = 0; i < module.reasons.length; i++) { |
| const reason = module.reasons[i]; |
| const dep = reason.dependency; |
| if ( |
| dep instanceof HarmonyExportImportedSpecifierDependency || |
| (dep instanceof HarmonyImportSpecifierDependency && |
| !dep.namespaceObjectAsContext) |
| ) { |
| const mapping = map.get(dep.id); |
| if (mapping) { |
| dep.redirectedModule = mapping.module; |
| dep.redirectedId = mapping.exportName; |
| mapping.module.addReason( |
| reason.module, |
| dep, |
| reason.explanation |
| ? reason.explanation + |
| " (skipped side-effect-free modules)" |
| : "(skipped side-effect-free modules)" |
| ); |
| // removing the currect reason, by not adding it to the newReasons array |
| // lazily create the newReasons array |
| if (newReasons === undefined) { |
| newReasons = i === 0 ? [] : module.reasons.slice(0, i); |
| } |
| continue; |
| } |
| } |
| if (newReasons !== undefined) newReasons.push(reason); |
| } |
| if (newReasons !== undefined) { |
| module.reasons = newReasons; |
| } |
| } |
| } |
| ); |
| }); |
| } |
| |
| static moduleHasSideEffects(moduleName, flagValue) { |
| switch (typeof flagValue) { |
| case "undefined": |
| return true; |
| case "boolean": |
| return flagValue; |
| case "string": |
| if (process.platform === "win32") { |
| flagValue = flagValue.replace(/\\/g, "/"); |
| } |
| return mm.isMatch(moduleName, flagValue, { |
| matchBase: true |
| }); |
| case "object": |
| return flagValue.some(glob => |
| SideEffectsFlagPlugin.moduleHasSideEffects(moduleName, glob) |
| ); |
| } |
| } |
| } |
| module.exports = SideEffectsFlagPlugin; |