Merge pull request #6238 from loganfsmyth/reexport-name-priority

Named exports should always take priority over star exports
This commit is contained in:
Logan Smyth
2017-09-13 13:33:50 -07:00
committed by GitHub
4 changed files with 107 additions and 2 deletions

View File

@@ -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));
@@ -173,6 +179,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() {
@@ -181,15 +189,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,
});
}
@@ -202,6 +217,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.
@@ -224,7 +281,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({

View File

@@ -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,
};

View File

@@ -1 +1,8 @@
export let z = 100;
export * from 'mod';
export class a {}
export function b() {}
export { c } from 'mod';
export let d = 42;
export let e = 1, f = 2;
export default function() {}

View File

@@ -3,11 +3,30 @@
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", {
enumerable: true,
get: function () {
return _mod.c;
}
});
exports.f = exports.e = exports.d = exports.a = exports.z = void 0;
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 () {
@@ -15,3 +34,20 @@ Object.keys(_mod).forEach(function (key) {
}
});
});
var z = 100;
exports.z = z;
class a {}
exports.a = a;
function b() {}
var d = 42;
exports.d = d;
var e = 1,
f = 2;
exports.f = f;
exports.e = e;
function _default() {}