mirror of https://github.com/webpack/webpack.git
track the target binding of harmony reexports
display target binding in module info header check conflicting star exports (fixes #7562) when statically known
This commit is contained in:
parent
377348be6c
commit
163e7674a0
|
|
@ -47,6 +47,7 @@
|
|||
* @typedef {Object} ExportSpec
|
||||
* @property {string} name the name of the export
|
||||
* @property {boolean=} canMangle can the export be renamed (defaults to true)
|
||||
* @property {boolean=} terminalBinding is the export a terminal binding that should be checked for export star conflicts
|
||||
* @property {(string | ExportSpec)[]=} exports nested exports
|
||||
* @property {Module=} from when reexported: from which module
|
||||
* @property {string[] | null=} export when reexported: from which export
|
||||
|
|
@ -57,6 +58,7 @@
|
|||
* @property {(string | ExportSpec)[] | true | null} exports exported names, true for unknown exports or null for no exports
|
||||
* @property {Set<string>=} excludeExports when exports = true, list of unaffected exports
|
||||
* @property {boolean=} canMangle can the export be renamed (defaults to true)
|
||||
* @property {boolean=} terminalBinding are the exports terminal bindings that should be checked for export star conflicts
|
||||
* @property {Module[]=} dependencies module on which the result depends on
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ const makeSerializable = require("./util/makeSerializable");
|
|||
const { forEachRuntime } = require("./util/runtime");
|
||||
|
||||
/** @typedef {import("./Dependency").RuntimeSpec} RuntimeSpec */
|
||||
/** @typedef {import("./Module")} Module */
|
||||
/** @typedef {import("./ModuleGraph")} ModuleGraph */
|
||||
/** @typedef {import("./util/Hash")} Hash */
|
||||
|
||||
/** @typedef {typeof UsageState.OnlyPropertiesUsed | typeof UsageState.NoInfo | typeof UsageState.Unknown | typeof UsageState.Used} RuntimeUsageStateType */
|
||||
|
|
@ -24,19 +26,27 @@ const UsageState = Object.freeze({
|
|||
});
|
||||
|
||||
class RestoreProvidedData {
|
||||
constructor(exports, otherProvided, otherCanMangleProvide) {
|
||||
constructor(
|
||||
exports,
|
||||
otherProvided,
|
||||
otherCanMangleProvide,
|
||||
otherTerminalBinding
|
||||
) {
|
||||
this.exports = exports;
|
||||
this.otherProvided = otherProvided;
|
||||
this.otherCanMangleProvide = otherCanMangleProvide;
|
||||
this.otherTerminalBinding = otherTerminalBinding;
|
||||
}
|
||||
|
||||
serialize({ write }) {
|
||||
write(this.exports);
|
||||
write(this.otherProvided), write(this.otherCanMangleProvide);
|
||||
write(this.otherProvided);
|
||||
write(this.otherCanMangleProvide);
|
||||
write(this.otherTerminalBinding);
|
||||
}
|
||||
|
||||
static deserialize({ read }) {
|
||||
return new RestoreProvidedData(read(), read(), read());
|
||||
return new RestoreProvidedData(read(), read(), read(), read());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,6 +234,17 @@ class ExportsInfo {
|
|||
return this._otherExportsInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} name export name
|
||||
* @returns {ExportInfo | undefined} export info for this name
|
||||
*/
|
||||
getReadOnlyExportInfoRecursive(name) {
|
||||
const exportInfo = this.getReadOnlyExportInfo(name[0]);
|
||||
if (name.length === 1) return exportInfo;
|
||||
if (!exportInfo.exportsInfo) return undefined;
|
||||
return exportInfo.exportsInfo.getReadOnlyExportInfoRecursive(name.slice(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]=} name the export name
|
||||
* @returns {ExportsInfo | undefined} the nested exports info
|
||||
|
|
@ -643,17 +664,20 @@ class ExportsInfo {
|
|||
getRestoreProvidedData() {
|
||||
const otherProvided = this._otherExportsInfo.provided;
|
||||
const otherCanMangleProvide = this._otherExportsInfo.canMangleProvide;
|
||||
const otherTerminalBinding = this._otherExportsInfo.terminalBinding;
|
||||
const exports = [];
|
||||
for (const exportInfo of this._exports.values()) {
|
||||
if (
|
||||
exportInfo.provided !== otherProvided ||
|
||||
exportInfo.canMangleProvide !== otherCanMangleProvide ||
|
||||
exportInfo.terminalBinding !== otherTerminalBinding ||
|
||||
exportInfo.exportsInfoOwned
|
||||
) {
|
||||
exports.push({
|
||||
name: exportInfo.name,
|
||||
provided: exportInfo.provided,
|
||||
canMangleProvide: exportInfo.canMangleProvide,
|
||||
terminalBinding: exportInfo.terminalBinding,
|
||||
exportsInfo: exportInfo.exportsInfoOwned
|
||||
? exportInfo.exportsInfo.getRestoreProvidedData()
|
||||
: undefined
|
||||
|
|
@ -663,21 +687,30 @@ class ExportsInfo {
|
|||
return new RestoreProvidedData(
|
||||
exports,
|
||||
otherProvided,
|
||||
otherCanMangleProvide
|
||||
otherCanMangleProvide,
|
||||
otherTerminalBinding
|
||||
);
|
||||
}
|
||||
|
||||
restoreProvided({ otherProvided, otherCanMangleProvide, exports }) {
|
||||
restoreProvided({
|
||||
otherProvided,
|
||||
otherCanMangleProvide,
|
||||
otherTerminalBinding,
|
||||
exports
|
||||
}) {
|
||||
for (const exportInfo of this._exports.values()) {
|
||||
exportInfo.provided = otherProvided;
|
||||
exportInfo.canMangleProvide = otherCanMangleProvide;
|
||||
exportInfo.terminalBinding = otherTerminalBinding;
|
||||
}
|
||||
this._otherExportsInfo.provided = otherProvided;
|
||||
this._otherExportsInfo.canMangleProvide = otherCanMangleProvide;
|
||||
this._otherExportsInfo.terminalBinding = otherTerminalBinding;
|
||||
for (const exp of exports) {
|
||||
const exportInfo = this.getExportInfo(exp.name);
|
||||
exportInfo.provided = exp.provided;
|
||||
exportInfo.canMangleProvide = exp.canMangleProvide;
|
||||
exportInfo.terminalBinding = exp.terminalBinding;
|
||||
if (exp.exportsInfo) {
|
||||
const exportsInfo = exportInfo.createNestedExportsInfo();
|
||||
exportsInfo.restoreProvided(exp.exportsInfo);
|
||||
|
|
@ -713,6 +746,11 @@ class ExportInfo {
|
|||
* @type {boolean | null | undefined}
|
||||
*/
|
||||
this.provided = initFrom ? initFrom.provided : undefined;
|
||||
/**
|
||||
* is the export a terminal binding that should be checked for export star conflicts
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.terminalBinding = initFrom ? initFrom.terminalBinding : false;
|
||||
/**
|
||||
* true: it can be mangled
|
||||
* false: is can not be mangled
|
||||
|
|
@ -731,6 +769,8 @@ class ExportInfo {
|
|||
this.exportsInfoOwned = false;
|
||||
/** @type {ExportsInfo=} */
|
||||
this.exportsInfo = undefined;
|
||||
/** @type {{ module: Module, export: string[] }=} */
|
||||
this.target = undefined;
|
||||
}
|
||||
|
||||
// TODO webpack 5 remove
|
||||
|
|
@ -983,6 +1023,18 @@ class ExportInfo {
|
|||
this._usedName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ModuleGraph} moduleGraph the module graph
|
||||
* @returns {ExportInfo | undefined} the terminal binding export info if known
|
||||
*/
|
||||
getTerminalExportInfo(moduleGraph) {
|
||||
if (this.terminalBinding) return this;
|
||||
const { target } = this;
|
||||
if (!target) return undefined;
|
||||
const exportsInfo = moduleGraph.getExportsInfo(target.module);
|
||||
return exportsInfo.getReadOnlyExportInfoRecursive(target.export);
|
||||
}
|
||||
|
||||
createNestedExportsInfo() {
|
||||
if (this.exportsInfoOwned) return this.exportsInfo;
|
||||
this.exportsInfoOwned = true;
|
||||
|
|
@ -1000,6 +1052,7 @@ class ExportInfo {
|
|||
hash.update(`${this.getUsed(runtime)}`);
|
||||
hash.update(`${this.provided}`);
|
||||
hash.update(`${this.canMangle}`);
|
||||
hash.update(`${this.terminalBinding}`);
|
||||
}
|
||||
|
||||
getUsedInfo() {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@
|
|||
"use strict";
|
||||
|
||||
const asyncLib = require("neo-async");
|
||||
const { equals } = require("./util/ArrayHelpers");
|
||||
const Queue = require("./util/Queue");
|
||||
|
||||
/** @typedef {import("./Compiler")} Compiler */
|
||||
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
|
||||
/** @typedef {import("./Dependency")} Dependency */
|
||||
/** @typedef {import("./Dependency").ExportSpec} ExportSpec */
|
||||
/** @typedef {import("./ExportsInfo")} ExportsInfo */
|
||||
/** @typedef {import("./Module")} Module */
|
||||
|
||||
|
|
@ -110,6 +112,7 @@ class FlagDependencyExportsPlugin {
|
|||
if (!exportDesc) return;
|
||||
const exports = exportDesc.exports;
|
||||
const canMangle = exportDesc.canMangle;
|
||||
const terminalBinding = exportDesc.terminalBinding || false;
|
||||
const exportDeps = exportDesc.dependencies;
|
||||
if (exports === true) {
|
||||
// unknown exports
|
||||
|
|
@ -122,7 +125,37 @@ class FlagDependencyExportsPlugin {
|
|||
changed = true;
|
||||
}
|
||||
} else if (Array.isArray(exports)) {
|
||||
// merge in new exports
|
||||
/**
|
||||
* @param {Module} module from module
|
||||
* @param {string[]=} exportName export name accessed
|
||||
* @returns {{module: Module, export: string[]}=} resolved target
|
||||
*/
|
||||
const resolveTarget = (module, exportName) => {
|
||||
const set = new Set();
|
||||
for (;;) {
|
||||
if (!exportName) break;
|
||||
const exportsInfo = moduleGraph.getExportsInfo(module);
|
||||
const exportInfo = exportsInfo.getReadOnlyExportInfoRecursive(
|
||||
exportName
|
||||
);
|
||||
if (!exportInfo) break;
|
||||
if (exportInfo.target) {
|
||||
// check circular
|
||||
if (set.has(exportInfo.target)) return undefined;
|
||||
set.add(exportInfo.target);
|
||||
module = exportInfo.target.module;
|
||||
exportName = exportInfo.target.export;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { module, export: exportName };
|
||||
};
|
||||
/**
|
||||
* merge in new exports
|
||||
* @param {ExportsInfo} exportsInfo own exports info
|
||||
* @param {(ExportSpec | string)[]} exports list of exports
|
||||
*/
|
||||
const mergeExports = (exportsInfo, exports) => {
|
||||
for (const exportNameOrSpec of exports) {
|
||||
if (typeof exportNameOrSpec === "string") {
|
||||
|
|
@ -140,6 +173,10 @@ class FlagDependencyExportsPlugin {
|
|||
exportInfo.canMangleProvide = false;
|
||||
changed = true;
|
||||
}
|
||||
if (terminalBinding && !exportInfo.terminalBinding) {
|
||||
exportInfo.terminalBinding = true;
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
const exportInfo = exportsInfo.getExportInfo(
|
||||
exportNameOrSpec.name
|
||||
|
|
@ -157,47 +194,67 @@ class FlagDependencyExportsPlugin {
|
|||
exportInfo.canMangleProvide = false;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (
|
||||
exportNameOrSpec.exports &&
|
||||
exportNameOrSpec.from
|
||||
(exportNameOrSpec.terminalBinding !== undefined
|
||||
? exportNameOrSpec.terminalBinding
|
||||
: terminalBinding) &&
|
||||
!exportInfo.terminalBinding
|
||||
) {
|
||||
exportInfo.terminalBinding = true;
|
||||
changed = true;
|
||||
}
|
||||
if (exportNameOrSpec.exports) {
|
||||
const nestedExportsInfo = exportInfo.createNestedExportsInfo();
|
||||
const fromExportsInfo = moduleGraph.getExportsInfo(
|
||||
exportNameOrSpec.from
|
||||
);
|
||||
const fromNestedExportsInfo = fromExportsInfo.getNestedExportsInfo(
|
||||
exportNameOrSpec.export
|
||||
);
|
||||
mergeExports(
|
||||
nestedExportsInfo,
|
||||
exportNameOrSpec.exports
|
||||
);
|
||||
if (fromNestedExportsInfo) {
|
||||
if (
|
||||
nestedExportsInfo.setRedirectNamedTo(
|
||||
fromNestedExportsInfo
|
||||
)
|
||||
}
|
||||
const target =
|
||||
exportNameOrSpec.from &&
|
||||
resolveTarget(
|
||||
exportNameOrSpec.from,
|
||||
exportNameOrSpec.export
|
||||
);
|
||||
const oldTarget = exportInfo.target;
|
||||
if (
|
||||
target &&
|
||||
(!oldTarget ||
|
||||
oldTarget.module !== target.module ||
|
||||
(target.export
|
||||
? !oldTarget.export ||
|
||||
!equals(oldTarget.export, target.export)
|
||||
: oldTarget.export))
|
||||
) {
|
||||
exportInfo.target = target;
|
||||
changed = true;
|
||||
const targetModuleExportsInfo =
|
||||
target &&
|
||||
moduleGraph.getExportsInfo(target.module);
|
||||
const targetExportsInfo =
|
||||
targetModuleExportsInfo &&
|
||||
targetModuleExportsInfo.getNestedExportsInfo(
|
||||
target.export
|
||||
);
|
||||
if (targetExportsInfo) {
|
||||
if (exportNameOrSpec.exports) {
|
||||
const nestedExportsInfo =
|
||||
exportInfo.exportsInfo;
|
||||
if (
|
||||
nestedExportsInfo.setRedirectNamedTo(
|
||||
targetExportsInfo
|
||||
)
|
||||
) {
|
||||
changed = true;
|
||||
}
|
||||
} else if (
|
||||
exportInfo.exportsInfo !== targetExportsInfo
|
||||
) {
|
||||
exportInfo.exportsInfo = targetExportsInfo;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
} else if (exportNameOrSpec.exports) {
|
||||
const nestedExportsInfo = exportInfo.createNestedExportsInfo();
|
||||
mergeExports(
|
||||
nestedExportsInfo,
|
||||
exportNameOrSpec.exports
|
||||
);
|
||||
} else if (exportNameOrSpec.from) {
|
||||
const fromExportsInfo = moduleGraph.getExportsInfo(
|
||||
exportNameOrSpec.from
|
||||
);
|
||||
const nestedExportsInfo = fromExportsInfo.getNestedExportsInfo(
|
||||
exportNameOrSpec.export
|
||||
);
|
||||
if (!exportInfo.exportsInfo && nestedExportsInfo) {
|
||||
exportInfo.exportsInfo = nestedExportsInfo;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
|
|||
/** @typedef {import("./ExportsInfo")} ExportsInfo */
|
||||
/** @typedef {import("./ExportsInfo").ExportInfo} ExportInfo */
|
||||
/** @typedef {import("./ModuleTemplate")} ModuleTemplate */
|
||||
/** @typedef {import("./RequestShortener")} RequestShortener */
|
||||
|
||||
const joinIterableWithComma = iterable => {
|
||||
// This is more performant than Array.from().join(", ")
|
||||
|
|
@ -35,6 +36,7 @@ const joinIterableWithComma = iterable => {
|
|||
* @param {ConcatSource} source output
|
||||
* @param {string} indent spacing
|
||||
* @param {ExportsInfo} exportsInfo data
|
||||
* @param {RequestShortener} requestShortener requestShortener
|
||||
* @param {Set<ExportInfo>} alreadyPrinted deduplication set
|
||||
* @returns {void}
|
||||
*/
|
||||
|
|
@ -42,6 +44,7 @@ const printExportsInfoToSource = (
|
|||
source,
|
||||
indent,
|
||||
exportsInfo,
|
||||
requestShortener,
|
||||
alreadyPrinted = new Set()
|
||||
) => {
|
||||
const otherExportsInfo = exportsInfo.otherExportsInfo;
|
||||
|
|
@ -73,7 +76,19 @@ const printExportsInfoToSource = (
|
|||
`${indent}export ${JSON.stringify(exportInfo.name).slice(
|
||||
1,
|
||||
-1
|
||||
)} [${exportInfo.getProvidedInfo()}] [${exportInfo.getUsedInfo()}] [${exportInfo.getRenameInfo()}]`
|
||||
)} [${exportInfo.getProvidedInfo()}] [${exportInfo.getUsedInfo()}] [${exportInfo.getRenameInfo()}]${
|
||||
exportInfo.target
|
||||
? ` -> ${exportInfo.target.module.readableIdentifier(
|
||||
requestShortener
|
||||
)}${
|
||||
exportInfo.target.export
|
||||
? ` .${exportInfo.target.export
|
||||
.map(e => JSON.stringify(e).slice(1, -1))
|
||||
.join(".")}`
|
||||
: ""
|
||||
}`
|
||||
: ""
|
||||
}`
|
||||
) + "\n"
|
||||
);
|
||||
if (exportInfo.exportsInfo) {
|
||||
|
|
@ -81,6 +96,7 @@ const printExportsInfoToSource = (
|
|||
source,
|
||||
indent + " ",
|
||||
exportInfo.exportsInfo,
|
||||
requestShortener,
|
||||
alreadyPrinted
|
||||
);
|
||||
}
|
||||
|
|
@ -119,6 +135,7 @@ class ModuleInfoHeaderPlugin {
|
|||
*/
|
||||
apply(compiler) {
|
||||
compiler.hooks.compilation.tap("ModuleInfoHeaderPlugin", compilation => {
|
||||
const { requestShortener } = compilation;
|
||||
const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
|
||||
hooks.renderModulePackage.tap(
|
||||
"ModuleInfoHeaderPlugin",
|
||||
|
|
@ -146,7 +163,7 @@ class ModuleInfoHeaderPlugin {
|
|||
);
|
||||
if (exportsType) {
|
||||
const exportsInfo = moduleGraph.getExportsInfo(module);
|
||||
printExportsInfoToSource(source, "", exportsInfo);
|
||||
printExportsInfoToSource(source, "", exportsInfo, requestShortener);
|
||||
}
|
||||
source.add(
|
||||
Template.toComment(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
const InitFragment = require("./InitFragment");
|
||||
const RuntimeGlobals = require("./RuntimeGlobals");
|
||||
const Template = require("./Template");
|
||||
const { equals } = require("./util/ArrayHelpers");
|
||||
const propertyAccess = require("./util/propertyAccess");
|
||||
|
||||
/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */
|
||||
|
|
@ -20,14 +21,6 @@ const propertyAccess = require("./util/propertyAccess");
|
|||
/** @typedef {import("./RequestShortener")} RequestShortener */
|
||||
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
|
||||
|
||||
const arrayEquals = (a, b) => {
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Module} module the module
|
||||
* @param {ChunkGraph} chunkGraph the chunk graph
|
||||
|
|
@ -709,7 +702,7 @@ class RuntimeTemplate {
|
|||
);
|
||||
return `${comment} undefined`;
|
||||
}
|
||||
const comment = arrayEquals(used, exportName)
|
||||
const comment = equals(used, exportName)
|
||||
? ""
|
||||
: Template.toNormalComment(propertyAccess(exportName)) + " ";
|
||||
const access = `${importVar}${comment}${propertyAccess(used)}`;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class HarmonyExportExpressionDependency extends NullDependency {
|
|||
getExports(moduleGraph) {
|
||||
return {
|
||||
exports: ["default"],
|
||||
terminalBinding: true,
|
||||
dependencies: undefined
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
const Dependency = require("../Dependency");
|
||||
const { UsageState } = require("../ExportsInfo");
|
||||
const HarmonyLinkingError = require("../HarmonyLinkingError");
|
||||
const InitFragment = require("../InitFragment");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const Template = require("../Template");
|
||||
|
|
@ -138,7 +139,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
|
|||
* @param {string[]} ids the requested export name of the imported module
|
||||
* @param {string | null} name the export name of for this module
|
||||
* @param {Set<string>} activeExports other named exports in the module
|
||||
* @param {Iterable<Dependency>} otherStarExports other star exports in the module
|
||||
* @param {Iterable<HarmonyExportImportedSpecifierDependency>} otherStarExports other star exports in the module
|
||||
* @param {boolean} strictExportPresence when true, missing exports in the imported module lead to errors instead of warnings
|
||||
*/
|
||||
constructor(
|
||||
|
|
@ -366,7 +367,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
|
|||
const ignoredExports = new Set([
|
||||
"default",
|
||||
...this.activeExports,
|
||||
...this._discoverActiveExportsFromOtherStarExports(moduleGraph)
|
||||
...this._discoverActiveExportsFromOtherStarExports(moduleGraph).keys()
|
||||
]);
|
||||
|
||||
if (!noExtraExports && !noExtraImports) {
|
||||
|
|
@ -487,25 +488,22 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
|
|||
|
||||
/**
|
||||
* @param {ModuleGraph} moduleGraph the module graph
|
||||
* @returns {Set<string>} exported names
|
||||
* @returns {Map<string, HarmonyExportImportedSpecifierDependency>} exported names and their origin dependency
|
||||
*/
|
||||
_discoverActiveExportsFromOtherStarExports(moduleGraph) {
|
||||
if (!this.otherStarExports) {
|
||||
return new Set();
|
||||
return new Map();
|
||||
}
|
||||
|
||||
const result = new Set();
|
||||
const result = new Map();
|
||||
// try to learn impossible exports from other star exports with provided exports
|
||||
for (const otherStarExport of this.otherStarExports) {
|
||||
const otherImportedModule = moduleGraph.getModule(otherStarExport);
|
||||
if (otherImportedModule) {
|
||||
const providedExports = moduleGraph.getProvidedExports(
|
||||
otherImportedModule
|
||||
);
|
||||
|
||||
if (Array.isArray(providedExports)) {
|
||||
for (const exportName of providedExports) {
|
||||
result.add(exportName);
|
||||
const exportsInfo = moduleGraph.getExportsInfo(otherImportedModule);
|
||||
for (const exportInfo of exportsInfo.exports) {
|
||||
if (exportInfo.provided === true && !result.has(exportInfo.name)) {
|
||||
result.set(exportInfo.name, otherStarExport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -638,11 +636,74 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
|
|||
*/
|
||||
_getErrors(moduleGraph) {
|
||||
const ids = this.getIds(moduleGraph);
|
||||
return this.getLinkingErrors(
|
||||
let errors = this.getLinkingErrors(
|
||||
moduleGraph,
|
||||
ids,
|
||||
`(reexported as '${this.name}')`
|
||||
);
|
||||
if (ids.length === 0 && this.name === null) {
|
||||
const potentialConflicts = this._discoverActiveExportsFromOtherStarExports(
|
||||
moduleGraph
|
||||
);
|
||||
if (potentialConflicts.size > 0) {
|
||||
const importedModule = moduleGraph.getModule(this);
|
||||
if (importedModule) {
|
||||
const exportsInfo = moduleGraph.getExportsInfo(importedModule);
|
||||
const conflicts = new Map();
|
||||
for (const exportInfo of exportsInfo.orderedExports) {
|
||||
if (exportInfo.provided !== true) continue;
|
||||
if (
|
||||
!exportInfo.terminalBinding &&
|
||||
!(exportInfo.target && exportInfo.target.export)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (exportInfo.name === "default") continue;
|
||||
if (this.activeExports.has(exportInfo.name)) continue;
|
||||
const conflictingDependency = potentialConflicts.get(
|
||||
exportInfo.name
|
||||
);
|
||||
if (!conflictingDependency) continue;
|
||||
const target = exportInfo.getTerminalExportInfo(moduleGraph);
|
||||
if (!target) continue;
|
||||
const conflictingModule = moduleGraph.getModule(
|
||||
conflictingDependency
|
||||
);
|
||||
if (conflictingModule === importedModule) continue;
|
||||
const conflictingExportInfo = moduleGraph.getExportInfo(
|
||||
conflictingModule,
|
||||
exportInfo.name
|
||||
);
|
||||
const conflictingTarget = conflictingExportInfo.getTerminalExportInfo(
|
||||
moduleGraph
|
||||
);
|
||||
if (!conflictingTarget) continue;
|
||||
if (target === conflictingTarget) continue;
|
||||
const list = conflicts.get(conflictingDependency.request);
|
||||
if (list === undefined) {
|
||||
conflicts.set(conflictingDependency.request, [exportInfo.name]);
|
||||
} else {
|
||||
list.push(exportInfo.name);
|
||||
}
|
||||
}
|
||||
for (const [request, exports] of conflicts) {
|
||||
if (!errors) errors = [];
|
||||
errors.push(
|
||||
new HarmonyLinkingError(
|
||||
`The requested module '${
|
||||
this.request
|
||||
}' contains conflicting star exports for the ${
|
||||
exports.size > 1 ? "names" : "name"
|
||||
} ${exports
|
||||
.map(e => `'${e}'`)
|
||||
.join(", ")} with the previous requested module '${request}'`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class HarmonyExportSpecifierDependency extends NullDependency {
|
|||
getExports(moduleGraph) {
|
||||
return {
|
||||
exports: [this.name],
|
||||
terminalBinding: true,
|
||||
dependencies: undefined
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency
|
|||
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
|
||||
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
|
||||
const JavascriptParser = require("../javascript/JavascriptParser");
|
||||
const { equals } = require("../util/ArrayHelpers");
|
||||
const LazySet = require("../util/LazySet");
|
||||
const { concatComparators, keepOriginalOrder } = require("../util/comparators");
|
||||
const createHash = require("../util/createHash");
|
||||
|
|
@ -169,14 +170,6 @@ const joinIterableWithComma = iterable => {
|
|||
return str;
|
||||
};
|
||||
|
||||
const arrayEquals = (a, b) => {
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} ConcatenationEntry
|
||||
* @property {"concatenated" | "external"} type
|
||||
|
|
@ -315,7 +308,7 @@ const getExternalImport = (
|
|||
exportName.length === 0 ||
|
||||
moduleGraph.getExportsInfo(importedModule).getUsedName(exportName, runtime);
|
||||
if (!used) return "/* unused export */undefined";
|
||||
const comment = arrayEquals(used, exportName)
|
||||
const comment = equals(used, exportName)
|
||||
? ""
|
||||
: Template.toNormalComment(`${exportName.join(".")}`);
|
||||
const reference = `${exprStart}${comment}${propertyAccess(used)}`;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
exports.equals = (a, b) => {
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,3 +1,3 @@
|
|||
// TODO: Technically this should lead to an error
|
||||
// This leads to an error
|
||||
export * from "./a?3";
|
||||
export * from "./b?3";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
// TODO: Technically this should lead to an error
|
||||
// This leads to an error
|
||||
export * from "./b?4";
|
||||
export * from "./a?4";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// TODO: Technically this should lead to an error
|
||||
// This leads to an error
|
||||
export * from "./a?6";
|
||||
export * from "./b?6";
|
||||
export * from "./c?6";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// TODO: Technically this should lead to an error
|
||||
// This leads to an error
|
||||
export * from "./d?7";
|
||||
export * from "./b?7";
|
||||
export * from "./c?7";
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
exports.x = "c";
|
||||
Object(exports).x = "c";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
// TODO: Technically this should lead to an error
|
||||
// This leads to an error
|
||||
export * from "./a?3";
|
||||
export * from "./b?3";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
// TODO: Technically this should lead to an error
|
||||
// This leads to an error
|
||||
export * from "./b?4";
|
||||
export * from "./a?4";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// TODO: Technically this should lead to an error
|
||||
// This leads to an error
|
||||
export * from "./a?6";
|
||||
export * from "./b?6";
|
||||
export * from "./c?6";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// TODO: Technically this should lead to an error
|
||||
// This leads to an error
|
||||
export * from "./d?7";
|
||||
export * from "./b?7";
|
||||
export * from "./c?7";
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
exports.x = "c";
|
||||
Object(exports).x = "c";
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
exports.x = "d";
|
||||
Object(exports).x = "d";
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
exports.x = "d";
|
||||
Object(exports).x = "d";
|
||||
|
|
|
|||
|
|
@ -14,22 +14,22 @@ var y5 = require("./cjs/5").x;
|
|||
var y6 = require("./cjs/6").x;
|
||||
var y7 = require("./cjs/7").x;
|
||||
|
||||
it("should not overwrite when using star export (known exports)", function() {
|
||||
it("should not overwrite when using star export (known exports)", function () {
|
||||
expect(x1).toBe("1");
|
||||
expect(x2).toBe("1");
|
||||
expect(x3).toBe("a");
|
||||
expect(x4).toBe("b");
|
||||
expect(x5).toBe("c");
|
||||
expect(x6).toBe("a");
|
||||
expect(x7).toBe("d"); // Looks wrong, but is irrelevant as this should be an error anyway
|
||||
expect(x7).toBe("b"); // Looks wrong, but is irrelevant as this is an error anyway
|
||||
});
|
||||
|
||||
it("should not overwrite when using star export (unknown exports)", function() {
|
||||
it("should not overwrite when using star export (unknown exports)", function () {
|
||||
expect(y1).toBe("1");
|
||||
expect(y2).toBe("1");
|
||||
expect(y3).toBe("a");
|
||||
expect(y4).toBe("b");
|
||||
expect(y5).toBe("c");
|
||||
expect(y6).toBe("a");
|
||||
expect(y7).toBe("d"); // Looks wrong, but is irrelevant as this should be an error anyway
|
||||
expect(y7).toBe("b"); // Looks wrong, but is irrelevant as this is an error anyway
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
module.exports = [
|
||||
[/name 'x'/, /\.\/a\?3/, /\.\/b\?3/],
|
||||
[/name 'x'/, /\.\/a\?4/, /\.\/b\?4/],
|
||||
[/name 'x'/, /\.\/a\?6/, /\.\/b\?6/],
|
||||
[/name 'x'/, /\.\/a\?7/, /\.\/b\?7/],
|
||||
[/name 'x'/, /\.\/a\?3/, /\.\/b\?3/],
|
||||
[/name 'x'/, /\.\/a\?4/, /\.\/b\?4/],
|
||||
[/name 'x'/, /\.\/a\?6/, /\.\/b\?6/],
|
||||
[/name 'x'/, /\.\/a\?7/, /\.\/b\?7/]
|
||||
];
|
||||
|
|
@ -2553,6 +2553,11 @@ declare abstract class ExportInfo {
|
|||
*/
|
||||
provided: boolean;
|
||||
|
||||
/**
|
||||
* is the export a terminal binding that should be checked for export star conflicts
|
||||
*/
|
||||
terminalBinding: boolean;
|
||||
|
||||
/**
|
||||
* true: it can be mangled
|
||||
* false: is can not be mangled
|
||||
|
|
@ -2568,6 +2573,7 @@ declare abstract class ExportInfo {
|
|||
canMangleUse: boolean;
|
||||
exportsInfoOwned: boolean;
|
||||
exportsInfo: ExportsInfo;
|
||||
target: { module: Module; export: string[] };
|
||||
readonly canMangle: boolean;
|
||||
setUsedInUnknownWay(runtime: string | SortableSet<string>): boolean;
|
||||
setUsedWithoutInfo(runtime: string | SortableSet<string>): boolean;
|
||||
|
|
@ -2596,6 +2602,7 @@ declare abstract class ExportInfo {
|
|||
* Sets the mangled name of this export
|
||||
*/
|
||||
setUsedName(name: string): void;
|
||||
getTerminalExportInfo(moduleGraph: ModuleGraph): ExportInfo;
|
||||
createNestedExportsInfo(): ExportsInfo;
|
||||
getNestedExportsInfo(): ExportsInfo;
|
||||
updateHash(hash?: any, runtime?: any): void;
|
||||
|
|
@ -2618,6 +2625,11 @@ declare interface ExportSpec {
|
|||
*/
|
||||
canMangle?: boolean;
|
||||
|
||||
/**
|
||||
* is the export a terminal binding that should be checked for export star conflicts
|
||||
*/
|
||||
terminalBinding?: boolean;
|
||||
|
||||
/**
|
||||
* nested exports
|
||||
*/
|
||||
|
|
@ -2645,6 +2657,7 @@ declare abstract class ExportsInfo {
|
|||
getOwnExportInfo(name: string): ExportInfo;
|
||||
getExportInfo(name: string): ExportInfo;
|
||||
getReadOnlyExportInfo(name: string): ExportInfo;
|
||||
getReadOnlyExportInfoRecursive(name: string[]): ExportInfo;
|
||||
getNestedExportsInfo(name?: string[]): ExportsInfo;
|
||||
setUnknownExportsProvided(
|
||||
canMangle?: boolean,
|
||||
|
|
@ -2679,6 +2692,7 @@ declare abstract class ExportsInfo {
|
|||
restoreProvided(__0: {
|
||||
otherProvided: any;
|
||||
otherCanMangleProvide: any;
|
||||
otherTerminalBinding: any;
|
||||
exports: any;
|
||||
}): void;
|
||||
}
|
||||
|
|
@ -2698,6 +2712,11 @@ declare interface ExportsSpec {
|
|||
*/
|
||||
canMangle?: boolean;
|
||||
|
||||
/**
|
||||
* are the exports terminal bindings that should be checked for export star conflicts
|
||||
*/
|
||||
terminalBinding?: boolean;
|
||||
|
||||
/**
|
||||
* module on which the result depends on
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue