Emit 'exports.foo' assignments for bindings that are exported in specifiers.

This commit is contained in:
Daniel Rosenwasser 2016-04-13 17:31:53 -07:00
parent da72357562
commit e13a07e3bd
2 changed files with 82 additions and 43 deletions

View File

@ -559,34 +559,64 @@ namespace ts {
}
function visitVariableStatement(node: VariableStatement): VisitResult<Statement> {
if (hasModifier(node, ModifierFlags.Export)) {
// If the variable is for a declaration that has a local name,
// do not elide the declaration.
const original = getOriginalNode(node);
if (original.kind === SyntaxKind.EnumDeclaration
|| original.kind === SyntaxKind.ModuleDeclaration) {
return setOriginalNode(
createVariableStatement(
/*modifiers*/ undefined,
node.declarationList
),
node
);
// If the variable is for a generated declaration,
// we should maintain it and just strip off the 'export' modifier if necesary.
const original = getOriginalNode(node);
if (original.kind === SyntaxKind.EnumDeclaration || original.kind === SyntaxKind.ModuleDeclaration) {
if (!hasModifier(node, ModifierFlags.Export)) {
return node;
}
const variables = getInitializedVariables(node.declarationList);
if (variables.length === 0) {
// elide statement if there are no initialized variables
return undefined;
}
return createStatement(
inlineExpressions(
map(variables, transformInitializedVariable)
)
return setOriginalNode(
createVariableStatement(
/*modifiers*/ undefined,
node.declarationList
),
node
);
}
return node;
const resultStatements: Statement[] = [];
// If we're exporting these variables, then these just become assignments to 'exports.blah'.
// We only want to emit assignments for variables with initializers.
if (hasModifier(node, ModifierFlags.Export)) {
const variables = getInitializedVariables(node.declarationList);
if (variables.length > 0) {
let inlineAssignments = createStatement(
inlineExpressions(
map(variables, transformInitializedVariable)
)
);
resultStatements.push(inlineAssignments);
}
}
else {
resultStatements.push(node);
}
// While we might not have been exported here, each variable might have been exported
// later on in an export specifier (e.g. `export {foo as blah, bar}`).
for (const decl of node.declarationList.declarations) {
addExportMemberAssignmentsForBindingName(resultStatements, decl.name);
}
return resultStatements;
}
/**
* Creates appropriate assignments for each binding identifier that is exported in an export specifier,
* and inserts it into 'resultStatements'.
*/
function addExportMemberAssignmentsForBindingName(resultStatements: Statement[], name: BindingName): void {
if (isBindingPattern(name)) {
for (const element of name.elements) {
addExportMemberAssignmentsForBindingName(resultStatements, element.name)
}
}
else {
addExportMemberAssignments(resultStatements, name);
}
}
function transformInitializedVariable(node: VariableDeclaration): Expression {
@ -665,28 +695,34 @@ namespace ts {
function visitExpressionStatement(node: ExpressionStatement): VisitResult<Statement> {
const original = getOriginalNode(node);
if (original.kind === SyntaxKind.EnumDeclaration
&& hasModifier(original, ModifierFlags.Export)) {
return visitExpressionStatementForEnumDeclaration(node, <EnumDeclaration>original);
const origKind = original.kind;
if (origKind === SyntaxKind.EnumDeclaration || origKind === SyntaxKind.ModuleDeclaration) {
return visitExpressionStatementForEnumOrNamespaceDeclaration(node, <EnumDeclaration | ModuleDeclaration>original);
}
return node;
}
function visitExpressionStatementForEnumDeclaration(node: ExpressionStatement, original: EnumDeclaration): VisitResult<Statement> {
if (isFirstDeclarationOfKind(original, SyntaxKind.EnumDeclaration)) {
const statements: Statement[] = [node];
addVarForExportedEnumDeclaration(statements, original);
return statements;
function visitExpressionStatementForEnumOrNamespaceDeclaration(node: ExpressionStatement, original: EnumDeclaration | ModuleDeclaration): VisitResult<Statement> {
const statements: Statement[] = [node];
// Preserve old behavior for enums in which a variable statement is emitted after the body itself.
if (hasModifier(original, ModifierFlags.Export) &&
original.kind === SyntaxKind.EnumDeclaration &&
isFirstDeclarationOfKind(original, SyntaxKind.EnumDeclaration)) {
addVarForExportedEnumOrNamespaceDeclaration(statements, original);
}
else {
addExportMemberAssignments(statements, original.name);
}
return node;
return statements;
}
/**
* Adds a trailing VariableStatement for an enum or module declaration.
*/
function addVarForExportedEnumDeclaration(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) {
function addVarForExportedEnumOrNamespaceDeclaration(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) {
statements.push(
createVariableStatement(
/*modifiers*/ undefined,

View File

@ -2229,14 +2229,17 @@ namespace ts {
function addVarForEnumDeclaration(statements: Statement[], node: EnumDeclaration) {
// Emit a variable statement for the enum.
statements.push(
createVariableStatement(
isES6ExportedDeclaration(node)
? visitNodes(node.modifiers, visitor, isModifier)
: undefined,
[createVariableDeclaration(
getDeclarationName(node)
)],
/*location*/ node
setOriginalNode(
createVariableStatement(
isES6ExportedDeclaration(node)
? visitNodes(node.modifiers, visitor, isModifier)
: undefined,
[createVariableDeclaration(
getDeclarationName(node)
)],
/*location*/ node
),
/*original*/ node
)
);
}
@ -2398,7 +2401,7 @@ namespace ts {
function isES6ExportedDeclaration(node: Node) {
return isExternalModuleExport(node)
&& moduleKind >= ModuleKind.ES6;
&& moduleKind === ModuleKind.ES6;
}
function shouldEmitVarForModuleDeclaration(node: ModuleDeclaration) {