Make sure that export * from does not overwrite named exports.
This commit is contained in:
@@ -49,6 +49,12 @@ export function rewriteModuleStatementsAndPrepareHeader(
|
||||
headers.push(buildESModuleHeader(meta, loose /* enumerable */));
|
||||
}
|
||||
|
||||
const nameList = buildExportNameListDeclaration(path, meta);
|
||||
if (nameList) {
|
||||
meta.exportNameListName = nameList.name;
|
||||
headers.push(nameList.statement);
|
||||
}
|
||||
|
||||
// Create all of the statically known named exports.
|
||||
headers.push(...buildExportInitializationStatements(path, meta));
|
||||
|
||||
@@ -201,6 +207,8 @@ function buildESModuleHeader(
|
||||
const namespaceReexport = template(`
|
||||
Object.keys(NAMESPACE).forEach(function(key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
VERIFY_NAME_LIST;
|
||||
|
||||
Object.defineProperty(EXPORTS, key, {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
@@ -209,15 +217,22 @@ const namespaceReexport = template(`
|
||||
});
|
||||
});
|
||||
`);
|
||||
const buildNameListCheck = template(`
|
||||
if (Object.prototype.hasOwnProperty.call(EXPORTS_LIST, key)) return;
|
||||
`);
|
||||
|
||||
/**
|
||||
* Create a re-export initialization loop for a specific imported namespace.
|
||||
*/
|
||||
function buildNamespaceReexport(metadata, namespace) {
|
||||
// TODO: This should skip exporting a prop that is already exported.
|
||||
return namespaceReexport({
|
||||
NAMESPACE: t.identifier(namespace),
|
||||
EXPORTS: t.identifier(metadata.exportName),
|
||||
VERIFY_NAME_LIST: metadata.exportNameListName
|
||||
? buildNameListCheck({
|
||||
EXPORTS_LIST: t.identifier(metadata.exportNameListName),
|
||||
})
|
||||
: null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -230,6 +245,48 @@ const reexportGetter = template(`
|
||||
});
|
||||
`);
|
||||
|
||||
/**
|
||||
* Build a statement declaring a variable that contains all of the exported
|
||||
* variable names in an object so they can easily be referenced from an
|
||||
* export * from statement to check for conflicts.
|
||||
*/
|
||||
function buildExportNameListDeclaration(
|
||||
programPath: NodePath,
|
||||
metadata: ModuleMetadata,
|
||||
) {
|
||||
const exportedVars = Object.create(null);
|
||||
for (const data of metadata.local.values()) {
|
||||
for (const name of data.names) {
|
||||
exportedVars[name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
let hasReexport = false;
|
||||
for (const data of metadata.source.values()) {
|
||||
for (const exportName of data.reexports.keys()) {
|
||||
exportedVars[exportName] = true;
|
||||
}
|
||||
for (const exportName of data.reexportNamespace) {
|
||||
exportedVars[exportName] = true;
|
||||
}
|
||||
|
||||
hasReexport = hasReexport || data.reexportAll;
|
||||
}
|
||||
|
||||
if (!hasReexport || Object.keys(exportedVars).length === 0) return null;
|
||||
|
||||
const name = programPath.scope.generateUidIdentifier("exportNames");
|
||||
|
||||
delete exportedVars.default;
|
||||
|
||||
return {
|
||||
name: name.name,
|
||||
statement: t.variableDeclaration("var", [
|
||||
t.variableDeclarator(name, t.valueToNode(exportedVars)),
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a set of statements that will initialize all of the statically-known
|
||||
* export names with their expected values.
|
||||
@@ -252,7 +309,7 @@ function buildExportInitializationStatements(
|
||||
exportNames.push(...data.names);
|
||||
}
|
||||
}
|
||||
for (const [, data] of metadata.source) {
|
||||
for (const data of metadata.source.values()) {
|
||||
for (const [exportName, importName] of data.reexports) {
|
||||
initStatements.push(
|
||||
reexportGetter({
|
||||
|
||||
@@ -4,6 +4,10 @@ import * as t from "babel-types";
|
||||
|
||||
export type ModuleMetadata = {
|
||||
exportName: string,
|
||||
|
||||
// The name of the variable that will reference an object containing export names.
|
||||
exportNameListName: null | string,
|
||||
|
||||
// Lookup from local binding to export information.
|
||||
local: Map<string, LocalExportMetadata>,
|
||||
|
||||
@@ -107,6 +111,7 @@ export default function normalizeModuleAndLoadMetadata(
|
||||
|
||||
return {
|
||||
exportName,
|
||||
exportNameListName: null,
|
||||
local,
|
||||
source,
|
||||
};
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
var _exportNames = {
|
||||
z: true,
|
||||
a: true,
|
||||
b: true,
|
||||
d: true,
|
||||
e: true,
|
||||
f: true,
|
||||
c: true
|
||||
};
|
||||
exports.b = b;
|
||||
exports.default = _default;
|
||||
Object.defineProperty(exports, "c", {
|
||||
@@ -17,6 +26,7 @@ var _mod = require("mod");
|
||||
|
||||
Object.keys(_mod).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
|
||||
Reference in New Issue
Block a user