add Compilation.getDependencyReference and hooks to override it

expose DependencyReference class
This commit is contained in:
Tobias Koppers 2018-06-08 10:34:38 +02:00
parent e1f0a66fd9
commit 50fe2e79ee
11 changed files with 159 additions and 37 deletions

View File

@ -36,6 +36,10 @@ const Queue = require("./util/Queue");
const SortableSet = require("./util/SortableSet");
const GraphHelpers = require("./GraphHelpers");
/** @typedef {import("./Module")} Module */
/** @typedef {import("./dependencies/DependencyReference")} DependencyReference */
/** @typedef {import("./Dependency")} Dependency */
const byId = (a, b) => {
if (a.id < b.id) return -1;
if (a.id > b.id) return 1;
@ -104,6 +108,13 @@ class Compilation extends Tapable {
failedModule: new SyncHook(["module", "error"]),
succeedModule: new SyncHook(["module"]),
/** @type {SyncWaterfallHook<DependencyReference, Dependency, Module>} */
dependencyReference: new SyncWaterfallHook([
"dependencyReference",
"dependency",
"module"
]),
finishModules: new SyncHook(["modules"]),
finishRebuildingModule: new SyncHook(["module"]),
@ -1175,6 +1186,19 @@ class Compilation extends Tapable {
}
}
/**
* @param {Module} module the module containing the dependency
* @param {Dependency} dependency the dependency
* @returns {DependencyReference} a reference for the dependency
*/
getDependencyReference(module, dependency) {
// TODO remove dep.getReference existance check in webpack 5
if (typeof dependency.getReference !== "function") return null;
const ref = dependency.getReference();
if (!ref) return null;
return this.hooks.dependencyReference.call(ref, dependency, module);
}
// This method creates the Chunk graph from the Module graph
processDependenciesBlocksForChunkGroups(inputChunkGroups) {
// Process is splitting into two parts:
@ -1193,7 +1217,7 @@ class Compilation extends Tapable {
const iteratorDependency = d => {
// We skip Dependencies without Reference
const ref = d.getReference();
const ref = this.getDependencyReference(currentModule, d);
if (!ref) {
return;
}
@ -1215,9 +1239,12 @@ class Compilation extends Tapable {
blockQueue.push(b);
};
/** @type {Module} */
let currentModule;
let block, blockQueue, blockInfoModules, blockInfoBlocks;
for (const module of this.modules) {
blockQueue = [module];
currentModule = module;
while (blockQueue.length > 0) {
block = blockQueue.pop();
blockInfoModules = new Set();

View File

@ -4,6 +4,11 @@
*/
"use strict";
/** @typedef {import("./Module")} Module */
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
/** @typedef {false | true | string[]} UsedExports */
const addToSet = (a, b) => {
for (const item of b) {
if (!a.includes(item)) a.push(item);
@ -54,37 +59,36 @@ class FlagDependencyUsagePlugin {
return;
}
queue.push([module, module.usedExports]);
queue.push([module, module, module.usedExports]);
};
const processDependenciesBlock = (depBlock, usedExports) => {
const processDependenciesBlock = (module, depBlock, usedExports) => {
for (const dep of depBlock.dependencies) {
processDependency(dep);
processDependency(module, dep);
}
for (const variable of depBlock.variables) {
for (const dep of variable.dependencies) {
processDependency(dep);
processDependency(module, dep);
}
}
for (const block of depBlock.blocks) {
queue.push([block, usedExports]);
queue.push([module, block, usedExports]);
}
};
const processDependency = dep => {
// TODO remove dep.getReference existance check in webpack 5
const reference = dep.getReference && dep.getReference();
const processDependency = (module, dep) => {
const reference = compilation.getDependencyReference(module, dep);
if (!reference) return;
const module = reference.module;
const referenceModule = reference.module;
const importedNames = reference.importedNames;
const oldUsed = module.used;
const oldUsedExports = module.usedExports;
const oldUsed = referenceModule.used;
const oldUsedExports = referenceModule.usedExports;
if (
!oldUsed ||
(importedNames &&
(!oldUsedExports || !isSubset(oldUsedExports, importedNames)))
) {
processModule(module, importedNames);
processModule(referenceModule, importedNames);
}
};
@ -92,6 +96,7 @@ class FlagDependencyUsagePlugin {
module.used = false;
}
/** @type {[Module, DependenciesBlock, UsedExports][]} */
const queue = [];
for (const preparedEntrypoint of compilation._preparedEntrypoints) {
if (preparedEntrypoint.module) {
@ -101,7 +106,7 @@ class FlagDependencyUsagePlugin {
while (queue.length) {
const queueItem = queue.pop();
processDependenciesBlock(queueItem[0], queueItem[1]);
processDependenciesBlock(queueItem[0], queueItem[1], queueItem[2]);
}
}
);

View File

@ -265,6 +265,10 @@ module.exports = class RuntimeTemplate {
if (exportName) {
const used = module.isUsed(exportName);
if (!used) {
const comment = Template.toNormalComment(`unused export ${exportName}`);
return `${comment} undefined`;
}
const comment =
used !== exportName ? Template.toNormalComment(exportName) + " " : "";
const access = `${importVar}[${comment}${JSON.stringify(used)}]`;

View File

@ -20,6 +20,13 @@ const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibi
const createHash = require("../util/createHash");
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Compilation")} Compilation */
/**
* @typedef {Object} ConcatenationEntry
* @property {"concatenated" | "external"} type
* @property {Module} module
*/
const ensureNsObjSource = (
info,
@ -275,7 +282,7 @@ const getPathInAst = (ast, node) => {
};
class ConcatenatedModule extends Module {
constructor(rootModule, modules) {
constructor(rootModule, modules, concatenationList) {
super("javascript/esm", null);
super.setChunks(rootModule._chunks);
@ -319,10 +326,9 @@ class ConcatenatedModule extends Module {
this.warnings = [];
this.errors = [];
this._orderedConcatenationList = this._createOrderedConcatenationList(
rootModule,
modulesSet
);
this._orderedConcatenationList =
concatenationList ||
ConcatenatedModule.createConcatenationList(rootModule, modulesSet, null);
for (const info of this._orderedConcatenationList) {
if (info.type === "concatenated") {
const m = info.module;
@ -409,7 +415,13 @@ class ConcatenatedModule extends Module {
}, 0);
}
_createOrderedConcatenationList(rootModule, modulesSet) {
/**
* @param {Module} rootModule the root of the concatenation
* @param {Set<Module>} modulesSet a set of modules which should be concatenated
* @param {Compilation} compilation the compilation context
* @returns {ConcatenationEntry[]} concatenation list
*/
static createConcatenationList(rootModule, modulesSet, compilation) {
const list = [];
const set = new Set();
@ -423,7 +435,7 @@ class ConcatenatedModule extends Module {
const references = module.dependencies
.filter(dep => dep instanceof HarmonyImportDependency)
.map(dep => {
const ref = dep.getReference();
const ref = compilation.getDependencyReference(module, dep);
if (ref) map.set(ref, dep);
return ref;
})
@ -432,7 +444,7 @@ class ConcatenatedModule extends Module {
// TODO webpack 5: remove this hack, see also DependencyReference
return references.map(ref => {
const dep = map.get(ref);
return () => dep.getReference().module;
return () => compilation.getDependencyReference(module, dep).module;
});
};

View File

@ -213,8 +213,9 @@ class ModuleConcatenationPlugin {
const failureCache = new Map();
// try to add all imports
for (const imp of this.getImports(currentRoot)) {
const problem = this.tryToAdd(
for (const imp of this._getImports(compilation, currentRoot)) {
const problem = this._tryToAdd(
compilation,
currentConfiguration,
imp,
possibleInners,
@ -245,9 +246,15 @@ class ModuleConcatenationPlugin {
for (const concatConfiguration of concatConfigurations) {
if (usedModules.has(concatConfiguration.rootModule)) continue;
const modules = concatConfiguration.getModules();
const rootModule = concatConfiguration.rootModule;
const newModule = new ConcatenatedModule(
concatConfiguration.rootModule,
modules
rootModule,
Array.from(modules),
ConcatenatedModule.createConcatenationList(
rootModule,
modules,
compilation
)
);
for (const warning of concatConfiguration.getWarningsSorted()) {
newModule.optimizationBailout.push(requestShortener => {
@ -320,15 +327,16 @@ class ModuleConcatenationPlugin {
);
}
getImports(module) {
_getImports(compilation, module) {
return new Set(
module.dependencies
// Get reference info only for harmony Dependencies
.map(
dep =>
dep instanceof HarmonyImportDependency ? dep.getReference() : null
)
.map(dep => {
if (!(dep instanceof HarmonyImportDependency)) return null;
if (!compilation) return dep.getReference();
return compilation.getDependencyReference(module, dep);
})
// Reference is valid and has a module
// Dependencies are simple enough to concat them
@ -345,7 +353,7 @@ class ModuleConcatenationPlugin {
);
}
tryToAdd(config, module, possibleModules, failureCache) {
_tryToAdd(compilation, config, module, possibleModules, failureCache) {
const cacheEntry = failureCache.get(module);
if (cacheEntry) {
return cacheEntry;
@ -383,7 +391,8 @@ class ModuleConcatenationPlugin {
)
continue;
const problem = this.tryToAdd(
const problem = this._tryToAdd(
compilation,
testConfig,
reason.module,
possibleModules,
@ -399,8 +408,14 @@ class ModuleConcatenationPlugin {
config.set(testConfig);
// Eagerly try to add imports too if possible
for (const imp of this.getImports(module)) {
const problem = this.tryToAdd(config, imp, possibleModules, failureCache);
for (const imp of this._getImports(compilation, module)) {
const problem = this._tryToAdd(
compilation,
config,
imp,
possibleModules,
failureCache
);
if (problem) {
config.addWarning(imp, problem);
}
@ -451,7 +466,7 @@ class ConcatConfiguration {
}
getModules() {
return this.modules.asArray();
return this.modules.asSet();
}
clone() {

View File

@ -25,7 +25,10 @@ class WasmFinalizeExportsPlugin {
for (const reason of module.reasons) {
// 2. is referenced by a non-WebAssembly module
if (reason.module.type.startsWith("webassembly") === false) {
const ref = reason.dependency.getReference();
const ref = compilation.getDependencyReference(
reason.module,
reason.dependency
);
const importedNames = ref.importedNames;

View File

@ -123,6 +123,9 @@ exportPlugins(exports, {
UmdMainTemplatePlugin: () => require("./UmdMainTemplatePlugin"),
WatchIgnorePlugin: () => require("./WatchIgnorePlugin")
});
exportPlugins((exports.dependencies = {}), {
DependencyReference: () => require("./dependencies/DependencyReference")
});
exportPlugins((exports.optimize = {}), {
AggressiveMergingPlugin: () => require("./optimize/AggressiveMergingPlugin"),
AggressiveSplittingPlugin: () =>

View File

@ -0,0 +1,8 @@
import { test } from "./module";
it("should run the test", () => {
expect(test()).toEqual({
used: "used",
unused: undefined
})
});

View File

@ -0,0 +1,8 @@
import { used, unused } from "./reference";
export function test() {
return {
used,
unused
};
}

View File

@ -0,0 +1,3 @@
export var used = "used";
export var unused = "unused";

View File

@ -0,0 +1,34 @@
const DependencyReference = require("../../../../").dependencies
.DependencyReference;
module.exports = {
optimization: {
usedExports: true,
concatenateModules: false
},
plugins: [
function() {
this.hooks.compilation.tap("Test", compilation => {
compilation.hooks.dependencyReference.tap(
"Test",
(ref, dep, module) => {
if (
module.identifier().endsWith("module.js") &&
ref.module &&
ref.module.identifier().endsWith("reference.js") &&
Array.isArray(ref.importedNames) &&
ref.importedNames.includes("unused")
) {
return new DependencyReference(
ref.module,
ref.importedNames.filter(item => item !== "unused"),
ref.weak,
ref.order
);
}
return ref;
}
);
});
}
]
};