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

/*
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;