You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
374 lines
10 KiB
374 lines
10 KiB
/* |
|
MIT License http://www.opensource.org/licenses/mit-license.php |
|
Author Tobias Koppers @sokra |
|
*/ |
|
|
|
"use strict"; |
|
|
|
const Dependency = require("../Dependency"); |
|
const { UsageState } = require("../ExportsInfo"); |
|
const Template = require("../Template"); |
|
const { equals } = require("../util/ArrayHelpers"); |
|
const makeSerializable = require("../util/makeSerializable"); |
|
const propertyAccess = require("../util/propertyAccess"); |
|
const { handleDependencyBase } = require("./CommonJsDependencyHelpers"); |
|
const ModuleDependency = require("./ModuleDependency"); |
|
const processExportInfo = require("./processExportInfo"); |
|
|
|
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ |
|
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */ |
|
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */ |
|
/** @typedef {import("../Dependency").TRANSITIVE} TRANSITIVE */ |
|
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */ |
|
/** @typedef {import("../Module")} Module */ |
|
/** @typedef {import("../ModuleGraph")} ModuleGraph */ |
|
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */ |
|
|
|
const idsSymbol = Symbol("CommonJsExportRequireDependency.ids"); |
|
|
|
const EMPTY_OBJECT = {}; |
|
|
|
class CommonJsExportRequireDependency extends ModuleDependency { |
|
constructor(range, valueRange, base, names, request, ids, resultUsed) { |
|
super(request); |
|
this.range = range; |
|
this.valueRange = valueRange; |
|
this.base = base; |
|
this.names = names; |
|
this.ids = ids; |
|
this.resultUsed = resultUsed; |
|
this.asiSafe = undefined; |
|
} |
|
|
|
get type() { |
|
return "cjs export require"; |
|
} |
|
|
|
/** |
|
* @returns {boolean | TRANSITIVE} true, when changes to the referenced module could affect the referencing module; TRANSITIVE, when changes to the referenced module could affect referencing modules of the referencing module |
|
*/ |
|
couldAffectReferencingModule() { |
|
return Dependency.TRANSITIVE; |
|
} |
|
|
|
/** |
|
* @param {ModuleGraph} moduleGraph the module graph |
|
* @returns {string[]} the imported id |
|
*/ |
|
getIds(moduleGraph) { |
|
return moduleGraph.getMeta(this)[idsSymbol] || this.ids; |
|
} |
|
|
|
/** |
|
* @param {ModuleGraph} moduleGraph the module graph |
|
* @param {string[]} ids the imported ids |
|
* @returns {void} |
|
*/ |
|
setIds(moduleGraph, ids) { |
|
moduleGraph.getMeta(this)[idsSymbol] = ids; |
|
} |
|
|
|
/** |
|
* Returns list of exports referenced by this dependency |
|
* @param {ModuleGraph} moduleGraph module graph |
|
* @param {RuntimeSpec} runtime the runtime for which the module is analysed |
|
* @returns {(string[] | ReferencedExport)[]} referenced exports |
|
*/ |
|
getReferencedExports(moduleGraph, runtime) { |
|
const ids = this.getIds(moduleGraph); |
|
const getFullResult = () => { |
|
if (ids.length === 0) { |
|
return Dependency.EXPORTS_OBJECT_REFERENCED; |
|
} else { |
|
return [ |
|
{ |
|
name: ids, |
|
canMangle: false |
|
} |
|
]; |
|
} |
|
}; |
|
if (this.resultUsed) return getFullResult(); |
|
let exportsInfo = moduleGraph.getExportsInfo( |
|
moduleGraph.getParentModule(this) |
|
); |
|
for (const name of this.names) { |
|
const exportInfo = exportsInfo.getReadOnlyExportInfo(name); |
|
const used = exportInfo.getUsed(runtime); |
|
if (used === UsageState.Unused) return Dependency.NO_EXPORTS_REFERENCED; |
|
if (used !== UsageState.OnlyPropertiesUsed) return getFullResult(); |
|
exportsInfo = exportInfo.exportsInfo; |
|
if (!exportsInfo) return getFullResult(); |
|
} |
|
if (exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused) { |
|
return getFullResult(); |
|
} |
|
/** @type {string[][]} */ |
|
const referencedExports = []; |
|
for (const exportInfo of exportsInfo.orderedExports) { |
|
processExportInfo( |
|
runtime, |
|
referencedExports, |
|
ids.concat(exportInfo.name), |
|
exportInfo, |
|
false |
|
); |
|
} |
|
return referencedExports.map(name => ({ |
|
name, |
|
canMangle: false |
|
})); |
|
} |
|
|
|
/** |
|
* Returns the exported names |
|
* @param {ModuleGraph} moduleGraph module graph |
|
* @returns {ExportsSpec | undefined} export names |
|
*/ |
|
getExports(moduleGraph) { |
|
const ids = this.getIds(moduleGraph); |
|
if (this.names.length === 1) { |
|
const name = this.names[0]; |
|
const from = moduleGraph.getConnection(this); |
|
if (!from) return; |
|
return { |
|
exports: [ |
|
{ |
|
name, |
|
from, |
|
export: ids.length === 0 ? null : ids, |
|
// we can't mangle names that are in an empty object |
|
// because one could access the prototype property |
|
// when export isn't set yet |
|
canMangle: !(name in EMPTY_OBJECT) && false |
|
} |
|
], |
|
dependencies: [from.module] |
|
}; |
|
} else if (this.names.length > 0) { |
|
const name = this.names[0]; |
|
return { |
|
exports: [ |
|
{ |
|
name, |
|
// we can't mangle names that are in an empty object |
|
// because one could access the prototype property |
|
// when export isn't set yet |
|
canMangle: !(name in EMPTY_OBJECT) && false |
|
} |
|
], |
|
dependencies: undefined |
|
}; |
|
} else { |
|
const from = moduleGraph.getConnection(this); |
|
if (!from) return; |
|
const reexportInfo = this.getStarReexports( |
|
moduleGraph, |
|
undefined, |
|
from.module |
|
); |
|
if (reexportInfo) { |
|
return { |
|
exports: Array.from(reexportInfo.exports, name => { |
|
return { |
|
name, |
|
from, |
|
export: ids.concat(name), |
|
canMangle: !(name in EMPTY_OBJECT) && false |
|
}; |
|
}), |
|
// TODO handle deep reexports |
|
dependencies: [from.module] |
|
}; |
|
} else { |
|
return { |
|
exports: true, |
|
from: ids.length === 0 ? from : undefined, |
|
canMangle: false, |
|
dependencies: [from.module] |
|
}; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* @param {ModuleGraph} moduleGraph the module graph |
|
* @param {RuntimeSpec} runtime the runtime |
|
* @param {Module} importedModule the imported module (optional) |
|
* @returns {{exports?: Set<string>, checked?: Set<string>}} information |
|
*/ |
|
getStarReexports( |
|
moduleGraph, |
|
runtime, |
|
importedModule = moduleGraph.getModule(this) |
|
) { |
|
let importedExportsInfo = moduleGraph.getExportsInfo(importedModule); |
|
const ids = this.getIds(moduleGraph); |
|
if (ids.length > 0) |
|
importedExportsInfo = importedExportsInfo.getNestedExportsInfo(ids); |
|
let exportsInfo = moduleGraph.getExportsInfo( |
|
moduleGraph.getParentModule(this) |
|
); |
|
if (this.names.length > 0) |
|
exportsInfo = exportsInfo.getNestedExportsInfo(this.names); |
|
|
|
const noExtraExports = |
|
importedExportsInfo && |
|
importedExportsInfo.otherExportsInfo.provided === false; |
|
const noExtraImports = |
|
exportsInfo && |
|
exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused; |
|
|
|
if (!noExtraExports && !noExtraImports) { |
|
return; |
|
} |
|
|
|
const isNamespaceImport = |
|
importedModule.getExportsType(moduleGraph, false) === "namespace"; |
|
|
|
/** @type {Set<string>} */ |
|
const exports = new Set(); |
|
/** @type {Set<string>} */ |
|
const checked = new Set(); |
|
|
|
if (noExtraImports) { |
|
for (const exportInfo of exportsInfo.orderedExports) { |
|
const name = exportInfo.name; |
|
if (exportInfo.getUsed(runtime) === UsageState.Unused) continue; |
|
if (name === "__esModule" && isNamespaceImport) { |
|
exports.add(name); |
|
} else if (importedExportsInfo) { |
|
const importedExportInfo = |
|
importedExportsInfo.getReadOnlyExportInfo(name); |
|
if (importedExportInfo.provided === false) continue; |
|
exports.add(name); |
|
if (importedExportInfo.provided === true) continue; |
|
checked.add(name); |
|
} else { |
|
exports.add(name); |
|
checked.add(name); |
|
} |
|
} |
|
} else if (noExtraExports) { |
|
for (const importedExportInfo of importedExportsInfo.orderedExports) { |
|
const name = importedExportInfo.name; |
|
if (importedExportInfo.provided === false) continue; |
|
if (exportsInfo) { |
|
const exportInfo = exportsInfo.getReadOnlyExportInfo(name); |
|
if (exportInfo.getUsed(runtime) === UsageState.Unused) continue; |
|
} |
|
exports.add(name); |
|
if (importedExportInfo.provided === true) continue; |
|
checked.add(name); |
|
} |
|
if (isNamespaceImport) { |
|
exports.add("__esModule"); |
|
checked.delete("__esModule"); |
|
} |
|
} |
|
|
|
return { exports, checked }; |
|
} |
|
|
|
serialize(context) { |
|
const { write } = context; |
|
write(this.asiSafe); |
|
write(this.range); |
|
write(this.valueRange); |
|
write(this.base); |
|
write(this.names); |
|
write(this.ids); |
|
write(this.resultUsed); |
|
super.serialize(context); |
|
} |
|
|
|
deserialize(context) { |
|
const { read } = context; |
|
this.asiSafe = read(); |
|
this.range = read(); |
|
this.valueRange = read(); |
|
this.base = read(); |
|
this.names = read(); |
|
this.ids = read(); |
|
this.resultUsed = read(); |
|
super.deserialize(context); |
|
} |
|
} |
|
|
|
makeSerializable( |
|
CommonJsExportRequireDependency, |
|
"webpack/lib/dependencies/CommonJsExportRequireDependency" |
|
); |
|
|
|
CommonJsExportRequireDependency.Template = class CommonJsExportRequireDependencyTemplate extends ( |
|
ModuleDependency.Template |
|
) { |
|
/** |
|
* @param {Dependency} dependency the dependency for which the template should be applied |
|
* @param {ReplaceSource} source the current replace source which can be modified |
|
* @param {DependencyTemplateContext} templateContext the context object |
|
* @returns {void} |
|
*/ |
|
apply( |
|
dependency, |
|
source, |
|
{ |
|
module, |
|
runtimeTemplate, |
|
chunkGraph, |
|
moduleGraph, |
|
runtimeRequirements, |
|
runtime |
|
} |
|
) { |
|
const dep = /** @type {CommonJsExportRequireDependency} */ (dependency); |
|
const used = moduleGraph |
|
.getExportsInfo(module) |
|
.getUsedName(dep.names, runtime); |
|
|
|
const [type, base] = handleDependencyBase( |
|
dep.base, |
|
module, |
|
runtimeRequirements |
|
); |
|
|
|
const importedModule = moduleGraph.getModule(dep); |
|
let requireExpr = runtimeTemplate.moduleExports({ |
|
module: importedModule, |
|
chunkGraph, |
|
request: dep.request, |
|
weak: dep.weak, |
|
runtimeRequirements |
|
}); |
|
if (importedModule) { |
|
const ids = dep.getIds(moduleGraph); |
|
const usedImported = moduleGraph |
|
.getExportsInfo(importedModule) |
|
.getUsedName(ids, runtime); |
|
if (usedImported) { |
|
const comment = equals(usedImported, ids) |
|
? "" |
|
: Template.toNormalComment(propertyAccess(ids)) + " "; |
|
requireExpr += `${comment}${propertyAccess(usedImported)}`; |
|
} |
|
} |
|
|
|
switch (type) { |
|
case "expression": |
|
source.replace( |
|
dep.range[0], |
|
dep.range[1] - 1, |
|
used |
|
? `${base}${propertyAccess(used)} = ${requireExpr}` |
|
: `/* unused reexport */ ${requireExpr}` |
|
); |
|
return; |
|
case "Object.defineProperty": |
|
throw new Error("TODO"); |
|
default: |
|
throw new Error("Unexpected type"); |
|
} |
|
} |
|
}; |
|
|
|
module.exports = CommonJsExportRequireDependency;
|
|
|