avoid crashing when exports info contains a circular reference

e. g. when using

import * as self from "./self";
export { self }
This commit is contained in:
Tobias Koppers 2020-02-21 11:15:20 +01:00
parent a105b663e9
commit aace364df3
2 changed files with 65 additions and 11 deletions

View File

@ -28,9 +28,36 @@ const joinIterableWithComma = iterable => {
return str;
};
const printExportsInfoToSource = (source, indent, exportsInfo) => {
let hasExports = false;
const printExportsInfoToSource = (
source,
indent,
exportsInfo,
alreadyPrinted = new Set()
) => {
const otherExportsInfo = exportsInfo.otherExportsInfo;
let alreadyPrintedExports = 0;
// determine exports to print
const printedExports = [];
for (const exportInfo of exportsInfo.orderedExports) {
if (!alreadyPrinted.has(exportInfo)) {
alreadyPrinted.add(exportInfo);
printedExports.push(exportInfo);
} else {
alreadyPrintedExports++;
}
}
let showOtherExports = false;
if (!alreadyPrinted.has(otherExportsInfo)) {
alreadyPrinted.add(otherExportsInfo);
showOtherExports = true;
} else {
alreadyPrintedExports++;
}
// print the exports
for (const exportInfo of printedExports) {
source.add(
Template.toComment(
`${indent}export ${JSON.stringify(exportInfo.name).slice(
@ -40,20 +67,39 @@ const printExportsInfoToSource = (source, indent, exportsInfo) => {
) + "\n"
);
if (exportInfo.exportsInfo) {
printExportsInfoToSource(source, indent + " ", exportInfo.exportsInfo);
printExportsInfoToSource(
source,
indent + " ",
exportInfo.exportsInfo,
alreadyPrinted
);
}
hasExports = true;
}
const otherExportsInfo = exportsInfo.otherExportsInfo;
if (otherExportsInfo.provided !== false || otherExportsInfo.used !== false) {
const title = hasExports ? "other exports" : "exports";
if (alreadyPrintedExports) {
source.add(
Template.toComment(
`${indent}${title} [${otherExportsInfo.getProvidedInfo()}] [${otherExportsInfo.getUsedInfo()}]`
`${indent}... (${alreadyPrintedExports} already listed exports)`
) + "\n"
);
}
if (showOtherExports) {
if (
otherExportsInfo.provided !== false ||
otherExportsInfo.used !== false
) {
const title =
printedExports.length > 0 || alreadyPrintedExports > 0
? "other exports"
: "exports";
source.add(
Template.toComment(
`${indent}${title} [${otherExportsInfo.getProvidedInfo()}] [${otherExportsInfo.getUsedInfo()}]`
) + "\n"
);
}
}
};
class ModuleInfoHeaderPlugin {

View File

@ -40,16 +40,23 @@ const EMPTY_MAP = new Map();
const EMPTY_SET = new Set();
/**
*
* @param {string[][]} referencedExports list of referenced exports, will be added to
* @param {string[]} prefix export prefix
* @param {ExportInfo} exportInfo the export info
* @param {Set<ExportInfo>} alreadyVisited already visited export info (to handle circular reexports)
*/
const processExportInfo = (referencedExports, prefix, exportInfo) => {
const processExportInfo = (
referencedExports,
prefix,
exportInfo,
alreadyVisited = new Set()
) => {
if (!exportInfo) {
referencedExports.push(prefix);
return;
}
if (alreadyVisited.has(exportInfo)) return;
alreadyVisited.add(exportInfo);
if (exportInfo.used === UsageState.Unused) return;
if (
exportInfo.used !== UsageState.OnlyPropertiesUsed ||
@ -64,7 +71,8 @@ const processExportInfo = (referencedExports, prefix, exportInfo) => {
processExportInfo(
referencedExports,
prefix.concat(exportInfo.name),
exportInfo
exportInfo,
alreadyVisited
);
}
};