Skip to content

Commit

Permalink
Merge pull request #7585 from webpack/feature/hook-into-get-reference
Browse files Browse the repository at this point in the history
hook into dependency reference
  • Loading branch information
sokra committed Jun 29, 2018
2 parents 86370e9 + 7beac3c commit 5a185c9
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 44 deletions.
27 changes: 26 additions & 1 deletion lib/Compilation.js
Expand Up @@ -37,6 +37,8 @@ const SortableSet = require("./util/SortableSet");
const GraphHelpers = require("./GraphHelpers");

/** @typedef {import("./Module")} Module */
/** @typedef {import("./dependencies/DependencyReference")} DependencyReference */
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("webpack-sources").Source} Source */
Expand Down Expand Up @@ -109,6 +111,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"]),

Expand Down Expand Up @@ -1118,6 +1127,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
* @private
Expand All @@ -1143,7 +1165,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;
}
Expand All @@ -1165,6 +1187,8 @@ class Compilation extends Tapable {
blockQueue.push(b);
};

/** @type {Module} */
let currentModule;
/** @type {DependenciesBlock} */
let block;
/** @type {TODO} */
Expand All @@ -1176,6 +1200,7 @@ class Compilation extends Tapable {

for (const module of this.modules) {
blockQueue = [module];
currentModule = module;
while (blockQueue.length > 0) {
block = blockQueue.pop();
blockInfoModules = new Set();
Expand Down
31 changes: 18 additions & 13 deletions lib/FlagDependencyUsagePlugin.js
Expand Up @@ -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);
Expand Down Expand Up @@ -54,44 +59,44 @@ 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);
}
};

for (const module of modules) {
module.used = false;
}

/** @type {[Module, DependenciesBlock, UsedExports][]} */
const queue = [];
for (const preparedEntrypoint of compilation._preparedEntrypoints) {
if (preparedEntrypoint.module) {
Expand All @@ -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]);
}
}
);
Expand Down
4 changes: 4 additions & 0 deletions lib/RuntimeTemplate.js
Expand Up @@ -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)}]`;
Expand Down
4 changes: 4 additions & 0 deletions lib/dependencies/DependencyReference.js
Expand Up @@ -7,6 +7,9 @@
/** @typedef {import("../Module")} Module */

class DependencyReference {
// TODO webpack 5: module must be dynamic, you must pass a function returning a module
// This is needed to remove the hack in ConcatenatedModule
// The problem is that the `module` in Dependency could be replaced i. e. because of Scope Hoisting
/**
*
* @param {Module} module the referenced module
Expand All @@ -15,6 +18,7 @@ class DependencyReference {
* @param {number} order the order information or NaN if don't care
*/
constructor(module, importedNames, weak = false, order = NaN) {
// TODO webpack 5: make it a getter
this.module = module;
// true: full object
// false: only sideeffects/no export
Expand Down
23 changes: 16 additions & 7 deletions lib/dependencies/HarmonyExportImportedSpecifierDependency.js
Expand Up @@ -602,10 +602,10 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS

getReexportStatement(module, key, name, valueKey) {
const exportsName = module.exportsArgument;
const returnValue = this.getReturnValue(valueKey);
const returnValue = this.getReturnValue(name, valueKey);
return `__webpack_require__.d(${exportsName}, ${JSON.stringify(
key
)}, function() { return ${name}${returnValue}; });\n`;
)}, function() { return ${returnValue}; });\n`;
}

getReexportFakeNamespaceObjectStatement(module, key, name) {
Expand All @@ -616,20 +616,29 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
}

getConditionalReexportStatement(module, key, name, valueKey) {
if (valueKey === false) {
return "/* unused export */\n";
}
const exportsName = module.exportsArgument;
const returnValue = this.getReturnValue(valueKey);
const returnValue = this.getReturnValue(name, valueKey);
return `if(__webpack_require__.o(${name}, ${JSON.stringify(
valueKey
)})) __webpack_require__.d(${exportsName}, ${JSON.stringify(
key
)}, function() { return ${name}${returnValue}; });\n`;
)}, function() { return ${returnValue}; });\n`;
}

getReturnValue(valueKey) {
getReturnValue(name, valueKey) {
if (valueKey === null) {
return "_default.a";
return `${name}_default.a`;
}
if (valueKey === "") {
return name;
}
if (valueKey === false) {
return "/* unused export */ undefined";
}

return valueKey && "[" + JSON.stringify(valueKey) + "]";
return `${name}[${JSON.stringify(valueKey)}]`;
}
};
31 changes: 23 additions & 8 deletions lib/optimize/ConcatenatedModule.js
Expand Up @@ -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,
Expand Down Expand Up @@ -125,6 +132,8 @@ const getFinalName = (
requestShortener,
strictHarmonyModule
);
} else if (!info.module.isUsed(exportName)) {
return "/* unused export */ undefined";
}
const name = info.internalNames.get(directExport);
if (!name) {
Expand Down Expand Up @@ -275,7 +284,7 @@ const getPathInAst = (ast, node) => {
};

class ConcatenatedModule extends Module {
constructor(rootModule, modules) {
constructor(rootModule, modules, concatenationList) {
super("javascript/esm", null);
super.setChunks(rootModule._chunks);

Expand Down Expand Up @@ -320,10 +329,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;
Expand Down Expand Up @@ -410,7 +418,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();

Expand All @@ -424,15 +438,16 @@ 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;
})
.filter(ref => ref);
DependencyReference.sort(references);
// 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;
});
};

Expand Down

0 comments on commit 5a185c9

Please sign in to comment.