mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 20:25:23 -06:00
Fix emit for modules and enums.
This commit is contained in:
parent
36257ec478
commit
d647f89daa
@ -16076,7 +16076,7 @@ namespace ts {
|
||||
|
||||
// When resolved as an expression identifier, if the given node references an exported entity, return the declaration
|
||||
// node of the exported entity's container. Otherwise, return undefined.
|
||||
function getReferencedExportContainer(node: Identifier): SourceFile | ModuleDeclaration | EnumDeclaration {
|
||||
function getReferencedExportContainer(node: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration {
|
||||
let symbol = getReferencedValueSymbol(node);
|
||||
if (symbol) {
|
||||
if (symbol.flags & SymbolFlags.ExportValue) {
|
||||
@ -16084,7 +16084,7 @@ namespace ts {
|
||||
// we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the
|
||||
// kinds that we do NOT prefix.
|
||||
const exportSymbol = getMergedSymbol(symbol.exportSymbol);
|
||||
if (exportSymbol.flags & SymbolFlags.ExportHasLocal) {
|
||||
if (exportSymbol.flags & SymbolFlags.ExportHasLocal && !prefixLocals) {
|
||||
return undefined;
|
||||
}
|
||||
symbol = exportSymbol;
|
||||
|
||||
@ -258,6 +258,9 @@ namespace ts {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return visitClassDeclaration(<ClassDeclaration>node);
|
||||
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
return visitExpressionStatement(<ExpressionStatement>node);
|
||||
|
||||
default:
|
||||
// This visitor does not descend into the tree, as export/import statements
|
||||
// are only transformed at the top level of a file.
|
||||
@ -544,24 +547,33 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function addExportMemberAssignment(statements: Statement[], node: FunctionDeclaration | ClassDeclaration) {
|
||||
function addExportMemberAssignment(statements: Statement[], node: DeclarationStatement) {
|
||||
if (hasModifier(node, ModifierFlags.Default)) {
|
||||
addExportDefault(statements, node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node), /*location*/ node);
|
||||
addExportDefault(statements, getDeclarationName(node), /*location*/ node);
|
||||
}
|
||||
else {
|
||||
statements.push(
|
||||
startOnNewLine(
|
||||
createStatement(
|
||||
createExportAssignment(node.name, node.name),
|
||||
/*location*/ node
|
||||
)
|
||||
)
|
||||
createExportStatement(node.name, node.name, /*location*/ node)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
const variables = getInitializedVariables(node.declarationList);
|
||||
if (variables.length === 0) {
|
||||
// elide statement if there are no initialized variables
|
||||
@ -651,6 +663,87 @@ namespace ts {
|
||||
return singleOrMany(statements);
|
||||
}
|
||||
|
||||
function visitExpressionStatement(node: ExpressionStatement): VisitResult<Statement> {
|
||||
const original = getOriginalNode(node);
|
||||
if (hasModifier(original, ModifierFlags.Export)) {
|
||||
switch (original.kind) {
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return visitExpressionStatementForEnumDeclaration(node, <EnumDeclaration>original);
|
||||
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return visitExpressionStatementForModuleDeclaration(node, <ModuleDeclaration>original);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function visitExpressionStatementForEnumDeclaration(node: ExpressionStatement, original: EnumDeclaration): VisitResult<Statement> {
|
||||
if (isFirstDeclarationOfKind(original, SyntaxKind.EnumDeclaration)) {
|
||||
const statements: Statement[] = [node];
|
||||
addVarForExportedEnumOrModule(statements, original);
|
||||
return statements;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function isModuleMergedWithES6Class(node: ModuleDeclaration) {
|
||||
return languageVersion === ScriptTarget.ES6
|
||||
&& isMergedWithClass(node);
|
||||
}
|
||||
|
||||
function visitExpressionStatementForModuleDeclaration(node: ExpressionStatement, original: ModuleDeclaration): VisitResult<Statement> {
|
||||
if (isFirstDeclarationOfKind(original, SyntaxKind.ModuleDeclaration)) {
|
||||
const statements: Statement[] = [node];
|
||||
if (isModuleMergedWithES6Class(original)) {
|
||||
addAssignmentForExportedModule(statements, original);
|
||||
}
|
||||
else {
|
||||
addVarForExportedEnumOrModule(statements, original);
|
||||
}
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing VariableStatement for an enum or module declaration.
|
||||
*/
|
||||
function addVarForExportedEnumOrModule(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) {
|
||||
statements.push(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
[createVariableDeclaration(
|
||||
getDeclarationName(node),
|
||||
createPropertyAccess(createIdentifier("exports"), getDeclarationName(node))
|
||||
)],
|
||||
/*location*/ node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing assignment for a module declaration.
|
||||
*/
|
||||
function addAssignmentForExportedModule(statements: Statement[], node: ModuleDeclaration) {
|
||||
statements.push(
|
||||
createStatement(
|
||||
createAssignment(
|
||||
getDeclarationName(node),
|
||||
createPropertyAccess(createIdentifier("exports"), getDeclarationName(node))
|
||||
),
|
||||
/*location*/ node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getDeclarationName(node: DeclarationStatement) {
|
||||
return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node);
|
||||
}
|
||||
|
||||
function substituteExpression(node: Expression) {
|
||||
node = previousExpressionSubstitution(node);
|
||||
if (isIdentifier(node)) {
|
||||
@ -663,7 +756,7 @@ namespace ts {
|
||||
function substituteExpressionIdentifier(node: Identifier): Expression {
|
||||
const original = getOriginalNode(node);
|
||||
if (isIdentifier(original)) {
|
||||
const container = resolver.getReferencedExportContainer(original);
|
||||
const container = resolver.getReferencedExportContainer(original, (getNodeEmitFlags(node) & NodeEmitFlags.PrefixExportedLocal) !== 0);
|
||||
if (container) {
|
||||
if (container.kind === SyntaxKind.SourceFile) {
|
||||
return createPropertyAccess(
|
||||
@ -760,6 +853,10 @@ namespace ts {
|
||||
return createCall(createIdentifier("require"), args);
|
||||
}
|
||||
|
||||
function createExportStatement(name: Identifier, value: Expression, location?: TextRange) {
|
||||
return startOnNewLine(createStatement(createExportAssignment(name, value), location));
|
||||
}
|
||||
|
||||
function createExportAssignment(name: Identifier, value: Expression) {
|
||||
return createAssignment(
|
||||
name.originalKeywordKind && languageVersion === ScriptTarget.ES3
|
||||
|
||||
@ -94,7 +94,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitWithStack(node: Node, visitor: (node: Node) => VisitResult<Node>): VisitResult<Node> {
|
||||
function saveStateAndInvoke<T>(node: Node, f: (node: Node) => T): T {
|
||||
// Save state
|
||||
const savedCurrentScope = currentScope;
|
||||
const savedCurrentParent = currentParent;
|
||||
@ -103,7 +103,7 @@ namespace ts {
|
||||
// Handle state changes before visiting a node.
|
||||
onBeforeVisitNode(node);
|
||||
|
||||
const visited = visitor(node);
|
||||
const visited = f(node);
|
||||
|
||||
// Restore state
|
||||
currentScope = savedCurrentScope;
|
||||
@ -119,7 +119,7 @@ namespace ts {
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitor(node: Node): VisitResult<Node> {
|
||||
return visitWithStack(node, visitorWorker);
|
||||
return saveStateAndInvoke(node, visitorWorker);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,7 +146,7 @@ namespace ts {
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function namespaceElementVisitor(node: Node): VisitResult<Node> {
|
||||
return visitWithStack(node, namespaceElementVisitorWorker);
|
||||
return saveStateAndInvoke(node, namespaceElementVisitorWorker);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,7 +155,7 @@ namespace ts {
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function namespaceElementVisitorWorker(node: Node): VisitResult<Node> {
|
||||
if (node.transformFlags & TransformFlags.TypeScript || isExported(node)) {
|
||||
if (node.transformFlags & TransformFlags.TypeScript || hasModifier(node, ModifierFlags.Export)) {
|
||||
// This node is explicitly marked as TypeScript, or is exported at the namespace
|
||||
// level, so we should transform the node.
|
||||
return visitTypeScript(node);
|
||||
@ -174,7 +174,7 @@ namespace ts {
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function classElementVisitor(node: Node): VisitResult<Node> {
|
||||
return visitWithStack(node, classElementVisitorWorker);
|
||||
return saveStateAndInvoke(node, classElementVisitorWorker);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -462,7 +462,7 @@ namespace ts {
|
||||
// Otherwise, if the class was exported at the top level and was decorated, emit an export
|
||||
// declaration or export default for the class.
|
||||
if (isNamespaceExport(node)) {
|
||||
addNamespaceExport(statements, name, name);
|
||||
addExportMemberAssignment(statements, node);
|
||||
}
|
||||
else if (node.decorators) {
|
||||
if (isDefaultExternalModuleExport(node)) {
|
||||
@ -619,16 +619,19 @@ namespace ts {
|
||||
|
||||
// let ${name} = ${classExpression};
|
||||
addNode(statements,
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
name,
|
||||
classExpression
|
||||
)
|
||||
],
|
||||
/*location*/ undefined,
|
||||
NodeFlags.Let)
|
||||
setOriginalNode(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
name,
|
||||
classExpression
|
||||
)
|
||||
],
|
||||
/*location*/ undefined,
|
||||
NodeFlags.Let)
|
||||
),
|
||||
/*original*/ node
|
||||
)
|
||||
);
|
||||
|
||||
@ -1785,6 +1788,17 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to emit a function-like declaration. We should not emit the
|
||||
* declaration if it is an overload, is abstract, or is an ambient declaration.
|
||||
*
|
||||
* @param node The declaration node.
|
||||
*/
|
||||
function shouldEmitFunctionLikeDeclaration(node: FunctionLikeDeclaration) {
|
||||
return !nodeIsMissing(node.body)
|
||||
&& !hasModifier(node, ModifierFlags.Abstract | ModifierFlags.Ambient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a method declaration of a class.
|
||||
*
|
||||
@ -1797,7 +1811,7 @@ namespace ts {
|
||||
* @param node The method node.
|
||||
*/
|
||||
function visitMethodDeclaration(node: MethodDeclaration) {
|
||||
if (shouldElideFunctionLikeDeclaration(node)) {
|
||||
if (!shouldEmitFunctionLikeDeclaration(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -1813,6 +1827,16 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to emit an accessor declaration. We should not emit the
|
||||
* declaration if it is abstract or is an ambient declaration.
|
||||
*
|
||||
* @param node The declaration node.
|
||||
*/
|
||||
function shouldEmitAccessorDeclaration(node: AccessorDeclaration) {
|
||||
return !hasModifier(node, ModifierFlags.Abstract | ModifierFlags.Ambient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a get accessor declaration of a class.
|
||||
*
|
||||
@ -1823,7 +1847,7 @@ namespace ts {
|
||||
* @param node The get accessor node.
|
||||
*/
|
||||
function visitGetAccessor(node: GetAccessorDeclaration) {
|
||||
if (shouldElideAccessorDeclaration(node)) {
|
||||
if (!shouldEmitAccessorDeclaration(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -1835,16 +1859,6 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a function-like declaration should be elided. A declaration should
|
||||
* be elided if it is an overload, is abstract, or is an ambient declaration.
|
||||
*
|
||||
* @param node The declaration node.
|
||||
*/
|
||||
function shouldElideAccessorDeclaration(node: AccessorDeclaration) {
|
||||
return hasModifier(node, ModifierFlags.Abstract | ModifierFlags.Ambient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a set accessor declaration of a class.
|
||||
*
|
||||
@ -1855,7 +1869,7 @@ namespace ts {
|
||||
* @param node The set accessor node.
|
||||
*/
|
||||
function visitSetAccessor(node: SetAccessorDeclaration) {
|
||||
if (shouldElideAccessorDeclaration(node)) {
|
||||
if (!shouldEmitAccessorDeclaration(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -1879,7 +1893,7 @@ namespace ts {
|
||||
* @param node The function node.
|
||||
*/
|
||||
function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> {
|
||||
if (shouldElideFunctionLikeDeclaration(node)) {
|
||||
if (!shouldEmitFunctionLikeDeclaration(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -1893,10 +1907,9 @@ namespace ts {
|
||||
);
|
||||
|
||||
if (isNamespaceExport(node)) {
|
||||
return [
|
||||
func,
|
||||
createNamespaceExport(getSynthesizedClone(node.name), getSynthesizedClone(node.name))
|
||||
];
|
||||
const statements: Statement[] = [func];
|
||||
addExportMemberAssignment(statements, node);
|
||||
return statements;
|
||||
}
|
||||
|
||||
return func;
|
||||
@ -1924,17 +1937,6 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a function-like declaration should be elided. A declaration should
|
||||
* be elided if it is an overload, is abstract, or is an ambient declaration.
|
||||
*
|
||||
* @param node The declaration node.
|
||||
*/
|
||||
function shouldElideFunctionLikeDeclaration(node: FunctionLikeDeclaration) {
|
||||
return nodeIsMissing(node.body)
|
||||
|| hasModifier(node, ModifierFlags.Abstract | ModifierFlags.Ambient);
|
||||
}
|
||||
|
||||
/**
|
||||
* @remarks
|
||||
* This function will be called when one of the following conditions are met:
|
||||
@ -2109,155 +2111,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits an enum declaration.
|
||||
*
|
||||
* This function will be called any time a TypeScript enum is encountered.
|
||||
*
|
||||
* @param node The enum declaration node.
|
||||
*/
|
||||
function visitEnumDeclaration(node: EnumDeclaration): VisitResult<Statement> {
|
||||
if (shouldElideEnumDeclaration(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const savedCurrentNamespaceLocalName = currentNamespaceLocalName;
|
||||
const statements: Statement[] = [];
|
||||
|
||||
let location: TextRange = node;
|
||||
if (!isExported(node) || (isExternalModuleExport(node) && isFirstDeclarationOfKind(node, SyntaxKind.EnumDeclaration))) {
|
||||
// Emit a VariableStatement if the enum is not exported, or is the first enum
|
||||
// with the same name exported from an external module.
|
||||
statements.push(
|
||||
createVariableStatement(
|
||||
visitNodes(node.modifiers, visitor, isModifier),
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(node.name)
|
||||
]),
|
||||
location
|
||||
)
|
||||
);
|
||||
location = undefined;
|
||||
}
|
||||
|
||||
const name = isNamespaceExport(node)
|
||||
? getNamespaceMemberName(node.name)
|
||||
: getSynthesizedClone(node.name);
|
||||
|
||||
currentNamespaceLocalName = getGeneratedNameForNode(node);
|
||||
|
||||
// (function (x) {
|
||||
// x[x["y"] = 0] = "y";
|
||||
// ...
|
||||
// })(x || (x = {}));
|
||||
statements.push(
|
||||
setOriginalNode(
|
||||
createStatement(
|
||||
createCall(
|
||||
createParen(
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
[createParameter(currentNamespaceLocalName)],
|
||||
transformEnumBody(node)
|
||||
)
|
||||
),
|
||||
[createLogicalOr(
|
||||
name,
|
||||
createAssignment(
|
||||
name,
|
||||
createObjectLiteral()
|
||||
)
|
||||
)]
|
||||
),
|
||||
location
|
||||
), node
|
||||
)
|
||||
);
|
||||
|
||||
if (isNamespaceExport(node)) {
|
||||
addNode(statements,
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(node.name, name)
|
||||
]),
|
||||
location
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
currentNamespaceLocalName = savedCurrentNamespaceLocalName;
|
||||
return statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the body of an enum declaration.
|
||||
*
|
||||
* @param node The enum declaration node.
|
||||
*/
|
||||
function transformEnumBody(node: EnumDeclaration): Block {
|
||||
const statements: Statement[] = [];
|
||||
startLexicalEnvironment();
|
||||
addNodes(statements, map(node.members, transformEnumMember));
|
||||
addNodes(statements, endLexicalEnvironment());
|
||||
return createBlock(statements, /*location*/ undefined, /*multiLine*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an enum member into a statement.
|
||||
*
|
||||
* @param member The enum member node.
|
||||
*/
|
||||
function transformEnumMember(member: EnumMember): Statement {
|
||||
const name = getExpressionForPropertyName(member);
|
||||
return createStatement(
|
||||
createAssignment(
|
||||
createElementAccess(
|
||||
currentNamespaceLocalName,
|
||||
createAssignment(
|
||||
createElementAccess(
|
||||
currentNamespaceLocalName,
|
||||
name
|
||||
),
|
||||
transformEnumMemberDeclarationValue(member)
|
||||
)
|
||||
),
|
||||
name
|
||||
),
|
||||
member
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the value of an enum member.
|
||||
*
|
||||
* @param member The enum member node.
|
||||
*/
|
||||
function transformEnumMemberDeclarationValue(member: EnumMember): Expression {
|
||||
const value = resolver.getConstantValue(member);
|
||||
if (value !== undefined) {
|
||||
return createLiteral(value);
|
||||
}
|
||||
else if (member.initializer) {
|
||||
return visitNode(member.initializer, visitor, isExpression);
|
||||
}
|
||||
else {
|
||||
return createVoidZero();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to elide an enum declaration.
|
||||
*
|
||||
* @param node The enum declaration node.
|
||||
*/
|
||||
function shouldElideEnumDeclaration(node: EnumDeclaration) {
|
||||
return isConst(node)
|
||||
&& !compilerOptions.preserveConstEnums
|
||||
&& !compilerOptions.isolatedModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits an await expression.
|
||||
*
|
||||
@ -2328,6 +2181,182 @@ namespace ts {
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a leading VariableStatement for an enum or module declaration.
|
||||
*/
|
||||
function addVarForEnumOrModuleDeclaration(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) {
|
||||
// Emit a variable statement for the enum.
|
||||
statements.push(
|
||||
createVariableStatement(
|
||||
visitNodes(node.modifiers, visitor, isModifier),
|
||||
[createVariableDeclaration(
|
||||
getDeclarationName(node)
|
||||
)],
|
||||
/*location*/ node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing VariableStatement for an enum or module declaration.
|
||||
*/
|
||||
function addVarForEnumOrModuleExportedFromNamespace(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) {
|
||||
statements.push(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
[createVariableDeclaration(
|
||||
getDeclarationName(node),
|
||||
getDeclarationNameExpression(node)
|
||||
)],
|
||||
/*location*/ node
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function addAssignmentForModuleExportedFromNamespace(statements: Statement[], node: ModuleDeclaration) {
|
||||
statements.push(
|
||||
createStatement(
|
||||
createAssignment(
|
||||
getDeclarationName(node),
|
||||
setNodeEmitFlags(getDeclarationName(node), NodeEmitFlags.PrefixExportedLocal)
|
||||
),
|
||||
/*location*/ node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to emit an enum declaration.
|
||||
*
|
||||
* @param node The enum declaration node.
|
||||
*/
|
||||
function shouldEmitEnumDeclaration(node: EnumDeclaration) {
|
||||
return !isConst(node)
|
||||
|| compilerOptions.preserveConstEnums
|
||||
|| compilerOptions.isolatedModules;
|
||||
}
|
||||
|
||||
function shouldEmitVarForEnumOrModuleDeclaration(node: EnumDeclaration | ModuleDeclaration) {
|
||||
return !hasModifier(node, ModifierFlags.Export)
|
||||
|| (isExternalModuleExport(node) && isFirstDeclarationOfKind(node, node.kind));
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits an enum declaration.
|
||||
*
|
||||
* This function will be called any time a TypeScript enum is encountered.
|
||||
*
|
||||
* @param node The enum declaration node.
|
||||
*/
|
||||
function visitEnumDeclaration(node: EnumDeclaration): VisitResult<Statement> {
|
||||
if (!shouldEmitEnumDeclaration(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const statements: Statement[] = [];
|
||||
if (shouldEmitVarForEnumOrModuleDeclaration(node)) {
|
||||
addVarForEnumOrModuleDeclaration(statements, node);
|
||||
}
|
||||
|
||||
const localName = getGeneratedNameForNode(node);
|
||||
const name = getDeclarationNameExpression(node);
|
||||
|
||||
// (function (x) {
|
||||
// x[x["y"] = 0] = "y";
|
||||
// ...
|
||||
// })(x || (x = {}));
|
||||
statements.push(
|
||||
setOriginalNode(
|
||||
createStatement(
|
||||
createCall(
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
[createParameter(localName)],
|
||||
transformEnumBody(node, localName)
|
||||
),
|
||||
[createLogicalOr(
|
||||
name,
|
||||
createAssignment(
|
||||
name,
|
||||
createObjectLiteral()
|
||||
)
|
||||
)]
|
||||
),
|
||||
/*location*/ node
|
||||
),
|
||||
/*original*/ node
|
||||
)
|
||||
);
|
||||
|
||||
if (isNamespaceExport(node)) {
|
||||
addVarForEnumOrModuleExportedFromNamespace(statements, node);
|
||||
}
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the body of an enum declaration.
|
||||
*
|
||||
* @param node The enum declaration node.
|
||||
*/
|
||||
function transformEnumBody(node: EnumDeclaration, localName: Identifier): Block {
|
||||
const savedCurrentNamespaceLocalName = currentNamespaceLocalName;
|
||||
currentNamespaceLocalName = localName;
|
||||
|
||||
const statements: Statement[] = [];
|
||||
startLexicalEnvironment();
|
||||
addNodes(statements, map(node.members, transformEnumMember));
|
||||
addNodes(statements, endLexicalEnvironment());
|
||||
|
||||
currentNamespaceLocalName = savedCurrentNamespaceLocalName;
|
||||
return createBlock(statements, /*location*/ undefined, /*multiLine*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an enum member into a statement.
|
||||
*
|
||||
* @param member The enum member node.
|
||||
*/
|
||||
function transformEnumMember(member: EnumMember): Statement {
|
||||
const name = getExpressionForPropertyName(member);
|
||||
return createStatement(
|
||||
createAssignment(
|
||||
createElementAccess(
|
||||
currentNamespaceLocalName,
|
||||
createAssignment(
|
||||
createElementAccess(
|
||||
currentNamespaceLocalName,
|
||||
name
|
||||
),
|
||||
transformEnumMemberDeclarationValue(member)
|
||||
)
|
||||
),
|
||||
name
|
||||
),
|
||||
member
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the value of an enum member.
|
||||
*
|
||||
* @param member The enum member node.
|
||||
*/
|
||||
function transformEnumMemberDeclarationValue(member: EnumMember): Expression {
|
||||
const value = resolver.getConstantValue(member);
|
||||
if (value !== undefined) {
|
||||
return createLiteral(value);
|
||||
}
|
||||
else if (member.initializer) {
|
||||
return visitNode(member.initializer, visitor, isExpression);
|
||||
}
|
||||
else {
|
||||
return createVoidZero();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to elide a module declaration.
|
||||
*
|
||||
@ -2337,20 +2366,9 @@ namespace ts {
|
||||
return isInstantiatedModule(node, compilerOptions.preserveConstEnums || compilerOptions.isolatedModules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a module declaration has a name that merges with a class declaration.
|
||||
*
|
||||
* @param node The module declaration node.
|
||||
*/
|
||||
function isModuleMergedWithClass(node: ModuleDeclaration) {
|
||||
function isModuleMergedWithES6Class(node: ModuleDeclaration) {
|
||||
return languageVersion === ScriptTarget.ES6
|
||||
&& !!(resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.LexicalModuleMergesWithClass);
|
||||
}
|
||||
|
||||
function shouldEmitVarForModuleDeclaration(node: ModuleDeclaration) {
|
||||
return !isModuleMergedWithClass(node)
|
||||
&& (!isExternalModuleExport(node)
|
||||
|| isFirstDeclarationOfKind(node, SyntaxKind.ModuleDeclaration));
|
||||
&& isMergedWithClass(node);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2365,41 +2383,29 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Debug.assert(isIdentifier(node.name));
|
||||
|
||||
Debug.assert(isIdentifier(node.name), "TypeScript module should have an Identifier name.");
|
||||
enableExpressionSubstitutionForNamespaceExports();
|
||||
|
||||
const savedCurrentNamespaceLocalName = currentNamespaceLocalName;
|
||||
const savedCurrentNamespace = currentNamespace;
|
||||
const statements: Statement[] = [];
|
||||
|
||||
if (shouldEmitVarForModuleDeclaration(node)) {
|
||||
// var x;
|
||||
statements.push(
|
||||
createVariableStatement(
|
||||
visitNodes(node.modifiers, visitor, isModifier),
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(<Identifier>node.name)
|
||||
]),
|
||||
/*location*/ node
|
||||
)
|
||||
);
|
||||
if (!isModuleMergedWithES6Class(node) && (isNamespaceExport(node) || shouldEmitVarForEnumOrModuleDeclaration(node))) {
|
||||
addVarForEnumOrModuleDeclaration(statements, node);
|
||||
}
|
||||
|
||||
const name = isNamespaceExport(node)
|
||||
? getNamespaceMemberName(node.name)
|
||||
: getSynthesizedClone(node.name);
|
||||
const localName = getGeneratedNameForNode(node);
|
||||
const name = getDeclarationNameExpression(node);
|
||||
|
||||
currentNamespaceLocalName = getGeneratedNameForNode(node);
|
||||
currentNamespace = node;
|
||||
|
||||
const moduleParam = createLogicalOr(
|
||||
name,
|
||||
createAssignment(
|
||||
let moduleArg =
|
||||
createLogicalOr(
|
||||
name,
|
||||
createObjectLiteral()
|
||||
)
|
||||
);
|
||||
createAssignment(
|
||||
name,
|
||||
createObjectLiteral()
|
||||
)
|
||||
);
|
||||
|
||||
if (isNamespaceExport(node)) {
|
||||
moduleArg = createAssignment(getDeclarationName(node), moduleArg);
|
||||
}
|
||||
|
||||
// (function (x_1) {
|
||||
// x_1.y = ...;
|
||||
@ -2409,30 +2415,22 @@ namespace ts {
|
||||
setOriginalNode(
|
||||
createStatement(
|
||||
createCall(
|
||||
createParen(
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
[createParameter(currentNamespaceLocalName)],
|
||||
transformModuleBody(node)
|
||||
)
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
[createParameter(localName)],
|
||||
transformModuleBody(node, localName)
|
||||
),
|
||||
[
|
||||
isNamespaceExport(node)
|
||||
? createAssignment(getSynthesizedClone(<Identifier>node.name), moduleParam)
|
||||
: moduleParam
|
||||
]
|
||||
[moduleArg]
|
||||
),
|
||||
/*location*/ node
|
||||
),
|
||||
node
|
||||
/*original*/ node
|
||||
),
|
||||
NodeEmitFlags.AdviseOnEmitNode
|
||||
)
|
||||
);
|
||||
|
||||
currentNamespaceLocalName = savedCurrentNamespaceLocalName;
|
||||
currentNamespace = savedCurrentNamespace;
|
||||
return statements;
|
||||
}
|
||||
|
||||
@ -2441,7 +2439,12 @@ namespace ts {
|
||||
*
|
||||
* @param node The module declaration node.
|
||||
*/
|
||||
function transformModuleBody(node: ModuleDeclaration): Block {
|
||||
function transformModuleBody(node: ModuleDeclaration, namespaceLocalName: Identifier): Block {
|
||||
const savedCurrentNamespaceLocalName = currentNamespaceLocalName;
|
||||
const savedCurrentNamespace = currentNamespace;
|
||||
currentNamespaceLocalName = namespaceLocalName;
|
||||
currentNamespace = node;
|
||||
|
||||
const statements: Statement[] = [];
|
||||
startLexicalEnvironment();
|
||||
const body = node.body;
|
||||
@ -2453,9 +2456,26 @@ namespace ts {
|
||||
}
|
||||
|
||||
addNodes(statements, endLexicalEnvironment());
|
||||
|
||||
currentNamespaceLocalName = savedCurrentNamespaceLocalName;
|
||||
currentNamespace = savedCurrentNamespace;
|
||||
return createBlock(statements, /*location*/ undefined, /*multiLine*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to emit an import equals declaration.
|
||||
*
|
||||
* @param node The import equals declaration node.
|
||||
*/
|
||||
function shouldEmitImportEqualsDeclaration(node: ImportEqualsDeclaration) {
|
||||
// preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when
|
||||
// - current file is not external module
|
||||
// - import declaration is top level and target is value imported by entity name
|
||||
return resolver.isReferencedAliasDeclaration(node)
|
||||
|| (!isExternalModule(currentSourceFile)
|
||||
&& resolver.isTopLevelValueImportEqualsWithEntityName(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits an import equals declaration.
|
||||
*
|
||||
@ -2466,7 +2486,7 @@ namespace ts {
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
if (shouldElideImportEqualsDeclaration(node)) {
|
||||
if (!shouldEmitImportEqualsDeclaration(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -2498,35 +2518,13 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to elide an import equals declaration.
|
||||
*
|
||||
* @param node The import equals declaration node.
|
||||
*/
|
||||
function shouldElideImportEqualsDeclaration(node: ImportEqualsDeclaration) {
|
||||
// preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when
|
||||
// - current file is not external module
|
||||
// - import declaration is top level and target is value imported by entity name
|
||||
return !resolver.isReferencedAliasDeclaration(node)
|
||||
&& (isExternalModule(currentSourceFile) || !resolver.isTopLevelValueImportEqualsWithEntityName(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the node is exported.
|
||||
*
|
||||
* @param node The node to test.
|
||||
*/
|
||||
function isExported(node: Node) {
|
||||
return hasModifier(node, ModifierFlags.Export);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the node is exported from a namespace.
|
||||
*
|
||||
* @param node The node to test.
|
||||
*/
|
||||
function isNamespaceExport(node: Node) {
|
||||
return currentNamespace !== undefined && isExported(node);
|
||||
return currentNamespace !== undefined && hasModifier(node, ModifierFlags.Export);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2535,7 +2533,7 @@ namespace ts {
|
||||
* @param node The node to test.
|
||||
*/
|
||||
function isExternalModuleExport(node: Node) {
|
||||
return currentNamespace === undefined && isExported(node);
|
||||
return currentNamespace === undefined && hasModifier(node, ModifierFlags.Export);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2557,16 +2555,6 @@ namespace ts {
|
||||
return isExternalModuleExport(node)
|
||||
&& hasModifier(node, ModifierFlags.Default);
|
||||
}
|
||||
/**
|
||||
* Gets a value indicating whether a node is the first declaration of its kind.
|
||||
*
|
||||
* @param node A Declaration node.
|
||||
* @param kind The SyntaxKind to find among related declarations.
|
||||
*/
|
||||
function isFirstDeclarationOfKind(node: Declaration, kind: SyntaxKind) {
|
||||
const original = getOriginalNode(node);
|
||||
return original.symbol && getDeclarationOfKind(original.symbol, kind) === original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a statement for the provided expression. This is used in calls to `map`.
|
||||
@ -2575,8 +2563,8 @@ namespace ts {
|
||||
return createStatement(expression, /*location*/ undefined);
|
||||
}
|
||||
|
||||
function addNamespaceExport(statements: Statement[], exportName: Identifier, exportValue: Expression, location?: TextRange) {
|
||||
statements.push(createNamespaceExport(exportName, exportValue, location));
|
||||
function addExportMemberAssignment(statements: Statement[], node: DeclarationStatement) {
|
||||
statements.push(createNamespaceExport(getDeclarationName(node), getDeclarationName(node)));
|
||||
}
|
||||
|
||||
function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) {
|
||||
@ -2601,10 +2589,23 @@ namespace ts {
|
||||
return createPropertyAccess(currentNamespaceLocalName, getSynthesizedClone(name));
|
||||
}
|
||||
|
||||
function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration | EnumDeclaration) {
|
||||
function getDeclarationName(node: DeclarationStatement | ClassExpression) {
|
||||
return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node);
|
||||
}
|
||||
|
||||
function getDeclarationNameExpression(node: DeclarationStatement) {
|
||||
const name = getDeclarationName(node);
|
||||
if (isNamespaceExport(node)) {
|
||||
return getNamespaceMemberName(name);
|
||||
}
|
||||
else {
|
||||
// We set the "PrefixExportedLocal" flag to indicate to any module transformer
|
||||
// downstream that any `exports.` prefix should be added.
|
||||
setNodeEmitFlags(name, getNodeEmitFlags(name) | NodeEmitFlags.PrefixExportedLocal);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
function getClassPrototype(node: ClassExpression | ClassDeclaration) {
|
||||
return createPropertyAccess(getDeclarationName(node), "prototype");
|
||||
}
|
||||
|
||||
@ -1924,7 +1924,7 @@ namespace ts {
|
||||
/* @internal */
|
||||
export interface EmitResolver {
|
||||
hasGlobalName(name: string): boolean;
|
||||
getReferencedExportContainer(node: Identifier): SourceFile | ModuleDeclaration | EnumDeclaration;
|
||||
getReferencedExportContainer(node: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration;
|
||||
getReferencedImportDeclaration(node: Identifier): Declaration;
|
||||
getReferencedDeclarationWithCollidingName(node: Identifier): Declaration;
|
||||
isDeclarationWithCollidingName(node: Declaration): boolean;
|
||||
@ -2856,6 +2856,7 @@ namespace ts {
|
||||
CapturesThis = 1 << 11, // The function captures a lexical `this`
|
||||
NoSourceMap = 1 << 12, // Do not emit a source map location for this node.
|
||||
NoNestedSourceMaps = 1 << 13, // Do not emit source map locations for children of this node.
|
||||
PrefixExportedLocal = 1 << 14,
|
||||
}
|
||||
|
||||
/** Additional context provided to `visitEachChild` */
|
||||
|
||||
@ -3034,6 +3034,31 @@ namespace ts {
|
||||
return node.initializer !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether a node is merged with a class declaration in the same scope.
|
||||
*/
|
||||
export function isMergedWithClass(node: Node) {
|
||||
if (node.symbol) {
|
||||
for (const declaration of node.symbol.declarations) {
|
||||
if (declaration.kind === SyntaxKind.ClassDeclaration && declaration !== node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether a node is the first declaration of its kind.
|
||||
*
|
||||
* @param node A Declaration node.
|
||||
* @param kind The SyntaxKind to find among related declarations.
|
||||
*/
|
||||
export function isFirstDeclarationOfKind(node: Node, kind: SyntaxKind) {
|
||||
return node.symbol && getDeclarationOfKind(node.symbol, kind) === node;
|
||||
}
|
||||
|
||||
// Node tests
|
||||
//
|
||||
// All node tests in the following list should *not* reference parent pointers so that
|
||||
|
||||
@ -21,7 +21,7 @@ System.register([], function (exports_1, context_1) {
|
||||
// filename: instantiatedModule.ts
|
||||
(function (M) {
|
||||
var x = 1;
|
||||
})(M = M || (M = {}));
|
||||
})(M || (M = {}));
|
||||
exports_1("M", M);
|
||||
}
|
||||
};
|
||||
|
||||
@ -30,7 +30,7 @@ System.register([], function (exports_1, context_1) {
|
||||
exports_1("TopLevelClass", TopLevelClass);
|
||||
(function (TopLevelModule) {
|
||||
var v;
|
||||
})(TopLevelModule = TopLevelModule || (TopLevelModule = {}));
|
||||
})(TopLevelModule || (TopLevelModule = {}));
|
||||
exports_1("TopLevelModule", TopLevelModule);
|
||||
(function (TopLevelEnum) {
|
||||
TopLevelEnum[TopLevelEnum["E"] = 0] = "E";
|
||||
@ -53,7 +53,7 @@ System.register([], function (exports_1, context_1) {
|
||||
NonTopLevelEnum[NonTopLevelEnum["E"] = 0] = "E";
|
||||
})(TopLevelModule2.NonTopLevelEnum || (TopLevelModule2.NonTopLevelEnum = {}));
|
||||
var NonTopLevelEnum = TopLevelModule2.NonTopLevelEnum;
|
||||
})(TopLevelModule2 = TopLevelModule2 || (TopLevelModule2 = {}));
|
||||
})(TopLevelModule2 || (TopLevelModule2 = {}));
|
||||
exports_1("TopLevelModule2", TopLevelModule2);
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user