From 686c94cd672f0b3ff42f3efffdcdaf91e4850220 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 23 Oct 2015 17:14:51 -0700 Subject: [PATCH] Added support for System modules. --- Jakefile.js | 16 +- scripts/processTypes/processTypes.ts | 10 +- src/compiler/binder.ts | 9 +- src/compiler/checker.ts | 8 - src/compiler/core.ts | 3 +- src/compiler/emitter.ts | 20 +- src/compiler/factory.generated.ts | 470 ++- src/compiler/factory.ts | 160 +- src/compiler/parser.ts | 51 +- src/compiler/printer.ts | 3779 ++++++++++++---------- src/compiler/transform.ts | 242 +- src/compiler/transforms/destructuring.ts | 278 ++ src/compiler/transforms/es6.ts | 169 +- src/compiler/transforms/module/es6.ts | 11 + src/compiler/transforms/module/module.ts | 706 ++++ src/compiler/transforms/module/system.ts | 1156 +++++++ src/compiler/transforms/ts.ts | 35 +- src/compiler/tsconfig.json | 7 +- src/compiler/types.ts | 399 ++- src/compiler/utilities.ts | 38 +- 20 files changed, 5231 insertions(+), 2336 deletions(-) create mode 100644 src/compiler/transforms/destructuring.ts create mode 100644 src/compiler/transforms/module/es6.ts create mode 100644 src/compiler/transforms/module/module.ts create mode 100644 src/compiler/transforms/module/system.ts diff --git a/Jakefile.js b/Jakefile.js index 990c8fbdede..56886ecc211 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -44,8 +44,12 @@ var compilerSources = [ "binder.ts", "checker.ts", "transform.ts", - "transforms/jsx.ts", + "transforms/destructuring.ts", "transforms/ts.ts", + "transforms/module/module.ts", + "transforms/module/system.ts", + "transforms/module/es6.ts", + "transforms/jsx.ts", "transforms/es6.ts", "declarationEmitter.ts", "printer.ts", @@ -70,8 +74,12 @@ var servicesSources = [ "binder.ts", "checker.ts", "transform.ts", - "transforms/jsx.ts", + "transforms/destructuring.ts", "transforms/ts.ts", + "transforms/module/module.ts", + "transforms/module/system.ts", + "transforms/module/es6.ts", + "transforms/jsx.ts", "transforms/es6.ts", "declarationEmitter.ts", "printer.ts", @@ -954,7 +962,7 @@ var tslintRulesOutFiles = tslintRules.map(function(p) { desc("Compiles tslint rules to js"); task("build-rules", tslintRulesOutFiles); tslintRulesFiles.forEach(function(ruleFile, i) { - compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, /*noOutFile*/ true, /*generateDeclarations*/ false, path.join(builtLocalDirectory, "tslint")); + compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, /*noOutFile*/ true, /*generateDeclarations*/ false, path.join(builtLocalDirectory, "tslint")); }); function getLinterOptions() { @@ -1014,7 +1022,7 @@ function lintWatchFile(filename) { if (event !== "change") { return; } - + if (!lintSemaphores[filename]) { lintSemaphores[filename] = true; lintFileAsync(getLinterOptions(), filename, function(err, result) { diff --git a/scripts/processTypes/processTypes.ts b/scripts/processTypes/processTypes.ts index 703e9c4d50b..814ed96612e 100644 --- a/scripts/processTypes/processTypes.ts +++ b/scripts/processTypes/processTypes.ts @@ -73,11 +73,17 @@ namespace ts {${each(discovery.createableNodes, syntaxNode => ` export function cloneNode(node: TNode, location?: TextRange, flags?: NodeFlags): TNode; export function cloneNode(node: Node, location?: TextRange, flags: NodeFlags = node.flags): Node { if (node) { + let clone: Node; switch (node.kind) {${each(discovery.createableNodes, syntaxNode => ` case SyntaxKind.${syntaxNode.kindName}: - return ${syntaxNode.createFunctionName}(${each(syntaxNode.createParameters, member => + clone = ${syntaxNode.createFunctionName}(${each(syntaxNode.createParameters, member => `(<${syntaxNode.typeName}>node).${member.propertyName}, ` - )}location, flags);`)} + )}location, flags); + break;`)} + } + if (clone) { + clone.original = node; + return clone; } } return node; diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3a2d3dd903e..a55efc8ff2d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1242,8 +1242,11 @@ namespace ts { // & ~(node.excludeTransformFlags = TransformFlags.CallOrArrayLiteralExcludes); case SyntaxKind.ExpressionStatement: - if (node.flags & NodeFlags.GeneratedSuper) { - transformFlags |= TransformFlags.ThisNodeIsES6; + if (node.flags & NodeFlags.Generated) { + let expression = (node).expression; + if (isCallExpression(expression) && isSuperKeyword(expression.expression)) { + transformFlags |= TransformFlags.ThisNodeIsES6; + } } break; @@ -1465,7 +1468,7 @@ namespace ts { // break; case SyntaxKind.ImportEqualsDeclaration: - // An ImportEqualsDeclaration with a namespace reference is TypeScript. + // An ImportEqualsDeclaration is TypeScriptwith a namespace reference is TypeScript. if (!isExternalModuleImportEqualsDeclaration(node)) { transformFlags |= TransformFlags.ThisNodeIsTypeScript; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7cbe661a14f..122f9714460 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -169,7 +169,6 @@ namespace ts { let emitParam = false; let emitAwaiter = false; let emitGenerator = false; - let emitExportStar = false; let resolutionTargets: TypeSystemEntity[] = []; let resolutionResults: boolean[] = []; @@ -13701,8 +13700,6 @@ namespace ts { if (moduleSymbol && moduleSymbol.exports["export="]) { error(node.moduleSpecifier, Diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, symbolToString(moduleSymbol)); } - - emitExportStar = true; } } } @@ -14055,7 +14052,6 @@ namespace ts { emitParam = false; emitAwaiter = false; emitGenerator = false; - emitExportStar = false; potentialThisCollisions.length = 0; forEach(node.statements, checkSourceElement); @@ -14090,10 +14086,6 @@ namespace ts { links.flags |= NodeCheckFlags.EmitGenerator; } - if (emitExportStar) { - links.flags |= NodeCheckFlags.EmitExportStar; - } - links.flags |= NodeCheckFlags.TypeChecked; } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 1ccf5c481a0..fc3f7028da7 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -832,7 +832,7 @@ namespace ts { /** * List of extensions that will be used to look for external modules. * This list is kept separate from supportedExtensions to for cases when we'll allow to include .js files in compilation, - * but still would like to load only TypeScript files as modules + * but still would like to load only TypeScript files as modules */ export const moduleFileExtensions = supportedExtensions; @@ -1163,6 +1163,7 @@ namespace ts { verboseDebugString = "\r\nVerbose Debug Information: " + verboseDebugInfo(); } + debugger; throw new Error("Debug Failure. False expression: " + (message || "") + verboseDebugString); } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 53cd5eb2184..9cda785b449 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1,7 +1,7 @@ /// -/// /// -/// +/// +/// /* @internal */ namespace ts { @@ -328,13 +328,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let newLine = host.getNewLine(); let jsxDesugaring = host.getCompilerOptions().jsx !== JsxEmit.Preserve; let shouldEmitJsx = (s: SourceFile) => (s.languageVariant === LanguageVariant.JSX && !jsxDesugaring); - let transformationChain = getTransformationChain(compilerOptions); - let sourceFiles: SourceFile[]; - let substitutions: TransformationSubstitutions; if (targetSourceFile === undefined) { - ({ sourceFiles, substitutions } = transformFilesIfNeeded(resolver, host, host.getSourceFiles(), transformationChain)); - forEach(sourceFiles, sourceFile => { + forEach(host.getSourceFiles(), sourceFile => { if (shouldEmitToOwnFile(sourceFile, compilerOptions)) { let jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, shouldEmitJsx(sourceFile) ? ".jsx" : ".js"); emitFile(jsFilePath, sourceFile); @@ -349,11 +345,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // targetSourceFile is specified (e.g calling emitter from language service or calling getSemanticDiagnostic from language service) if (shouldEmitToOwnFile(targetSourceFile, compilerOptions)) { let jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, host, shouldEmitJsx(targetSourceFile) ? ".jsx" : ".js"); - ({ sourceFiles: [targetSourceFile], substitutions } = transformFilesIfNeeded(resolver, host, [targetSourceFile], transformationChain)); emitFile(jsFilePath, targetSourceFile); } else if (!isDeclarationFile(targetSourceFile) && (compilerOptions.outFile || compilerOptions.out)) { - ({ sourceFiles, substitutions } = transformFilesIfNeeded(resolver, host, host.getSourceFiles(), transformationChain)); emitFile(compilerOptions.outFile || compilerOptions.out); } } @@ -7044,7 +7038,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } - function emitDirectivePrologues(statements: Statement[], startWithNewLine: boolean): number { + function emitDirectivePrologues(statements: Node[], startWithNewLine: boolean): number { for (let i = 0; i < statements.length; ++i) { if (isPrologueDirective(statements[i])) { if (startWithNewLine || i > 0) { @@ -7609,8 +7603,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitFile(jsFilePath: string, sourceFile?: SourceFile) { if (compilerOptions.experimentalTransforms) { - let nodes = sourceFile ? [sourceFile] : sourceFiles; - let text = printNodes(resolver, substitutions, host, filter(nodes, isNonDeclarationFile)); + let nodes = sourceFile ? [sourceFile] : host.getSourceFiles(); + let text = printFiles(resolver, host, filter(nodes, isNonDeclarationFile), getTransformationChain(compilerOptions)); writeFile(host, diagnostics, jsFilePath, text, compilerOptions.emitBOM); } else { @@ -7618,7 +7612,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } if (compilerOptions.declaration) { - writeDeclarationFile(jsFilePath, getOriginalNodeIf(sourceFile, isSourceFile), host, resolver, diagnostics); + writeDeclarationFile(jsFilePath, sourceFile, host, resolver, diagnostics); } } } diff --git a/src/compiler/factory.generated.ts b/src/compiler/factory.generated.ts index d9439189542..8af35964dcb 100644 --- a/src/compiler/factory.generated.ts +++ b/src/compiler/factory.generated.ts @@ -788,11 +788,9 @@ namespace ts { if (initializer) node.initializer = initializer; return node; } - export function createShorthandPropertyAssignment(name?: Identifier, equalsToken?: Node, objectAssignmentInitializer?: Expression, location?: TextRange, flags?: NodeFlags): ShorthandPropertyAssignment { + export function createShorthandPropertyAssignment(name?: Identifier, location?: TextRange, flags?: NodeFlags): ShorthandPropertyAssignment { let node = createNode(SyntaxKind.ShorthandPropertyAssignment, location, flags); if (name) node.name = name; - if (equalsToken) node.equalsToken = equalsToken; - if (objectAssignmentInitializer) node.objectAssignmentInitializer = objectAssignmentInitializer; return node; } export function createEnumMember(name?: DeclarationName, initializer?: Expression, location?: TextRange, flags?: NodeFlags): EnumMember { @@ -1699,9 +1697,9 @@ namespace ts { } return node; } - export function updateShorthandPropertyAssignment(node: ShorthandPropertyAssignment, name: Identifier, equalsToken: Node, objectAssignmentInitializer: Expression): ShorthandPropertyAssignment { - if (name !== node.name || equalsToken !== node.equalsToken || objectAssignmentInitializer !== node.objectAssignmentInitializer) { - let newNode = createShorthandPropertyAssignment(name, equalsToken, objectAssignmentInitializer); + export function updateShorthandPropertyAssignment(node: ShorthandPropertyAssignment, name: Identifier): ShorthandPropertyAssignment { + if (name !== node.name) { + let newNode = createShorthandPropertyAssignment(name); return updateFrom(node, newNode); } return node; @@ -2854,309 +2852,465 @@ namespace ts { export function cloneNode(node: TNode, location?: TextRange, flags?: NodeFlags): TNode; export function cloneNode(node: Node, location?: TextRange, flags: NodeFlags = node.flags): Node { if (node) { + let clone: Node; switch (node.kind) { case SyntaxKind.NumericLiteral: - return createNumericLiteral((node).text, location, flags); + clone = createNumericLiteral((node).text, location, flags); + break; case SyntaxKind.StringLiteral: - return createStringLiteral((node).text, location, flags); + clone = createStringLiteral((node).text, location, flags); + break; case SyntaxKind.RegularExpressionLiteral: - return createRegularExpressionLiteral((node).text, location, flags); + clone = createRegularExpressionLiteral((node).text, location, flags); + break; case SyntaxKind.NoSubstitutionTemplateLiteral: - return createNoSubstitutionTemplateLiteral((node).text, location, flags); + clone = createNoSubstitutionTemplateLiteral((node).text, location, flags); + break; case SyntaxKind.TemplateHead: - return createTemplateHead((node).text, location, flags); + clone = createTemplateHead((node).text, location, flags); + break; case SyntaxKind.TemplateMiddle: - return createTemplateMiddle((node).text, location, flags); + clone = createTemplateMiddle((node).text, location, flags); + break; case SyntaxKind.TemplateTail: - return createTemplateTail((node).text, location, flags); + clone = createTemplateTail((node).text, location, flags); + break; case SyntaxKind.Identifier: - return createIdentifier((node).text, (node).originalKeywordKind, location, flags); + clone = createIdentifier((node).text, (node).originalKeywordKind, location, flags); + break; case SyntaxKind.FalseKeyword: - return createFalseKeyword(location, flags); + clone = createFalseKeyword(location, flags); + break; case SyntaxKind.NullKeyword: - return createNullKeyword(location, flags); + clone = createNullKeyword(location, flags); + break; case SyntaxKind.SuperKeyword: - return createSuperKeyword(location, flags); + clone = createSuperKeyword(location, flags); + break; case SyntaxKind.ThisKeyword: - return createThisKeyword(location, flags); + clone = createThisKeyword(location, flags); + break; case SyntaxKind.TrueKeyword: - return createTrueKeyword(location, flags); + clone = createTrueKeyword(location, flags); + break; case SyntaxKind.QualifiedName: - return createQualifiedName((node).left, (node).right, location, flags); + clone = createQualifiedName((node).left, (node).right, location, flags); + break; case SyntaxKind.ComputedPropertyName: - return createComputedPropertyName((node).expression, location, flags); + clone = createComputedPropertyName((node).expression, location, flags); + break; case SyntaxKind.TypeParameter: - return createTypeParameter((node).name, (node).constraint, (node).expression, location, flags); + clone = createTypeParameter((node).name, (node).constraint, (node).expression, location, flags); + break; case SyntaxKind.Parameter: - return createParameter((node).decorators, (node).modifiers, (node).dotDotDotToken, (node).name, (node).questionToken, (node).type, (node).initializer, location, flags); + clone = createParameter((node).decorators, (node).modifiers, (node).dotDotDotToken, (node).name, (node).questionToken, (node).type, (node).initializer, location, flags); + break; case SyntaxKind.Decorator: - return createDecorator((node).expression, location, flags); + clone = createDecorator((node).expression, location, flags); + break; case SyntaxKind.PropertySignature: - return createPropertySignature((node).decorators, (node).modifiers, (node).name, (node).questionToken, (node).type, location, flags); + clone = createPropertySignature((node).decorators, (node).modifiers, (node).name, (node).questionToken, (node).type, location, flags); + break; case SyntaxKind.PropertyDeclaration: - return createPropertyDeclaration((node).decorators, (node).modifiers, (node).name, (node).type, (node).initializer, location, flags); + clone = createPropertyDeclaration((node).decorators, (node).modifiers, (node).name, (node).type, (node).initializer, location, flags); + break; case SyntaxKind.MethodSignature: - return createMethodSignature((node).decorators, (node).modifiers, (node).name, (node).questionToken, (node).typeParameters, (node).parameters, (node).type, location, flags); + clone = createMethodSignature((node).decorators, (node).modifiers, (node).name, (node).questionToken, (node).typeParameters, (node).parameters, (node).type, location, flags); + break; case SyntaxKind.MethodDeclaration: - return createMethodDeclaration((node).decorators, (node).modifiers, (node).asteriskToken, (node).name, (node).typeParameters, (node).parameters, (node).type, (node).body, location, flags); + clone = createMethodDeclaration((node).decorators, (node).modifiers, (node).asteriskToken, (node).name, (node).typeParameters, (node).parameters, (node).type, (node).body, location, flags); + break; case SyntaxKind.Constructor: - return createConstructor((node).decorators, (node).modifiers, (node).parameters, (node).type, (node).body, location, flags); + clone = createConstructor((node).decorators, (node).modifiers, (node).parameters, (node).type, (node).body, location, flags); + break; case SyntaxKind.GetAccessor: - return createGetAccessor((node).decorators, (node).modifiers, (node).name, (node).parameters, (node).type, (node).body, location, flags); + clone = createGetAccessor((node).decorators, (node).modifiers, (node).name, (node).parameters, (node).type, (node).body, location, flags); + break; case SyntaxKind.SetAccessor: - return createSetAccessor((node).decorators, (node).modifiers, (node).name, (node).parameters, (node).type, (node).body, location, flags); + clone = createSetAccessor((node).decorators, (node).modifiers, (node).name, (node).parameters, (node).type, (node).body, location, flags); + break; case SyntaxKind.CallSignature: - return createCallSignature((node).typeParameters, (node).parameters, (node).type, (node).questionToken, location, flags); + clone = createCallSignature((node).typeParameters, (node).parameters, (node).type, (node).questionToken, location, flags); + break; case SyntaxKind.ConstructSignature: - return createConstructSignature((node).typeParameters, (node).parameters, (node).type, (node).questionToken, location, flags); + clone = createConstructSignature((node).typeParameters, (node).parameters, (node).type, (node).questionToken, location, flags); + break; case SyntaxKind.IndexSignature: - return createIndexSignature((node).decorators, (node).modifiers, (node).parameters, (node).type, (node).questionToken, location, flags); + clone = createIndexSignature((node).decorators, (node).modifiers, (node).parameters, (node).type, (node).questionToken, location, flags); + break; case SyntaxKind.TypePredicate: - return createTypePredicate((node).parameterName, (node).type, location, flags); + clone = createTypePredicate((node).parameterName, (node).type, location, flags); + break; case SyntaxKind.TypeReference: - return createTypeReference((node).typeName, (node).typeArguments, location, flags); + clone = createTypeReference((node).typeName, (node).typeArguments, location, flags); + break; case SyntaxKind.FunctionType: - return createFunctionType((node).typeParameters, (node).parameters, (node).type, location, flags); + clone = createFunctionType((node).typeParameters, (node).parameters, (node).type, location, flags); + break; case SyntaxKind.ConstructorType: - return createConstructorType((node).typeParameters, (node).parameters, (node).type, location, flags); + clone = createConstructorType((node).typeParameters, (node).parameters, (node).type, location, flags); + break; case SyntaxKind.TypeQuery: - return createTypeQuery((node).exprName, location, flags); + clone = createTypeQuery((node).exprName, location, flags); + break; case SyntaxKind.TypeLiteral: - return createTypeLiteral((node).members, location, flags); + clone = createTypeLiteral((node).members, location, flags); + break; case SyntaxKind.ArrayType: - return createArrayType((node).elementType, location, flags); + clone = createArrayType((node).elementType, location, flags); + break; case SyntaxKind.TupleType: - return createTupleType((node).elementTypes, location, flags); + clone = createTupleType((node).elementTypes, location, flags); + break; case SyntaxKind.UnionType: - return createUnionType((node).types, location, flags); + clone = createUnionType((node).types, location, flags); + break; case SyntaxKind.IntersectionType: - return createIntersectionType((node).types, location, flags); + clone = createIntersectionType((node).types, location, flags); + break; case SyntaxKind.ParenthesizedType: - return createParenthesizedType((node).type, location, flags); + clone = createParenthesizedType((node).type, location, flags); + break; case SyntaxKind.ObjectBindingPattern: - return createObjectBindingPattern((node).elements, location, flags); + clone = createObjectBindingPattern((node).elements, location, flags); + break; case SyntaxKind.ArrayBindingPattern: - return createArrayBindingPattern((node).elements, location, flags); + clone = createArrayBindingPattern((node).elements, location, flags); + break; case SyntaxKind.BindingElement: - return createBindingElement((node).propertyName, (node).dotDotDotToken, (node).name, (node).initializer, location, flags); + clone = createBindingElement((node).propertyName, (node).dotDotDotToken, (node).name, (node).initializer, location, flags); + break; case SyntaxKind.ArrayLiteralExpression: - return createArrayLiteralExpression((node).elements, location, flags); + clone = createArrayLiteralExpression((node).elements, location, flags); + break; case SyntaxKind.ObjectLiteralExpression: - return createObjectLiteralExpression((node).properties, location, flags); + clone = createObjectLiteralExpression((node).properties, location, flags); + break; case SyntaxKind.PropertyAccessExpression: - return createPropertyAccessExpression((node).expression, (node).dotToken, (node).name, location, flags); + clone = createPropertyAccessExpression((node).expression, (node).dotToken, (node).name, location, flags); + break; case SyntaxKind.ElementAccessExpression: - return createElementAccessExpression((node).expression, (node).argumentExpression, location, flags); + clone = createElementAccessExpression((node).expression, (node).argumentExpression, location, flags); + break; case SyntaxKind.CallExpression: - return createCallExpression((node).expression, (node).typeArguments, (node).arguments, location, flags); + clone = createCallExpression((node).expression, (node).typeArguments, (node).arguments, location, flags); + break; case SyntaxKind.NewExpression: - return createNewExpression((node).expression, (node).typeArguments, (node).arguments, location, flags); + clone = createNewExpression((node).expression, (node).typeArguments, (node).arguments, location, flags); + break; case SyntaxKind.TaggedTemplateExpression: - return createTaggedTemplateExpression((node).tag, (node).template, location, flags); + clone = createTaggedTemplateExpression((node).tag, (node).template, location, flags); + break; case SyntaxKind.TypeAssertionExpression: - return createTypeAssertionExpression((node).type, (node).expression, location, flags); + clone = createTypeAssertionExpression((node).type, (node).expression, location, flags); + break; case SyntaxKind.ParenthesizedExpression: - return createParenthesizedExpression((node).expression, location, flags); + clone = createParenthesizedExpression((node).expression, location, flags); + break; case SyntaxKind.FunctionExpression: - return createFunctionExpression((node).decorators, (node).modifiers, (node).asteriskToken, (node).name, (node).typeParameters, (node).parameters, (node).type, (node).body, location, flags); + clone = createFunctionExpression((node).decorators, (node).modifiers, (node).asteriskToken, (node).name, (node).typeParameters, (node).parameters, (node).type, (node).body, location, flags); + break; case SyntaxKind.ArrowFunction: - return createArrowFunction((node).decorators, (node).modifiers, (node).typeParameters, (node).parameters, (node).type, (node).equalsGreaterThanToken, (node).body, location, flags); + clone = createArrowFunction((node).decorators, (node).modifiers, (node).typeParameters, (node).parameters, (node).type, (node).equalsGreaterThanToken, (node).body, location, flags); + break; case SyntaxKind.DeleteExpression: - return createDeleteExpression((node).expression, location, flags); + clone = createDeleteExpression((node).expression, location, flags); + break; case SyntaxKind.TypeOfExpression: - return createTypeOfExpression((node).expression, location, flags); + clone = createTypeOfExpression((node).expression, location, flags); + break; case SyntaxKind.VoidExpression: - return createVoidExpression((node).expression, location, flags); + clone = createVoidExpression((node).expression, location, flags); + break; case SyntaxKind.AwaitExpression: - return createAwaitExpression((node).expression, location, flags); + clone = createAwaitExpression((node).expression, location, flags); + break; case SyntaxKind.PrefixUnaryExpression: - return createPrefixUnaryExpression((node).operator, (node).operand, location, flags); + clone = createPrefixUnaryExpression((node).operator, (node).operand, location, flags); + break; case SyntaxKind.PostfixUnaryExpression: - return createPostfixUnaryExpression((node).operand, (node).operator, location, flags); + clone = createPostfixUnaryExpression((node).operand, (node).operator, location, flags); + break; case SyntaxKind.BinaryExpression: - return createBinaryExpression((node).left, (node).operatorToken, (node).right, location, flags); + clone = createBinaryExpression((node).left, (node).operatorToken, (node).right, location, flags); + break; case SyntaxKind.ConditionalExpression: - return createConditionalExpression((node).condition, (node).questionToken, (node).whenTrue, (node).colonToken, (node).whenFalse, location, flags); + clone = createConditionalExpression((node).condition, (node).questionToken, (node).whenTrue, (node).colonToken, (node).whenFalse, location, flags); + break; case SyntaxKind.TemplateExpression: - return createTemplateExpression((node).head, (node).templateSpans, location, flags); + clone = createTemplateExpression((node).head, (node).templateSpans, location, flags); + break; case SyntaxKind.YieldExpression: - return createYieldExpression((node).asteriskToken, (node).expression, location, flags); + clone = createYieldExpression((node).asteriskToken, (node).expression, location, flags); + break; case SyntaxKind.SpreadElementExpression: - return createSpreadElementExpression((node).expression, location, flags); + clone = createSpreadElementExpression((node).expression, location, flags); + break; case SyntaxKind.ClassExpression: - return createClassExpression((node).decorators, (node).modifiers, (node).name, (node).typeParameters, (node).heritageClauses, (node).members, location, flags); + clone = createClassExpression((node).decorators, (node).modifiers, (node).name, (node).typeParameters, (node).heritageClauses, (node).members, location, flags); + break; case SyntaxKind.OmittedExpression: - return createOmittedExpression(location, flags); + clone = createOmittedExpression(location, flags); + break; case SyntaxKind.ExpressionWithTypeArguments: - return createExpressionWithTypeArguments((node).expression, (node).typeArguments, location, flags); + clone = createExpressionWithTypeArguments((node).expression, (node).typeArguments, location, flags); + break; case SyntaxKind.AsExpression: - return createAsExpression((node).expression, (node).type, location, flags); + clone = createAsExpression((node).expression, (node).type, location, flags); + break; case SyntaxKind.TemplateSpan: - return createTemplateSpan((node).expression, (node).literal, location, flags); + clone = createTemplateSpan((node).expression, (node).literal, location, flags); + break; case SyntaxKind.SemicolonClassElement: - return createSemicolonClassElement(location, flags); + clone = createSemicolonClassElement(location, flags); + break; case SyntaxKind.Block: - return createBlock((node).statements, location, flags); + clone = createBlock((node).statements, location, flags); + break; case SyntaxKind.VariableStatement: - return createVariableStatement((node).decorators, (node).modifiers, (node).declarationList, location, flags); + clone = createVariableStatement((node).decorators, (node).modifiers, (node).declarationList, location, flags); + break; case SyntaxKind.EmptyStatement: - return createEmptyStatement(location, flags); + clone = createEmptyStatement(location, flags); + break; case SyntaxKind.ExpressionStatement: - return createExpressionStatement((node).expression, location, flags); + clone = createExpressionStatement((node).expression, location, flags); + break; case SyntaxKind.IfStatement: - return createIfStatement((node).expression, (node).thenStatement, (node).elseStatement, location, flags); + clone = createIfStatement((node).expression, (node).thenStatement, (node).elseStatement, location, flags); + break; case SyntaxKind.DoStatement: - return createDoStatement((node).statement, (node).expression, location, flags); + clone = createDoStatement((node).statement, (node).expression, location, flags); + break; case SyntaxKind.WhileStatement: - return createWhileStatement((node).expression, (node).statement, location, flags); + clone = createWhileStatement((node).expression, (node).statement, location, flags); + break; case SyntaxKind.ForStatement: - return createForStatement((node).initializer, (node).condition, (node).incrementor, (node).statement, location, flags); + clone = createForStatement((node).initializer, (node).condition, (node).incrementor, (node).statement, location, flags); + break; case SyntaxKind.ForInStatement: - return createForInStatement((node).initializer, (node).expression, (node).statement, location, flags); + clone = createForInStatement((node).initializer, (node).expression, (node).statement, location, flags); + break; case SyntaxKind.ForOfStatement: - return createForOfStatement((node).initializer, (node).expression, (node).statement, location, flags); + clone = createForOfStatement((node).initializer, (node).expression, (node).statement, location, flags); + break; case SyntaxKind.ContinueStatement: - return createContinueStatement((node).label, location, flags); + clone = createContinueStatement((node).label, location, flags); + break; case SyntaxKind.BreakStatement: - return createBreakStatement((node).label, location, flags); + clone = createBreakStatement((node).label, location, flags); + break; case SyntaxKind.ReturnStatement: - return createReturnStatement((node).expression, location, flags); + clone = createReturnStatement((node).expression, location, flags); + break; case SyntaxKind.WithStatement: - return createWithStatement((node).expression, (node).statement, location, flags); + clone = createWithStatement((node).expression, (node).statement, location, flags); + break; case SyntaxKind.SwitchStatement: - return createSwitchStatement((node).expression, (node).caseBlock, location, flags); + clone = createSwitchStatement((node).expression, (node).caseBlock, location, flags); + break; case SyntaxKind.LabeledStatement: - return createLabeledStatement((node).label, (node).statement, location, flags); + clone = createLabeledStatement((node).label, (node).statement, location, flags); + break; case SyntaxKind.ThrowStatement: - return createThrowStatement((node).expression, location, flags); + clone = createThrowStatement((node).expression, location, flags); + break; case SyntaxKind.TryStatement: - return createTryStatement((node).tryBlock, (node).catchClause, (node).finallyBlock, location, flags); + clone = createTryStatement((node).tryBlock, (node).catchClause, (node).finallyBlock, location, flags); + break; case SyntaxKind.DebuggerStatement: - return createDebuggerStatement(location, flags); + clone = createDebuggerStatement(location, flags); + break; case SyntaxKind.VariableDeclaration: - return createVariableDeclaration((node).name, (node).type, (node).initializer, location, flags); + clone = createVariableDeclaration((node).name, (node).type, (node).initializer, location, flags); + break; case SyntaxKind.VariableDeclarationList: - return createVariableDeclarationList((node).declarations, location, flags); + clone = createVariableDeclarationList((node).declarations, location, flags); + break; case SyntaxKind.FunctionDeclaration: - return createFunctionDeclaration((node).decorators, (node).modifiers, (node).asteriskToken, (node).name, (node).typeParameters, (node).parameters, (node).type, (node).body, location, flags); + clone = createFunctionDeclaration((node).decorators, (node).modifiers, (node).asteriskToken, (node).name, (node).typeParameters, (node).parameters, (node).type, (node).body, location, flags); + break; case SyntaxKind.ClassDeclaration: - return createClassDeclaration((node).decorators, (node).modifiers, (node).name, (node).typeParameters, (node).heritageClauses, (node).members, location, flags); + clone = createClassDeclaration((node).decorators, (node).modifiers, (node).name, (node).typeParameters, (node).heritageClauses, (node).members, location, flags); + break; case SyntaxKind.InterfaceDeclaration: - return createInterfaceDeclaration((node).decorators, (node).modifiers, (node).name, (node).typeParameters, (node).heritageClauses, (node).members, location, flags); + clone = createInterfaceDeclaration((node).decorators, (node).modifiers, (node).name, (node).typeParameters, (node).heritageClauses, (node).members, location, flags); + break; case SyntaxKind.TypeAliasDeclaration: - return createTypeAliasDeclaration((node).decorators, (node).modifiers, (node).name, (node).typeParameters, (node).type, location, flags); + clone = createTypeAliasDeclaration((node).decorators, (node).modifiers, (node).name, (node).typeParameters, (node).type, location, flags); + break; case SyntaxKind.EnumDeclaration: - return createEnumDeclaration((node).decorators, (node).modifiers, (node).name, (node).members, location, flags); + clone = createEnumDeclaration((node).decorators, (node).modifiers, (node).name, (node).members, location, flags); + break; case SyntaxKind.ModuleDeclaration: - return createModuleDeclaration((node).decorators, (node).modifiers, (node).name, (node).body, location, flags); + clone = createModuleDeclaration((node).decorators, (node).modifiers, (node).name, (node).body, location, flags); + break; case SyntaxKind.ModuleBlock: - return createModuleBlock((node).statements, location, flags); + clone = createModuleBlock((node).statements, location, flags); + break; case SyntaxKind.CaseBlock: - return createCaseBlock((node).clauses, location, flags); + clone = createCaseBlock((node).clauses, location, flags); + break; case SyntaxKind.ImportEqualsDeclaration: - return createImportEqualsDeclaration((node).decorators, (node).modifiers, (node).name, (node).moduleReference, location, flags); + clone = createImportEqualsDeclaration((node).decorators, (node).modifiers, (node).name, (node).moduleReference, location, flags); + break; case SyntaxKind.ImportDeclaration: - return createImportDeclaration((node).decorators, (node).modifiers, (node).importClause, (node).moduleSpecifier, location, flags); + clone = createImportDeclaration((node).decorators, (node).modifiers, (node).importClause, (node).moduleSpecifier, location, flags); + break; case SyntaxKind.ImportClause: - return createImportClause((node).name, (node).namedBindings, location, flags); + clone = createImportClause((node).name, (node).namedBindings, location, flags); + break; case SyntaxKind.NamespaceImport: - return createNamespaceImport((node).name, location, flags); + clone = createNamespaceImport((node).name, location, flags); + break; case SyntaxKind.NamedImports: - return createNamedImports((node).elements, location, flags); + clone = createNamedImports((node).elements, location, flags); + break; case SyntaxKind.ImportSpecifier: - return createImportSpecifier((node).propertyName, (node).name, location, flags); + clone = createImportSpecifier((node).propertyName, (node).name, location, flags); + break; case SyntaxKind.ExportAssignment: - return createExportAssignment((node).decorators, (node).modifiers, (node).expression, location, flags); + clone = createExportAssignment((node).decorators, (node).modifiers, (node).expression, location, flags); + break; case SyntaxKind.ExportDeclaration: - return createExportDeclaration((node).decorators, (node).modifiers, (node).exportClause, (node).moduleSpecifier, location, flags); + clone = createExportDeclaration((node).decorators, (node).modifiers, (node).exportClause, (node).moduleSpecifier, location, flags); + break; case SyntaxKind.NamedExports: - return createNamedExports((node).elements, location, flags); + clone = createNamedExports((node).elements, location, flags); + break; case SyntaxKind.ExportSpecifier: - return createExportSpecifier((node).propertyName, (node).name, location, flags); + clone = createExportSpecifier((node).propertyName, (node).name, location, flags); + break; case SyntaxKind.MissingDeclaration: - return createMissingDeclaration((node).decorators, (node).modifiers, (node).questionToken, location, flags); + clone = createMissingDeclaration((node).decorators, (node).modifiers, (node).questionToken, location, flags); + break; case SyntaxKind.ExternalModuleReference: - return createExternalModuleReference((node).expression, location, flags); + clone = createExternalModuleReference((node).expression, location, flags); + break; case SyntaxKind.JsxElement: - return createJsxElement((node).openingElement, (node).children, (node).closingElement, location, flags); + clone = createJsxElement((node).openingElement, (node).children, (node).closingElement, location, flags); + break; case SyntaxKind.JsxSelfClosingElement: - return createJsxSelfClosingElement((node).tagName, (node).attributes, location, flags); + clone = createJsxSelfClosingElement((node).tagName, (node).attributes, location, flags); + break; case SyntaxKind.JsxOpeningElement: - return createJsxOpeningElement((node).tagName, (node).attributes, location, flags); + clone = createJsxOpeningElement((node).tagName, (node).attributes, location, flags); + break; case SyntaxKind.JsxText: - return createJsxText(location, flags); + clone = createJsxText(location, flags); + break; case SyntaxKind.JsxClosingElement: - return createJsxClosingElement((node).tagName, location, flags); + clone = createJsxClosingElement((node).tagName, location, flags); + break; case SyntaxKind.JsxAttribute: - return createJsxAttribute((node).name, (node).initializer, location, flags); + clone = createJsxAttribute((node).name, (node).initializer, location, flags); + break; case SyntaxKind.JsxSpreadAttribute: - return createJsxSpreadAttribute((node).expression, location, flags); + clone = createJsxSpreadAttribute((node).expression, location, flags); + break; case SyntaxKind.JsxExpression: - return createJsxExpression((node).expression, location, flags); + clone = createJsxExpression((node).expression, location, flags); + break; case SyntaxKind.CaseClause: - return createCaseClause((node).expression, (node).statements, location, flags); + clone = createCaseClause((node).expression, (node).statements, location, flags); + break; case SyntaxKind.DefaultClause: - return createDefaultClause((node).statements, location, flags); + clone = createDefaultClause((node).statements, location, flags); + break; case SyntaxKind.HeritageClause: - return createHeritageClause((node).token, (node).types, location, flags); + clone = createHeritageClause((node).token, (node).types, location, flags); + break; case SyntaxKind.CatchClause: - return createCatchClause((node).variableDeclaration, (node).block, location, flags); + clone = createCatchClause((node).variableDeclaration, (node).block, location, flags); + break; case SyntaxKind.PropertyAssignment: - return createPropertyAssignment((node).name, (node).initializer, location, flags); + clone = createPropertyAssignment((node).name, (node).initializer, location, flags); + break; case SyntaxKind.ShorthandPropertyAssignment: - return createShorthandPropertyAssignment((node).name, (node).equalsToken, (node).objectAssignmentInitializer, location, flags); + clone = createShorthandPropertyAssignment((node).name, location, flags); + break; case SyntaxKind.EnumMember: - return createEnumMember((node).name, (node).initializer, location, flags); + clone = createEnumMember((node).name, (node).initializer, location, flags); + break; case SyntaxKind.SourceFile: - return createSourceFileNode((node).statements, (node).endOfFileToken, (node).fileName, (node).text, (node).amdDependencies, (node).moduleName, (node).referencedFiles, (node).languageVariant, (node).renamedDependencies, (node).hasNoDefaultLib, (node).languageVersion, (node).externalModuleIndicator, (node).isDefaultLib, (node).identifiers, (node).parseDiagnostics, (node).bindDiagnostics, (node).lineMap, (node).classifiableNames, (node).resolvedModules, (node).imports, location, flags); + clone = createSourceFileNode((node).statements, (node).endOfFileToken, (node).fileName, (node).text, (node).amdDependencies, (node).moduleName, (node).referencedFiles, (node).languageVariant, (node).renamedDependencies, (node).hasNoDefaultLib, (node).languageVersion, (node).externalModuleIndicator, (node).isDefaultLib, (node).identifiers, (node).parseDiagnostics, (node).bindDiagnostics, (node).lineMap, (node).classifiableNames, (node).resolvedModules, (node).imports, location, flags); + break; case SyntaxKind.JSDocTypeExpression: - return createJSDocTypeExpression((node).type, location, flags); + clone = createJSDocTypeExpression((node).type, location, flags); + break; case SyntaxKind.JSDocAllType: - return createJSDocAllType(location, flags); + clone = createJSDocAllType(location, flags); + break; case SyntaxKind.JSDocUnknownType: - return createJSDocUnknownType(location, flags); + clone = createJSDocUnknownType(location, flags); + break; case SyntaxKind.JSDocArrayType: - return createJSDocArrayType((node).elementType, location, flags); + clone = createJSDocArrayType((node).elementType, location, flags); + break; case SyntaxKind.JSDocUnionType: - return createJSDocUnionType((node).types, location, flags); + clone = createJSDocUnionType((node).types, location, flags); + break; case SyntaxKind.JSDocTupleType: - return createJSDocTupleType((node).types, location, flags); + clone = createJSDocTupleType((node).types, location, flags); + break; case SyntaxKind.JSDocNullableType: - return createJSDocNullableType((node).type, location, flags); + clone = createJSDocNullableType((node).type, location, flags); + break; case SyntaxKind.JSDocNonNullableType: - return createJSDocNonNullableType((node).type, location, flags); + clone = createJSDocNonNullableType((node).type, location, flags); + break; case SyntaxKind.JSDocRecordType: - return createJSDocRecordType((node).members, location, flags); + clone = createJSDocRecordType((node).members, location, flags); + break; case SyntaxKind.JSDocRecordMember: - return createJSDocRecordMember((node).name, (node).type, location, flags); + clone = createJSDocRecordMember((node).name, (node).type, location, flags); + break; case SyntaxKind.JSDocTypeReference: - return createJSDocTypeReference((node).name, (node).typeArguments, location, flags); + clone = createJSDocTypeReference((node).name, (node).typeArguments, location, flags); + break; case SyntaxKind.JSDocOptionalType: - return createJSDocOptionalType((node).type, location, flags); + clone = createJSDocOptionalType((node).type, location, flags); + break; case SyntaxKind.JSDocFunctionType: - return createJSDocFunctionType((node).parameters, (node).type, location, flags); + clone = createJSDocFunctionType((node).parameters, (node).type, location, flags); + break; case SyntaxKind.JSDocVariadicType: - return createJSDocVariadicType((node).type, location, flags); + clone = createJSDocVariadicType((node).type, location, flags); + break; case SyntaxKind.JSDocConstructorType: - return createJSDocConstructorType((node).type, location, flags); + clone = createJSDocConstructorType((node).type, location, flags); + break; case SyntaxKind.JSDocThisType: - return createJSDocThisType((node).type, location, flags); + clone = createJSDocThisType((node).type, location, flags); + break; case SyntaxKind.JSDocComment: - return createJSDocComment((node).tags, location, flags); + clone = createJSDocComment((node).tags, location, flags); + break; case SyntaxKind.JSDocTag: - return createJSDocTag((node).atToken, (node).tagName, location, flags); + clone = createJSDocTag((node).atToken, (node).tagName, location, flags); + break; case SyntaxKind.JSDocParameterTag: - return createJSDocParameterTag((node).preParameterName, (node).typeExpression, (node).postParameterName, (node).atToken, (node).tagName, location, flags); + clone = createJSDocParameterTag((node).preParameterName, (node).typeExpression, (node).postParameterName, (node).atToken, (node).tagName, location, flags); + break; case SyntaxKind.JSDocReturnTag: - return createJSDocReturnTag((node).typeExpression, (node).atToken, (node).tagName, location, flags); + clone = createJSDocReturnTag((node).typeExpression, (node).atToken, (node).tagName, location, flags); + break; case SyntaxKind.JSDocTypeTag: - return createJSDocTypeTag((node).typeExpression, (node).atToken, (node).tagName, location, flags); + clone = createJSDocTypeTag((node).typeExpression, (node).atToken, (node).tagName, location, flags); + break; case SyntaxKind.JSDocTemplateTag: - return createJSDocTemplateTag((node).typeParameters, (node).atToken, (node).tagName, location, flags); + clone = createJSDocTemplateTag((node).typeParameters, (node).atToken, (node).tagName, location, flags); + break; case SyntaxKind.RawExpression: - return createRawExpression((node).text, location, flags); + clone = createRawExpression((node).text, location, flags); + break; case SyntaxKind.RawStatement: - return createRawStatement((node).text, location, flags); + clone = createRawStatement((node).text, location, flags); + break; + } + if (clone) { + clone.original = node; + return clone; } } return node; @@ -3377,7 +3531,7 @@ namespace ts { case SyntaxKind.PropertyAssignment: return updatePropertyAssignment(node, transformer.visitNode((node).name, visitor, isPropertyName), transformer.visitNode((node).initializer, visitor, isExpressionNode)); case SyntaxKind.ShorthandPropertyAssignment: - return updateShorthandPropertyAssignment(node, transformer.visitNode((node).name, visitor, isIdentifier), (node).equalsToken, transformer.visitNode((node).objectAssignmentInitializer, visitor, isExpressionNode)); + return updateShorthandPropertyAssignment(node, transformer.visitNode((node).name, visitor, isIdentifier)); case SyntaxKind.EnumMember: return updateEnumMember(node, transformer.visitNode((node).name, visitor, isDeclarationNameNode), transformer.visitNode((node).initializer, visitor, isExpressionNode)); case SyntaxKind.SourceFile: diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 015a787c69b..1f22ff0dad7 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -36,7 +36,9 @@ namespace ts { } export function setOriginalNode(node: T, original: Node): T { - node.original = node; + if (node && node !== original) { + node.original = node; + } return node; } @@ -46,8 +48,8 @@ namespace ts { return node; } - export function startOnNewLine(node: T): T { - (node).startsOnNewLine = true; + export function startOnNewLine(node: T, value?: boolean): T { + (node).startsOnNewLine = value === undefined ? true : value; return node; } @@ -135,7 +137,7 @@ namespace ts { return modifiers; } - + export function createNumericLiteral2(value: number, location?: TextRange, flags?: NodeFlags): LiteralExpression { let node = createNumericLiteral(String(value), location, flags); return node; @@ -234,8 +236,12 @@ namespace ts { return createPropertyAssignment(createIdentifier(name), initializer); } - export function createAssignmentExpression(left: Expression, right: Expression) { - return createBinaryExpression2(left, SyntaxKind.EqualsToken, right); + export function createAssignmentStatement(left: Expression, right: Expression, location?: TextRange) { + return createExpressionStatement(createAssignmentExpression(left, right), location); + } + + export function createAssignmentExpression(left: Expression, right: Expression, location?: TextRange) { + return createBinaryExpression2(left, SyntaxKind.EqualsToken, right, location); } export function createStrictEqualityExpression(left: Expression, right: Expression) { @@ -246,6 +252,10 @@ namespace ts { return createBinaryExpression2(left, SyntaxKind.ExclamationEqualsEqualsToken, right); } + export function createLogicalNotExpression(operand: LeftHandSideExpression) { + return createPrefixUnaryExpression(SyntaxKind.ExclamationToken, operand); + } + export function createLogicalAndExpression(left: Expression, right: Expression) { return createBinaryExpression2(left, SyntaxKind.AmpersandAmpersandToken, right); } @@ -308,6 +318,10 @@ namespace ts { return createVariableStatement2(varDeclList, location, flags & ~(NodeFlags.Let | NodeFlags.Const)); } + export function createVariableStatement4(name: string, initializer?: Expression, location?: TextRange, flags?: NodeFlags) { + return createVariableStatement3(createIdentifier(name), initializer, location, flags); + } + export function createLetStatement(name: Identifier, initializer: Expression, location?: TextRange, exported?: boolean) { return createVariableStatement3(name, initializer, location, exported ? NodeFlags.Let | NodeFlags.Export : NodeFlags.Let); } @@ -324,8 +338,8 @@ namespace ts { return createClassDeclaration(undefined, undefined, name, undefined, createClassHeritageClauses(baseTypeNode), members, location, flags); } - export function createClassExpression2(name: Identifier, baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[]): ClassExpression { - return createClassExpression(undefined, undefined, name, undefined, createClassHeritageClauses(baseTypeNode), members); + export function createClassExpression2(name: Identifier, baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[], location?: TextRange): ClassExpression { + return createClassExpression(undefined, undefined, name, undefined, createClassHeritageClauses(baseTypeNode), members, location); } export function createClassExpression3(baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[]): ClassExpression { @@ -394,6 +408,17 @@ namespace ts { return createElementAccessExpression2(expression, createNumericLiteral2(index), location, flags); } + export function createObjectLiteralExpression2(propertyMap: Map) { + let properties: PropertyAssignment[] = []; + for (let key in propertyMap) { + if (hasProperty(propertyMap, key)) { + properties.push(createPropertyAssignment2(key, propertyMap[key])); + } + } + + return createObjectLiteralExpression(properties); + } + export function createConcatCall(value: Expression, _arguments: Expression[]) { return createCallExpression2(createPropertyAccessExpression3(value, "concat"), _arguments); } @@ -437,32 +462,93 @@ namespace ts { return createCallExpression2(createIdentifier("__metadata"), [createStringLiteral(metadataKey), metadataValue]); } + export function createHasOwnPropertyCall(target: LeftHandSideExpression, property: Expression) { + return createCallExpression2(createPropertyAccessExpression3(target, "hasOwnProperty"), [property]); + } + + export function createExportStarHelperCall(moduleValue: Expression) { + return createCallExpression2(createIdentifier("__export"), [moduleValue]); + } + + export const enum PropertyDescriptorFlags { + Empty = 0, + + Enumerable = 1 << 0, + NotEnumerable = 1 << 1, + + Configurable = 1 << 2, + NotConfigurable = 1 << 3, + + Writable = 1 << 4, + NotWritable = 1 << 5, + + PreferNewLine = 1 << 6, + + Default = Enumerable | Configurable | Writable | PreferNewLine, + DefaultDataProperty = Enumerable | Configurable | Writable | PreferNewLine, + DefaultAccessorProperty = Enumerable | Configurable | PreferNewLine, + + EnumerableMask = Enumerable | NotEnumerable, + ConfigurableMask = Configurable | NotConfigurable, + WritableMask = Writable | NotWritable, + + TrueMask = Enumerable | Configurable | Writable, + FalseMask = NotEnumerable | NotConfigurable | NotWritable, + + DataPropertyMask = EnumerableMask | ConfigurableMask | WritableMask, + AccessorPropertyMask = EnumerableMask | ConfigurableMask, + } + + export function createDataPropertyDescriptor(value: Expression, flags?: PropertyDescriptorFlags) { + return createPropertyDescriptor(/*get*/ undefined, /*set*/ undefined, value, flags); + } + + export function createAccessorPropertyDescriptor(get: Expression, set: Expression, flags?: PropertyDescriptorFlags) { + return createPropertyDescriptor(get, set, /*value*/ undefined, flags); + } + + function createPropertyDescriptor(get: Expression, set: Expression, value: Expression, flags: PropertyDescriptorFlags = PropertyDescriptorFlags.Default) { + let isDataProperty = get === undefined && set === undefined; + let properties: ObjectLiteralElement[] = []; + addPropertyDescriptorProperty(properties, isDataProperty, "get", flags, get); + addPropertyDescriptorProperty(properties, isDataProperty, "set", flags, set); + addPropertyDescriptorProperty(properties, isDataProperty, "value", flags, value); + addPropertyDescriptorOption(properties, isDataProperty, "enumerable", flags, PropertyDescriptorFlags.EnumerableMask); + addPropertyDescriptorOption(properties, isDataProperty, "configurable", flags, PropertyDescriptorFlags.ConfigurableMask); + addPropertyDescriptorOption(properties, isDataProperty, "writable", flags, PropertyDescriptorFlags.WritableMask); + return createObjectLiteralExpression(properties); + } + + function addPropertyDescriptorProperty(properties: ObjectLiteralElement[], isDataProperty: boolean, propertyName: string, flags: PropertyDescriptorFlags, propertyValue: Expression) { + if (propertyValue && isDataProperty === (propertyName === "value")) { + let property = createPropertyAssignment2(propertyName, propertyValue); + startOnNewLine(property, (flags & PropertyDescriptorFlags.PreferNewLine) !== 0); + properties.push(property); + } + } + + function addPropertyDescriptorOption(properties: ObjectLiteralElement[], isDataProperty: boolean, optionName: string, flags: PropertyDescriptorFlags, optionMask: PropertyDescriptorFlags) { + let flagsMask = isDataProperty + ? PropertyDescriptorFlags.DataPropertyMask + : PropertyDescriptorFlags.AccessorPropertyMask; + + if (flags & flagsMask & optionMask) { + let propertyValue = flags & PropertyDescriptorFlags.FalseMask + ? createFalseKeyword() + : createTrueKeyword(); + + let property = createPropertyAssignment2(optionName, propertyValue); + startOnNewLine(property, (flags & PropertyDescriptorFlags.PreferNewLine) !== 0); + properties.push(property); + } + } + export function createDefinePropertyCall(target: Expression, memberName: Expression, descriptor: Expression, location?: TextRange) { return createCallExpression2(createPropertyAccessExpression3(createIdentifier("Object"), "defineProperty"), [target, memberName, descriptor], location); } export function createDefinePropertyCall2(target: Expression, memberName: Expression, getter: Expression, setter: Expression, location?: TextRange) { - let properties: ObjectLiteralElement[] = []; - if (getter) { - let get = createPropertyAssignment2("get", getter); - properties.push(get); - startOnNewLine(get); - } - if (setter) { - let set = createPropertyAssignment2("set", setter) - startOnNewLine(set); - properties.push(set); - } - - let enumerable = createPropertyAssignment2("enumerable", createTrueKeyword()); - startOnNewLine(enumerable); - properties.push(enumerable); - - let configurable = createPropertyAssignment2("configurable", createTrueKeyword()); - startOnNewLine(configurable); - properties.push(configurable); - - let descriptor = createObjectLiteralExpression(properties); + let descriptor = createAccessorPropertyDescriptor(getter, setter, PropertyDescriptorFlags.DefaultAccessorProperty); return createDefinePropertyCall(target, memberName, descriptor, location); } @@ -504,6 +590,10 @@ namespace ts { : createElementAccessExpression2(target, cloneNode(memberName), location, flags); } + export function createStringLiteralForIdentifier(name: Identifier): StringLiteral { + return createStringLiteral(name.text) + } + export function createExpressionForPropertyName(memberName: PropertyName, location?: TextRange): Expression { return isIdentifier(memberName) ? createStringLiteral(memberName.text, location) : isComputedPropertyName(memberName) ? cloneNode(memberName.expression, location) @@ -536,4 +626,16 @@ namespace ts { } return false; } + + function createIsObjectExpression(expression: LeftHandSideExpression) { + return createStrictEqualityExpression(createTypeOfExpression(expression), createStringLiteral("object")); + } + + function createIsFunctionExpression(expression: LeftHandSideExpression) { + return createStrictEqualityExpression(createTypeOfExpression(expression), createStringLiteral("function")); + } + + function createIsNotUndefinedExpression(expression: LeftHandSideExpression) { + return createStrictInequalityExpression(expression, createVoidZeroExpression()); + } } \ No newline at end of file diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e8076c74798..a3ef2fbf7d4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -396,6 +396,25 @@ namespace ts { return result; } + // @internal + export function parseSynthesizedStatements(sourceText: string) { + let sourceFile = Parser.parseSourceFile("parseStatement.ts", sourceText, ScriptTarget.Latest, /*syntaxCursor*/ undefined, /*setParentNodes*/ false, /*synthesized*/ true); + return sourceFile.statements; + } + + // @internal + export function parseSynthesizedStatement(sourceText: string) { + let statements = parseSynthesizedStatements(sourceText); + return firstOrUndefined(statements); + } + + // @internal + export function parseSynthesizedExpression(sourceText: string) { + let statement = parseSynthesizedStatement(`(${sourceText})`); + let expression = (statement).expression; + return (expression).expression; + } + // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter // indicates what changed between the 'text' that this SourceFile has and the 'newText'. // The SourceFile will be created with the compiler attempting to reuse as many nodes from @@ -518,10 +537,10 @@ namespace ts { // attached to the EOF token. let parseErrorBeforeNextFinishedNode = false; - export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean): SourceFile { + export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, synthesized?: boolean): SourceFile { initializeState(fileName, _sourceText, languageVersion, _syntaxCursor); - let result = parseSourceFileWorker(fileName, languageVersion, setParentNodes); + let result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, synthesized); clearState(); @@ -561,7 +580,7 @@ namespace ts { sourceText = undefined; } - function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean): SourceFile { + function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean, synthesized: boolean): SourceFile { sourceFile = createSourceFile(fileName, languageVersion); // Prime the scanner. @@ -583,6 +602,10 @@ namespace ts { fixupParentReferences(sourceFile); } + if (synthesized) { + makeSynthesized(sourceFile); + } + // If this is a javascript file, proactively see if we can get JSDoc comments for // relevant nodes in the file. We'll use these to provide typing informaion if they're // available. @@ -623,6 +646,22 @@ namespace ts { } } + function makeSynthesized(sourceFile: Node) { + visitNode(sourceFile); + + function visitNode(n: Node) { + delete n.pos; + delete n.end; + forEachChild(n, visitNode, visitNodes); + } + + function visitNodes(n: NodeArray) { + delete n.pos; + delete n.end; + forEach(n, visitNode); + } + } + export function fixupParentReferences(sourceFile: Node) { // normally parent references are set during binding. However, for clients that only need // a syntax tree, and no semantic features, then the binding process is an unnecessary @@ -3197,7 +3236,7 @@ namespace ts { /** * Parse ES7 unary expression and await expression - * + * * ES7 UnaryExpression: * 1) SimpleUnaryExpression[?yield] * 2) IncrementExpression[?yield] ** UnaryExpression[?yield] @@ -4973,8 +5012,8 @@ namespace ts { // implements is a future reserved word so // 'class implements' might mean either // - class expression with omitted name, 'implements' starts heritage clause - // - class with name 'implements' - // 'isImplementsClause' helps to disambiguate between these two cases + // - class with name 'implements' + // 'isImplementsClause' helps to disambiguate between these two cases return isIdentifier() && !isImplementsClause() ? parseIdentifier() : undefined; diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 5473cd2b158..64ba2c0f285 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -7,1878 +7,2140 @@ namespace ts { const brackets = createBracketsMap(); const delimiters = createDelimiterMap(); - export interface PrintHost extends ScriptReferenceHost { - getNewLine(): string; + export function printFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformations?: TransformationChain) { + // emit output for the __extends helper function + const extendsHelper = ` +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +};`; + + // emit output for the __decorate helper function + const decorateHelper = ` +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); + switch (arguments.length) { + case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); + case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); + case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); } +};`; + + // emit output for the __metadata helper function + const metadataHelper = ` +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +};`; + + // emit output for the __param helper function + const paramHelper = ` +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +};`; + + // emit output for the __awaiter helper function + const awaiterHelper = ` +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) { + return new Promise(function (resolve, reject) { + generator = generator.apply(thisArg, _arguments); + function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); } + function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } } + function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } } + function step(verb, value) { + var result = generator[verb](value); + result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject); + } + step("next", void 0); + }); +};`; + + // emit output for the __export helper function + const exportStarHelper = ` +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +}`; + + // emit output for the UMD helper function. + const umdHelper = `(function (dependencies, factory) { + if (typeof module === 'object' && typeof module.exports === 'object') { + var v = factory(require, exports); if (v !== undefined) module.exports = v; + } + else if (typeof define === 'function' && define.amd) { + define(dependencies, factory); + } +})`; - /** - * Pretty-prints a set of input source files to an output string. - * @param resolver The emit resolver. - * @param host The emit host - * @param sourceFiles The input source files. - * @param fileName The output file name. - */ - export function printNodes(resolver: EmitResolver, substitutions: TransformationSubstitutions, host: PrintHost, nodes: Node[]) { let writer = createTextWriter(host.getNewLine()); - let { write, writeTextOfNode, writeLine, increaseIndent, decreaseIndent } = writer; - let { assignmentSubstitution, bindingIdentifierSubstitution, expressionIdentifierSubstitution } = substitutions; + let { + write, + writeTextOfNode, + writeLine, + getIndent, + increaseIndent, + decreaseIndent + } = writer; - let compilerOptions = host.getCompilerOptions(); - let languageVersion = compilerOptions.target || ScriptTarget.ES3; - let diagnostics: Diagnostic[] = []; - let currentSourceFile: SourceFile; - let parentNode: Node; - let currentNode: Node; + // Add the pretty printer to the transformation chain. + transformations = transformations ? chainTransformationPhases([transformations, createPrinter]) : createPrinter; - /** Emit a node */ - let emit = emitNode; - - let emitDetachedComments = function (node: Node) { }; - let emitLeadingComments = function (node: Node) { }; - let emitTrailingComments = function (node: Node) { }; - let emitTrailingCommentsOfPosition = function (pos: number) { }; - - /** Called just before starting emit of a node */ - let emitStart = function (node: Node) { }; - - /** Called once the emit of the node is done */ - let emitEnd = function (node: Node) { }; - - /** Called to before starting the lexical scopes as in function/class in the emitted code because of node - * @param scopeDeclaration node that starts the lexical scope - * @param scopeName Optional name of this scope instead of deducing one from the declaration node */ - let scopeEmitStart = function(scopeDeclaration: Node, scopeName?: string) { }; - - /** Called after coming out of the scope */ - let scopeEmitEnd = function() { }; - - if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) { - // initializeEmitterWithSourceMaps(); - } - - for (let node of nodes) { - emit(node); - } + // Transform each file and print it to the writer. + transformFiles(resolver, host, sourceFiles, transformations); writeLine(); return writer.getText(); - function emitNode(node: Node) { - if (node) { - let savedParentNode = parentNode; - parentNode = currentNode; - emitNodeWorker(currentNode = node); - currentNode = parentNode; - parentNode = savedParentNode; - } - } + function createPrinter(transformer: Transformer) { + let { + startLexicalEnvironment, + endLexicalEnvironment, + getGeneratedNodeFlags, + mapNode, + tryPushNode, + setNode, + popNode, + getParentNode, + } = transformer; - function tryEmitSubstitute(node: T, substitution: (node: T) => Node) { - let substitute = substitution ? substitution(node) : node; - if (substitute && substitute !== node) { - let savedCurrentNode = currentNode; - emitNodeWorker(currentNode = substitute); - currentNode = savedCurrentNode; - return true; + let expressionSubstitution = transformer.getExpressionSubstitution(); + let compilerOptions = host.getCompilerOptions(); + let languageVersion = getLanguageVersion(compilerOptions); + let moduleKind = getModuleKind(compilerOptions); + let diagnostics: Diagnostic[] = []; + let currentSourceFile: SourceFile; + let helpersEmitted: NodeCheckFlags; + + /** Emit a node */ + let emit = emitNode; + + let emitDetachedComments = function (node: Node) { }; + let emitLeadingComments = function (node: Node) { }; + let emitTrailingComments = function (node: Node) { }; + let emitTrailingCommentsOfPosition = function (pos: number) { }; + + /** Called just before starting emit of a node */ + let emitStart = function (node: Node) { }; + + /** Called once the emit of the node is done */ + let emitEnd = function (node: Node) { }; + + /** Called to before starting the lexical scopes as in function/class in the emitted code because of node + * @param scopeDeclaration node that starts the lexical scope + * @param scopeName Optional name of this scope instead of deducing one from the declaration node */ + let scopeEmitStart = function(scopeDeclaration: Node, scopeName?: string) { }; + + /** Called after coming out of the scope */ + let scopeEmitEnd = function() { }; + + if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) { + // initializeEmitterWithSourceMaps(); } - return false; - } + return printSourceFile; - function emitNodeWorker(node: Node) { - switch (node.kind) { - // Literals - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return emitLiteral(node); - - // Pseudo-literals - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - return emitLiteral(node); - - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); - - // Reserved words - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.ThisKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.VoidKeyword: - return writeTokenNode(node); - - // Contextual keywords - case SyntaxKind.AnyKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.SymbolKeyword: - return writeTokenNode(node); - - // Parse tree nodes - - // Names - case SyntaxKind.QualifiedName: - return emitQualifiedName(node); - case SyntaxKind.ComputedPropertyName: - return emitComputedPropertyName(node); - - // Signature elements - case SyntaxKind.TypeParameter: - return emitTypeParameter(node); - case SyntaxKind.Parameter: - return emitParameter(node); - case SyntaxKind.Decorator: - return emitDecorator(node); - - // Type members - case SyntaxKind.PropertySignature: - return emitPropertySignature(node); - case SyntaxKind.PropertyDeclaration: - return emitPropertyDeclaration(node); - case SyntaxKind.MethodSignature: - return emitMethodSignature(node); - case SyntaxKind.MethodDeclaration: - return emitMethodDeclaration(node); - case SyntaxKind.Constructor: - return emitConstructor(node); - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return emitAccessorDeclaration(node); - case SyntaxKind.CallSignature: - return emitCallSignature(node); - case SyntaxKind.ConstructSignature: - return emitConstructSignature(node); - case SyntaxKind.IndexSignature: - return emitIndexSignature(node); - - // Types - case SyntaxKind.TypePredicate: - return emitTypePredicate(node); - case SyntaxKind.TypeReference: - return emitTypeReference(node); - case SyntaxKind.FunctionType: - return emitFunctionType(node); - case SyntaxKind.ConstructorType: - return emitConstructorType(node); - case SyntaxKind.TypeQuery: - return emitTypeQuery(node); - case SyntaxKind.TypeLiteral: - return emitTypeLiteral(node); - case SyntaxKind.ArrayType: - return emitArrayType(node); - case SyntaxKind.TupleType: - return emitTupleType(node); - case SyntaxKind.UnionType: - return emitUnionType(node); - case SyntaxKind.IntersectionType: - return emitIntersectionType(node); - case SyntaxKind.ParenthesizedType: - return emitParenthesizedType(node); - - // Binding patterns - case SyntaxKind.ObjectBindingPattern: - return emitObjectBindingPattern(node); - case SyntaxKind.ArrayBindingPattern: - return emitArrayBindingPattern(node); - case SyntaxKind.BindingElement: - return emitBindingElement(node); - - // Expressions - case SyntaxKind.ArrayLiteralExpression: - return emitArrayLiteralExpression(node); - case SyntaxKind.ObjectLiteralExpression: - return emitObjectLiteralExpression(node); - case SyntaxKind.PropertyAccessExpression: - return emitPropertyAccessExpression(node); - case SyntaxKind.ElementAccessExpression: - return emitElementAccessExpression(node); - case SyntaxKind.CallExpression: - return emitCallExpression(node); - case SyntaxKind.NewExpression: - return emitNewExpression(node); - case SyntaxKind.TaggedTemplateExpression: - return emitTaggedTemplateExpression(node); - case SyntaxKind.TypeAssertionExpression: - return emitTypeAssertionExpression(node); - case SyntaxKind.ParenthesizedExpression: - return emitParenthesizedExpression(node); - case SyntaxKind.FunctionExpression: - return emitFunctionExpression(node); - case SyntaxKind.ArrowFunction: - return emitArrowFunction(node); - case SyntaxKind.DeleteExpression: - return emitDeleteExpression(node); - case SyntaxKind.TypeOfExpression: - return emitTypeOfExpression(node); - case SyntaxKind.VoidExpression: - return emitVoidExpression(node); - case SyntaxKind.AwaitExpression: - return emitAwaitExpression(node); - case SyntaxKind.PrefixUnaryExpression: - return emitPrefixUnaryExpression(node); - case SyntaxKind.PostfixUnaryExpression: - return emitPostfixUnaryExpression(node); - case SyntaxKind.BinaryExpression: - return emitBinaryExpression(node); - case SyntaxKind.ConditionalExpression: - return emitConditionalExpression(node); - case SyntaxKind.TemplateExpression: - return emitTemplateExpression(node); - case SyntaxKind.YieldExpression: - return emitYieldExpression(node); - case SyntaxKind.SpreadElementExpression: - return emitSpreadElementExpression(node); - case SyntaxKind.ClassExpression: - return emitClassExpression(node); - case SyntaxKind.OmittedExpression: - return; - case SyntaxKind.ExpressionWithTypeArguments: - return emitExpressionWithTypeArguments(node); - case SyntaxKind.AsExpression: - return emitAsExpression(node); - - // Misc - case SyntaxKind.TemplateSpan: - return emitTemplateSpan(node); - case SyntaxKind.SemicolonClassElement: - return write(";"); - - // Statements - case SyntaxKind.Block: - return emitBlock(node); - case SyntaxKind.VariableStatement: - return emitVariableStatement(node); - case SyntaxKind.EmptyStatement: - return write(";"); - case SyntaxKind.ExpressionStatement: - return emitExpressionStatement(node); - case SyntaxKind.IfStatement: - return emitIfStatement(node); - case SyntaxKind.DoStatement: - return emitDoStatement(node); - case SyntaxKind.WhileStatement: - return emitWhileStatement(node); - case SyntaxKind.ForStatement: - return emitForStatement(node); - case SyntaxKind.ForInStatement: - return emitForInStatement(node); - case SyntaxKind.ForOfStatement: - return emitForOfStatement(node); - case SyntaxKind.ContinueStatement: - return emitContinueStatement(node); - case SyntaxKind.BreakStatement: - return emitBreakStatement(node); - case SyntaxKind.ReturnStatement: - return emitReturnStatement(node); - case SyntaxKind.WithStatement: - return emitWithStatement(node); - case SyntaxKind.SwitchStatement: - return emitSwitchStatement(node); - case SyntaxKind.LabeledStatement: - return emitLabeledStatement(node); - case SyntaxKind.ThrowStatement: - return emitThrowStatement(node); - case SyntaxKind.TryStatement: - return emitTryStatement(node); - case SyntaxKind.DebuggerStatement: - return emitDebuggerStatement(node); - - // Declarations - case SyntaxKind.VariableDeclaration: - return emitVariableDeclaration(node); - case SyntaxKind.VariableDeclarationList: - return emitVariableDeclarationList(node); - case SyntaxKind.FunctionDeclaration: - return emitFunctionDeclaration(node); - case SyntaxKind.ClassDeclaration: - return emitClassDeclaration(node); - case SyntaxKind.InterfaceDeclaration: - return emitInterfaceDeclaration(node); - case SyntaxKind.TypeAliasDeclaration: - return emitTypeAliasDeclaration(node); - case SyntaxKind.EnumDeclaration: - return emitEnumDeclaration(node); - case SyntaxKind.ModuleDeclaration: - return emitModuleDeclaration(node); - case SyntaxKind.ModuleBlock: - return emitModuleBlock(node); - case SyntaxKind.CaseBlock: - return emitCaseBlock(node); - case SyntaxKind.ImportEqualsDeclaration: - return emitImportEqualsDeclaration(node); - case SyntaxKind.ImportDeclaration: - return emitImportDeclaration(node); - case SyntaxKind.ImportClause: - return emitImportClause(node); - case SyntaxKind.NamespaceImport: - return emitNamespaceImport(node); - case SyntaxKind.NamedImports: - return emitNamedImportsOrExports(node); - case SyntaxKind.ImportSpecifier: - return emitImportOrExportSpecifier(node); - case SyntaxKind.ExportAssignment: - return emitExportAssignment(node); - case SyntaxKind.ExportDeclaration: - return emitExportDeclaration(node); - case SyntaxKind.NamedExports: - return emitNamedImportsOrExports(node); - case SyntaxKind.ExportSpecifier: - return emitImportOrExportSpecifier(node); - case SyntaxKind.MissingDeclaration: - return; - - // Module references - case SyntaxKind.ExternalModuleReference: - return emitExternalModuleReference(node); - - // JSX - case SyntaxKind.JsxElement: - return emitJsxElement(node); - case SyntaxKind.JsxSelfClosingElement: - return emitJsxSelfClosingElement(node); - case SyntaxKind.JsxOpeningElement: - return emitJsxOpeningElement(node); - case SyntaxKind.JsxText: - return emitJsxText(node); - case SyntaxKind.JsxClosingElement: - return emitJsxClosingElement(node); - case SyntaxKind.JsxAttribute: - return emitJsxAttribute(node); - case SyntaxKind.JsxSpreadAttribute: - return emitJsxSpreadAttribute(node); - case SyntaxKind.JsxExpression: - return emitJsxExpression(node); - - // Clauses - case SyntaxKind.CaseClause: - return emitCaseClause(node); - case SyntaxKind.DefaultClause: - return emitDefaultClause(node); - case SyntaxKind.HeritageClause: - return emitHeritageClause(node); - case SyntaxKind.CatchClause: - return emitCatchClause(node); - - // Property assignments - case SyntaxKind.PropertyAssignment: - return emitPropertyAssignment(node); - case SyntaxKind.ShorthandPropertyAssignment: - return emitShorthandPropertyAssignment(node); - - // Enum - case SyntaxKind.EnumMember: - return emitEnumMember(node); - - // Top-level nodes - case SyntaxKind.SourceFile: - return emitSourceFile(node); - - // JSDoc nodes (ignored) - // Raw nodes (deprecated) - } - } - - // - // Literals/Pseudo-literals - // - - // SyntaxKind.NumericLiteral - // SyntaxKind.StringLiteral - // SyntaxKind.RegularExpressionLiteral - // SyntaxKind.NoSubstitutionTemplateLiteral - // SyntaxKind.TemplateHead - // SyntaxKind.TemplateMiddle - // SyntaxKind.TemplateTail - function emitLiteral(node: LiteralExpression) { - let text = getLiteralText(node, currentSourceFile, languageVersion); - if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) - && (isStringLiteral(node) || isTemplateLiteralKind(node.kind))) { - writer.writeLiteral(text); - } - else { - write(text); - } - } - - // - // Identifiers - // - - function emitIdentifier(node: Identifier) { - if (isExpressionIdentifier(node)) { - emitExpressionIdentifier(node); - } - else if (tryEmitSubstitute(node, bindingIdentifierSubstitution)) { - return; - } - else if (nodeIsSynthesized(node)) { - write(node.text); - } - else { - writeTextOfNode(currentSourceFile, node); - } - } - - function isExpressionIdentifier(node: Node): boolean { - switch (parentNode.kind) { - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.BinaryExpression: - case SyntaxKind.CallExpression: - case SyntaxKind.CaseClause: - case SyntaxKind.ComputedPropertyName: - case SyntaxKind.ConditionalExpression: - case SyntaxKind.Decorator: - case SyntaxKind.DeleteExpression: - case SyntaxKind.DoStatement: - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.ExportAssignment: - case SyntaxKind.ExpressionStatement: - case SyntaxKind.ExpressionWithTypeArguments: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.IfStatement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxSpreadAttribute: - case SyntaxKind.JsxExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.PostfixUnaryExpression: - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.ReturnStatement: - case SyntaxKind.ShorthandPropertyAssignment: - case SyntaxKind.SpreadElementExpression: - case SyntaxKind.SwitchStatement: - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.TemplateSpan: - case SyntaxKind.ThrowStatement: - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.TypeOfExpression: - case SyntaxKind.VoidExpression: - case SyntaxKind.WhileStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.YieldExpression: - return true; - case SyntaxKind.BindingElement: - case SyntaxKind.EnumMember: - case SyntaxKind.Parameter: - case SyntaxKind.PropertyAssignment: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.VariableDeclaration: - return (parentNode).initializer === node; - case SyntaxKind.PropertyAccessExpression: - return (parentNode).expression === node; - case SyntaxKind.ArrowFunction: - case SyntaxKind.FunctionExpression: - return (parentNode).body === node; - case SyntaxKind.ImportEqualsDeclaration: - return (parentNode).moduleReference === node; - case SyntaxKind.QualifiedName: - return (parentNode).left === node; - } - return false; - } - - function emitExpressionIdentifier(node: Identifier) { - if (tryEmitSubstitute(node, expressionIdentifierSubstitution)) { - return; - } - else if (nodeIsSynthesized(node)) { - write(node.text); - } - else { - writeTextOfNode(currentSourceFile, node); - } - } - - // - // Names - // - - function emitQualifiedName(node: QualifiedName) { - emit(node.left); - write("."); - emit(node.right); - } - - function emitComputedPropertyName(node: ComputedPropertyName) { - write("["); - emit(node.expression); - write("]"); - } - - // - // Signature elements - // - - function emitTypeParameter(node: TypeParameterDeclaration) { - emitStart(node); - emit(node.name); - emitWithPrefix("extends ", node.constraint); - emitEnd(node); - } - - function emitParameter(node: ParameterDeclaration) { - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - writeIfPresent(node.dotDotDotToken, "..."); - emit(node.name); - writeIfPresent(node.questionToken, "?"); - emitWithPrefix(" = ", node.initializer); - emitWithPrefix(": ", node.type); - emitEnd(node); - } - - function emitDecorator(decorator: Decorator) { - emitLeadingComments(decorator); - emitStart(decorator); - write("@"); - emit(decorator.expression); - emitEnd(decorator); - emitTrailingComments(decorator); - } - - // - // Type members - // - - function emitPropertySignature(node: PropertySignature) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - emit(node.name); - writeIfPresent(node.questionToken, "?"); - emitWithPrefix(": ", node.type); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitPropertyDeclaration(node: PropertyDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - emit(node.name); - emitWithPrefix(": ", node.type); - emitWithPrefix(" = ", node.initializer); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitMethodSignature(node: MethodSignature) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - emit(node.name); - writeIfPresent(node.questionToken, "?"); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.parameters, ListFormat.Parameters); - emitWithPrefix(": ", node.type); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitMethodDeclaration(node: MethodDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - writeIfPresent(node.asteriskToken, "*"); - emit(node.name); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.parameters, ListFormat.Parameters); - emitWithPrefix(": ", node.type); - emit(node.body); - writeIfMissing(node.body, ";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitConstructor(node: ConstructorDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitModifiers(node); - write("constructor"); - emitList(node, node.parameters, ListFormat.Parameters); - emit(node.body); - writeIfMissing(node.body, ";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitAccessorDeclaration(node: AccessorDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); - emit(node.name); - emitList(node, node.parameters, ListFormat.Parameters); - emitWithPrefix(": ", node.type); - emit(node.body); - writeIfMissing(node.body, ";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitCallSignature(node: CallSignatureDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.parameters, ListFormat.Parameters); - emitWithPrefix(": ", node.type); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitConstructSignature(node: ConstructSignatureDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - write("new "); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.parameters, ListFormat.Parameters); - emitWithPrefix(": ", node.type); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitIndexSignature(node: IndexSignatureDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - emitList(node, node.parameters, ListFormat.IndexSignatureParameters); - emitWithPrefix(": ", node.type); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - // - // Types - // - - function emitTypePredicate(node: TypePredicateNode) { - emitStart(node); - emit(node.parameterName); - write(" is "); - emit(node.type); - emitEnd(node); - } - - function emitTypeReference(node: TypeReferenceNode) { - emitStart(node); - emit(node.typeName); - emitList(node, node.typeArguments, ListFormat.TypeArguments); - emitEnd(node); - } - - function emitFunctionType(node: FunctionTypeNode) { - emitStart(node); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.parameters, ListFormat.ArrowParameters); - write(" => "); - emit(node.type); - emitEnd(node); - } - - function emitConstructorType(node: ConstructorTypeNode) { - emitStart(node); - write("new "); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.parameters, ListFormat.ArrowParameters); - write(" => "); - emit(node.type); - emitEnd(node); - } - - function emitTypeQuery(node: TypeQueryNode) { - emitStart(node); - write("typeof "); - emit(node.exprName); - emitEnd(node); - } - - function emitTypeLiteral(node: TypeLiteralNode) { - emitStart(node); - emitList(node, node.members, ListFormat.TypeElements); - emitEnd(node); - } - - function emitArrayType(node: ArrayTypeNode) { - emitStart(node); - emit(node.elementType); - write("[]"); - emitEnd(node); - } - - function emitTupleType(node: TupleTypeNode) { - emitStart(node); - emitList(node, node.elementTypes, ListFormat.TupleTypeElementTypes); - emitEnd(node); - } - - function emitUnionType(node: UnionTypeNode) { - emitStart(node); - emitList(node, node.types, ListFormat.UnionTypeConstituents) - emitEnd(node); - } - - function emitIntersectionType(node: IntersectionTypeNode) { - emitStart(node); - emitList(node, node.types, ListFormat.IntersectionTypeConstituents); - emitEnd(node); - } - - function emitParenthesizedType(node: ParenthesizedTypeNode) { - write("("); - emit(node.type); - write(")"); - } - - // - // Binding patterns - // - - function emitObjectBindingPattern(node: ObjectBindingPattern) { - emitList(node, node.elements, ListFormat.ObjectBindingPatternElements); - } - - function emitArrayBindingPattern(node: ArrayBindingPattern) { - emitList(node, node.elements, ListFormat.ArrayBindingPatternElements); - } - - function emitBindingElement(node: BindingElement) { - emitWithSuffix(node.propertyName, ": "); - writeIfPresent(node.dotDotDotToken, "..."); - emit(node.name); - emitWithPrefix(" = ", node.initializer); - } - - // - // Expressions - // - - function emitArrayLiteralExpression(node: ArrayLiteralExpression) { - emitList(node, node.elements, ListFormat.ArrayLiteralExpressionElements); - } - - function emitObjectLiteralExpression(node: ObjectLiteralExpression) { - emitList(node, node.properties, languageVersion < ScriptTarget.ES5 - ? ListFormat.ObjectLiteralExpressionProperties - : ListFormat.ObjectLiteralExpressionPropertiesForES5AndLater); - } - - function emitPropertyAccessExpression(node: PropertyAccessExpression) { - if (tryEmitConstantValue(node)) { - return; + function printSourceFile(node: SourceFile) { + emitNode(node); + return node; } - let indentBeforeDot = needsIndentation(node, node.expression, node.dotToken); - let indentAfterDot = needsIndentation(node, node.dotToken, node.name); - let shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); - - emit(node.expression); - increaseIndentIf(indentBeforeDot); - write(shouldEmitDotDot ? ".." : "."); - increaseIndentIf(indentAfterDot); - emit(node.name); - decreaseIndentIf(indentBeforeDot, indentAfterDot); - } - - // 1..toString is a valid property access, emit a dot after the literal - // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal - function needsDotDotForPropertyAccess(expression: Expression) { - if (isNumericLiteral(expression)) { - // check if numeric literal was originally written with a dot - let text = getLiteralText(expression, currentSourceFile, languageVersion); - return text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; - } - else { - // check if constant enum value is integer - let constantValue = tryGetConstEnumValue(expression); - // isFinite handles cases when constantValue is undefined - return isFinite(constantValue) && Math.floor(constantValue) === constantValue; - } - return false; - } - - function emitElementAccessExpression(node: ElementAccessExpression) { - if (tryEmitConstantValue(node)) { - return; + function emitNode(node: Node) { + if (node) { + let wasPushed = tryPushNode(node); + emitNodeWorker(node); + if (wasPushed) { + popNode(); + } + } } - emit(node.expression); - write("["); - emit(node.argumentExpression); - write("]"); - } + function emitNodeWorker(node: Node) { + switch (node.kind) { + // Literals + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return emitLiteral(node); - function emitCallExpression(node: CallExpression) { - emit(node.expression); - emitList(node, node.arguments, ListFormat.CallExpressionArguments); - } + // Pseudo-literals + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return emitLiteral(node); - function emitNewExpression(node: NewExpression) { - write("new "); - emit(node.expression); - emitList(node, node.arguments, ListFormat.NewExpressionArguments); - } + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); - function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { - emit(node.tag); - emit(node.template); - } + // Reserved words + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.VoidKeyword: + return writeTokenNode(node); - function emitTypeAssertionExpression(node: TypeAssertion) { - write("<"); - emit(node.type); - write(">"); - emit(node.expression); - } + // Contextual keywords + case SyntaxKind.AnyKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.SymbolKeyword: + return writeTokenNode(node); - function emitParenthesizedExpression(node: ParenthesizedExpression) { - write("("); - emit(node.expression); - write(")"); - } + // Parse tree nodes - function emitFunctionExpression(node: FunctionExpression) { - emitFunctionDeclarationOrExpression(node); - } + // Names + case SyntaxKind.QualifiedName: + return emitQualifiedName(node); + case SyntaxKind.ComputedPropertyName: + return emitComputedPropertyName(node); - function emitArrowFunction(node: ArrowFunction) { - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.parameters, ListFormat.ArrowParameters); - emitWithPrefix(": ", node.type); - emit(node.body); - emitEnd(node); - } + // Signature elements + case SyntaxKind.TypeParameter: + return emitTypeParameter(node); + case SyntaxKind.Parameter: + return emitParameter(node); + case SyntaxKind.Decorator: + return emitDecorator(node); - function emitDeleteExpression(node: DeleteExpression) { - write("delete "); - emit(node.expression); - } + // Type members + case SyntaxKind.PropertySignature: + return emitPropertySignature(node); + case SyntaxKind.PropertyDeclaration: + return emitPropertyDeclaration(node); + case SyntaxKind.MethodSignature: + return emitMethodSignature(node); + case SyntaxKind.MethodDeclaration: + return emitMethodDeclaration(node); + case SyntaxKind.Constructor: + return emitConstructor(node); + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return emitAccessorDeclaration(node); + case SyntaxKind.CallSignature: + return emitCallSignature(node); + case SyntaxKind.ConstructSignature: + return emitConstructSignature(node); + case SyntaxKind.IndexSignature: + return emitIndexSignature(node); - function emitTypeOfExpression(node: TypeOfExpression) { - write("typeof "); - emit(node.expression); - } + // Types + case SyntaxKind.TypePredicate: + return emitTypePredicate(node); + case SyntaxKind.TypeReference: + return emitTypeReference(node); + case SyntaxKind.FunctionType: + return emitFunctionType(node); + case SyntaxKind.ConstructorType: + return emitConstructorType(node); + case SyntaxKind.TypeQuery: + return emitTypeQuery(node); + case SyntaxKind.TypeLiteral: + return emitTypeLiteral(node); + case SyntaxKind.ArrayType: + return emitArrayType(node); + case SyntaxKind.TupleType: + return emitTupleType(node); + case SyntaxKind.UnionType: + return emitUnionType(node); + case SyntaxKind.IntersectionType: + return emitIntersectionType(node); + case SyntaxKind.ParenthesizedType: + return emitParenthesizedType(node); - function emitVoidExpression(node: VoidExpression) { - write("void "); - emit(node.expression); - } + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + return emitObjectBindingPattern(node); + case SyntaxKind.ArrayBindingPattern: + return emitArrayBindingPattern(node); + case SyntaxKind.BindingElement: + return emitBindingElement(node); - function emitAwaitExpression(node: AwaitExpression) { - write("await "); - emit(node.expression); - } + // Expressions + case SyntaxKind.ArrayLiteralExpression: + return emitArrayLiteralExpression(node); + case SyntaxKind.ObjectLiteralExpression: + return emitObjectLiteralExpression(node); + case SyntaxKind.PropertyAccessExpression: + return emitPropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return emitElementAccessExpression(node); + case SyntaxKind.CallExpression: + return emitCallExpression(node); + case SyntaxKind.NewExpression: + return emitNewExpression(node); + case SyntaxKind.TaggedTemplateExpression: + return emitTaggedTemplateExpression(node); + case SyntaxKind.TypeAssertionExpression: + return emitTypeAssertionExpression(node); + case SyntaxKind.ParenthesizedExpression: + return emitParenthesizedExpression(node); + case SyntaxKind.FunctionExpression: + return emitFunctionExpression(node); + case SyntaxKind.ArrowFunction: + return emitArrowFunction(node); + case SyntaxKind.DeleteExpression: + return emitDeleteExpression(node); + case SyntaxKind.TypeOfExpression: + return emitTypeOfExpression(node); + case SyntaxKind.VoidExpression: + return emitVoidExpression(node); + case SyntaxKind.AwaitExpression: + return emitAwaitExpression(node); + case SyntaxKind.PrefixUnaryExpression: + return emitPrefixUnaryExpression(node); + case SyntaxKind.PostfixUnaryExpression: + return emitPostfixUnaryExpression(node); + case SyntaxKind.BinaryExpression: + return emitBinaryExpression(node); + case SyntaxKind.ConditionalExpression: + return emitConditionalExpression(node); + case SyntaxKind.TemplateExpression: + return emitTemplateExpression(node); + case SyntaxKind.YieldExpression: + return emitYieldExpression(node); + case SyntaxKind.SpreadElementExpression: + return emitSpreadElementExpression(node); + case SyntaxKind.ClassExpression: + return emitClassExpression(node); + case SyntaxKind.OmittedExpression: + return; + case SyntaxKind.ExpressionWithTypeArguments: + return emitExpressionWithTypeArguments(node); + case SyntaxKind.AsExpression: + return emitAsExpression(node); - function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { - writeToken(node.operator); - if (shouldEmitWhitespaceBeforeOperand(node)) { - write(" "); + // Misc + case SyntaxKind.TemplateSpan: + return emitTemplateSpan(node); + case SyntaxKind.SemicolonClassElement: + return write(";"); + + // Statements + case SyntaxKind.Block: + return emitBlock(node); + case SyntaxKind.VariableStatement: + return emitVariableStatement(node); + case SyntaxKind.EmptyStatement: + return write(";"); + case SyntaxKind.ExpressionStatement: + return emitExpressionStatement(node); + case SyntaxKind.IfStatement: + return emitIfStatement(node); + case SyntaxKind.DoStatement: + return emitDoStatement(node); + case SyntaxKind.WhileStatement: + return emitWhileStatement(node); + case SyntaxKind.ForStatement: + return emitForStatement(node); + case SyntaxKind.ForInStatement: + return emitForInStatement(node); + case SyntaxKind.ForOfStatement: + return emitForOfStatement(node); + case SyntaxKind.ContinueStatement: + return emitContinueStatement(node); + case SyntaxKind.BreakStatement: + return emitBreakStatement(node); + case SyntaxKind.ReturnStatement: + return emitReturnStatement(node); + case SyntaxKind.WithStatement: + return emitWithStatement(node); + case SyntaxKind.SwitchStatement: + return emitSwitchStatement(node); + case SyntaxKind.LabeledStatement: + return emitLabeledStatement(node); + case SyntaxKind.ThrowStatement: + return emitThrowStatement(node); + case SyntaxKind.TryStatement: + return emitTryStatement(node); + case SyntaxKind.DebuggerStatement: + return emitDebuggerStatement(node); + + // Declarations + case SyntaxKind.VariableDeclaration: + return emitVariableDeclaration(node); + case SyntaxKind.VariableDeclarationList: + return emitVariableDeclarationList(node); + case SyntaxKind.FunctionDeclaration: + return emitFunctionDeclaration(node); + case SyntaxKind.ClassDeclaration: + return emitClassDeclaration(node); + case SyntaxKind.InterfaceDeclaration: + return emitInterfaceDeclaration(node); + case SyntaxKind.TypeAliasDeclaration: + return emitTypeAliasDeclaration(node); + case SyntaxKind.EnumDeclaration: + return emitEnumDeclaration(node); + case SyntaxKind.ModuleDeclaration: + return emitModuleDeclaration(node); + case SyntaxKind.ModuleBlock: + return emitModuleBlock(node); + case SyntaxKind.CaseBlock: + return emitCaseBlock(node); + case SyntaxKind.ImportEqualsDeclaration: + return emitImportEqualsDeclaration(node); + case SyntaxKind.ImportDeclaration: + return emitImportDeclaration(node); + case SyntaxKind.ImportClause: + return emitImportClause(node); + case SyntaxKind.NamespaceImport: + return emitNamespaceImport(node); + case SyntaxKind.NamedImports: + return emitNamedImportsOrExports(node); + case SyntaxKind.ImportSpecifier: + return emitImportOrExportSpecifier(node); + case SyntaxKind.ExportAssignment: + return emitExportAssignment(node); + case SyntaxKind.ExportDeclaration: + return emitExportDeclaration(node); + case SyntaxKind.NamedExports: + return emitNamedImportsOrExports(node); + case SyntaxKind.ExportSpecifier: + return emitImportOrExportSpecifier(node); + case SyntaxKind.MissingDeclaration: + return; + + // Module references + case SyntaxKind.ExternalModuleReference: + return emitExternalModuleReference(node); + + // JSX + case SyntaxKind.JsxElement: + return emitJsxElement(node); + case SyntaxKind.JsxSelfClosingElement: + return emitJsxSelfClosingElement(node); + case SyntaxKind.JsxOpeningElement: + return emitJsxOpeningElement(node); + case SyntaxKind.JsxText: + return emitJsxText(node); + case SyntaxKind.JsxClosingElement: + return emitJsxClosingElement(node); + case SyntaxKind.JsxAttribute: + return emitJsxAttribute(node); + case SyntaxKind.JsxSpreadAttribute: + return emitJsxSpreadAttribute(node); + case SyntaxKind.JsxExpression: + return emitJsxExpression(node); + + // Clauses + case SyntaxKind.CaseClause: + return emitCaseClause(node); + case SyntaxKind.DefaultClause: + return emitDefaultClause(node); + case SyntaxKind.HeritageClause: + return emitHeritageClause(node); + case SyntaxKind.CatchClause: + return emitCatchClause(node); + + // Property assignments + case SyntaxKind.PropertyAssignment: + return emitPropertyAssignment(node); + case SyntaxKind.ShorthandPropertyAssignment: + return emitShorthandPropertyAssignment(node); + + // Enum + case SyntaxKind.EnumMember: + return emitEnumMember(node); + + // Top-level nodes + case SyntaxKind.SourceFile: + return emitSourceFile(node); + + // JSDoc nodes (ignored) + // Raw nodes (deprecated) + case SyntaxKind.RawStatement: + case SyntaxKind.RawExpression: + return emitRaw(node); + } } - emit(node.operand); - } - function shouldEmitWhitespaceBeforeOperand(node: PrefixUnaryExpression) { - // In some cases, we need to emit a space between the operator and the operand. One obvious case - // is when the operator is an identifier, like delete or typeof. We also need to do this for plus - // and minus expressions in certain cases. Specifically, consider the following two cases (parens - // are just for clarity of exposition, and not part of the source code): // - // (+(+1)) - // (+(++1)) + // Literals/Pseudo-literals // - // We need to emit a space in both cases. In the first case, the absence of a space will make - // the resulting expression a prefix increment operation. And in the second, it will make the resulting - // expression a prefix increment whose operand is a plus expression - (++(+x)) - // The same is true of minus of course. - let operand = node.operand; - return isPrefixUnaryExpression(operand) - && ((node.operator === SyntaxKind.PlusToken && (operand.operator === SyntaxKind.PlusToken || operand.operator === SyntaxKind.PlusPlusToken)) - || (node.operator === SyntaxKind.MinusToken && (operand.operator === SyntaxKind.MinusToken || operand.operator === SyntaxKind.MinusMinusToken))); - } - function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { - emit(node.operand); - writeToken(node.operator); - } - - function emitBinaryExpression(node: BinaryExpression) { - // Allow transformers to substitute assignments to handle exports. - if (isAssignmentOperator(node.kind) && tryEmitSubstitute(node, assignmentSubstitution)) { - return; + // SyntaxKind.NumericLiteral + // SyntaxKind.StringLiteral + // SyntaxKind.RegularExpressionLiteral + // SyntaxKind.NoSubstitutionTemplateLiteral + // SyntaxKind.TemplateHead + // SyntaxKind.TemplateMiddle + // SyntaxKind.TemplateTail + function emitLiteral(node: LiteralExpression) { + let text = getLiteralText(node, currentSourceFile, languageVersion); + if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) + && (isStringLiteral(node) || isTemplateLiteralKind(node.kind))) { + writer.writeLiteral(text); + } + else { + write(text); + } } - let isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; - let indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); - let indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); + // + // Identifiers + // - emit(node.left); - increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); - writeTokenNode(node.operatorToken); - increaseIndentIf(indentAfterOperator, " "); - emit(node.right); - decreaseIndentIf(indentBeforeOperator, indentAfterOperator); - } - - function emitConditionalExpression(node: ConditionalExpression) { - let indentBeforeQuestion = needsIndentation(node, node.condition, node.questionToken); - let indentAfterQuestion = needsIndentation(node, node.questionToken, node.whenTrue); - let indentBeforeColon = needsIndentation(node, node.whenTrue, node.colonToken); - let indentAfterColon = needsIndentation(node, node.colonToken, node.whenFalse); - - emit(node.condition); - increaseIndentIf(indentBeforeQuestion, " "); - write("?"); - increaseIndentIf(indentAfterQuestion, " "); - emit(node.whenTrue); - decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion); - - increaseIndentIf(indentBeforeColon, " "); - write(":"); - increaseIndentIf(indentAfterColon); - emit(node.whenFalse); - decreaseIndentIf(indentBeforeColon, indentAfterColon); - } - - function emitTemplateExpression(node: TemplateExpression) { - emit(node.head); - emitList(node, node.templateSpans, ListFormat.TemplateExpressionTemplateSpans); - } - - function emitYieldExpression(node: YieldExpression) { - write(node.asteriskToken ? "yield*" : "yield"); - emitWithPrefix(" ", node.expression); - } - - function emitSpreadElementExpression(node: SpreadElementExpression) { - write("..."); - emit(node.expression); - } - - function emitClassExpression(node: ClassExpression) { - emitClassDeclarationOrExpression(node); - } - - function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - emitStart(node); - emit(node.expression); - emitList(node, node.typeArguments, ListFormat.TypeArguments); - emitEnd(node); - } - - function emitAsExpression(node: AsExpression) { - emit(node.expression); - write(" as "); - emit(node.type); - } - - // - // Misc - // - - function emitTemplateSpan(node: TemplateSpan) { - emit(node.expression); - emit(node.literal); - } - - // - // Statements - // - - function emitBlock(node: Block) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.statements, ListFormat.BlockStatements); - emitEnd(node); - emitTrailingComments(node); - } - - function emitVariableStatement(node: VariableStatement) { - emitLeadingComments(node); - emitStart(node); - emitModifiers(node); - emit(node.declarationList); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitExpressionStatement(node: ExpressionStatement) { - emit(node.expression); - write(";"); - } - - function emitIfStatement(node: IfStatement) { - write("if ("); - emit(node.expression); - write(")"); - emitEmbeddedStatement(node.thenStatement); - if (node.elseStatement) { - writeLine(); - write("else"); - emitEmbeddedStatement(node.elseStatement); - } - } - - function emitDoStatement(node: DoStatement) { - write("do"); - emitEmbeddedStatement(node.statement); - if (isBlock(node.statement)) { - write(" "); - } - else { - writeLine(); + function emitIdentifier(node: Identifier) { + if (isExpressionIdentifier(node)) { + emitExpressionIdentifier(node); + } + else if (nodeIsSynthesized(node)) { + write(node.text); + } + else { + writeTextOfNode(currentSourceFile, node); + } } - write("while ("); - emit(node.expression); - write(");"); - } - - function emitWhileStatement(node: WhileStatement) { - write("while ("); - emit(node.expression); - write(")"); - emitEmbeddedStatement(node.statement); - } - - function emitForStatement(node: ForStatement) { - write("for ("); - emit(node.initializer); - write(";"); - emitWithPrefix(" ", node.condition); - write(";"); - emitWithPrefix(" ", node.incrementor); - write(")"); - emitEmbeddedStatement(node.statement); - } - - function emitForInStatement(node: ForInStatement) { - write("for ("); - emit(node.initializer); - write(" in "); - emit(node.expression); - write(")"); - emitEmbeddedStatement(node.statement); - } - - function emitForOfStatement(node: ForOfStatement) { - write("for ("); - emit(node.initializer); - write(" of "); - emit(node.expression); - write(")"); - emitEmbeddedStatement(node.statement); - } - - function emitContinueStatement(node: ContinueStatement) { - write("continue"); - emitWithPrefix(" ", node.label); - write(";"); - } - - function emitBreakStatement(node: BreakStatement) { - write("break"); - emitWithPrefix(" ", node.label); - write(";"); - } - - function emitReturnStatement(node: ReturnStatement) { - write("return"); - emitWithPrefix(" ", node.expression); - write(";"); - } - - function emitWithStatement(node: WithStatement) { - write("with ("); - emit(node.expression); - write(")"); - emitEmbeddedStatement(node.statement); - } - - function emitSwitchStatement(node: SwitchStatement) { - write("switch ("); - emit(node.expression); - write(") "); - emit(node.caseBlock); - } - - function emitLabeledStatement(node: LabeledStatement) { - emit(node.label); - write(":"); - emitEmbeddedStatement(node.statement); - } - - function emitThrowStatement(node: ThrowStatement) { - write("throw"); - emitWithPrefix(" ", node.expression); - write(";"); - } - - function emitTryStatement(node: TryStatement) { - write("try "); - emit(node.tryBlock); - emit(node.catchClause); - if (node.finallyBlock) { - writeLine(); - write("finally "); - emit(node.finallyBlock); + function isExpressionIdentifier(node: Node): boolean { + let parentNode = getParentNode(); + switch (parentNode.kind) { + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.BinaryExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.CaseClause: + case SyntaxKind.ComputedPropertyName: + case SyntaxKind.ConditionalExpression: + case SyntaxKind.Decorator: + case SyntaxKind.DeleteExpression: + case SyntaxKind.DoStatement: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.ExportAssignment: + case SyntaxKind.ExpressionStatement: + case SyntaxKind.ExpressionWithTypeArguments: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.IfStatement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxSpreadAttribute: + case SyntaxKind.JsxExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.PostfixUnaryExpression: + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.ReturnStatement: + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.SpreadElementExpression: + case SyntaxKind.SwitchStatement: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.TemplateSpan: + case SyntaxKind.ThrowStatement: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: + case SyntaxKind.WhileStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.YieldExpression: + return true; + case SyntaxKind.BindingElement: + case SyntaxKind.EnumMember: + case SyntaxKind.Parameter: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.VariableDeclaration: + return (parentNode).initializer === node; + case SyntaxKind.PropertyAccessExpression: + return (parentNode).expression === node; + case SyntaxKind.ArrowFunction: + case SyntaxKind.FunctionExpression: + return (parentNode).body === node; + case SyntaxKind.ImportEqualsDeclaration: + return (parentNode).moduleReference === node; + case SyntaxKind.QualifiedName: + return (parentNode).left === node; + } + return false; } - } - function emitDebuggerStatement(node: DebuggerStatement) { - write("debugger;"); - } + function emitExpressionIdentifier(node: Identifier) { + if (tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + else if (nodeIsSynthesized(node)) { + if (getGeneratedNodeFlags(node) & GeneratedNodeFlags.UMDDefine) { + writeLines(umdHelper); + } + else { + write(node.text); + } + } + else { + writeTextOfNode(currentSourceFile, node); + } + } - // - // Declarations - // + // + // Names + // - function emitVariableDeclaration(node: VariableDeclaration) { - emit(node.name); - emitWithPrefix(" = ", node.initializer); - } - - function emitVariableDeclarationList(node: VariableDeclarationList) { - write(isLet(node) ? "let " : isConst(node) ? "const " : "var "); - emitList(node, node.declarations, ListFormat.VariableDeclarations); - } - - function emitFunctionDeclaration(node: FunctionDeclaration) { - emitFunctionDeclarationOrExpression(node); - } - - function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - write(node.asteriskToken ? "function*" : "function"); - emitWithPrefix(" ", node.name); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.parameters, ListFormat.Parameters); - emitWithPrefix(": ", node.type); - emitWithPrefix(" ", node.body); - writeIfMissing(node.body, ";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitClassDeclaration(node: ClassDeclaration) { - emitClassDeclarationOrExpression(node); - } - - function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - write("class"); - emitWithPrefix(" ", node.name); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.heritageClauses, ListFormat.HeritageClauses); - write(" "); - emitList(node, node.members, ListFormat.ClassMembers); - emitEnd(node); - emitTrailingComments(node); - } - - function emitInterfaceDeclaration(node: InterfaceDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - write("interface "); - emit(node.name); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - emitList(node, node.heritageClauses, ListFormat.HeritageClauses); - write(" "); - emitList(node, node.members, ListFormat.TypeElements); - emitEnd(node); - emitTrailingComments(node); - } - - function emitTypeAliasDeclaration(node: TypeAliasDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitList(node, node.decorators, ListFormat.Decorators); - emitModifiers(node); - write("type "); - emit(node.name); - emitList(node, node.typeParameters, ListFormat.TypeParameters); - write(" = "); - emit(node.type); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitEnumDeclaration(node: EnumDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitModifiers(node); - write("enum "); - emit(node.name); - write(" "); - emitList(node, node.members, ListFormat.EnumMembers); - emitEnd(node); - emitTrailingComments(node); - } - - function emitModuleDeclaration(node: ModuleDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitModifiers(node); - write(node.flags & NodeFlags.Namespace ? "namespace " : "module "); - emit(node.name); - - let body = node.body; - while (isModuleDeclaration(body)) { + function emitQualifiedName(node: QualifiedName) { + emit(node.left); write("."); - emit((body).name); - body = (body).body; + emit(node.right); } - write(" "); - emit(body); - emitEnd(node); - emitTrailingComments(node); - } - - function emitModuleBlock(node: ModuleBlock) { - emitList(node, node.statements, ListFormat.ModuleBlockStatements); - } - - function emitCaseBlock(node: CaseBlock) { - emitList(node, node.clauses, ListFormat.CaseBlockClauses); - } - - function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitModifiers(node); - write("import "); - emit(node.name); - write(" = "); - emit(node.moduleReference); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitImportDeclaration(node: ImportDeclaration) { - emitLeadingComments(node); - emitStart(node); - emitModifiers(node); - write("import "); - emit(node.importClause); - emit(node.moduleSpecifier); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - - function emitImportClause(node: ImportClause) { - emitStart(node); - emit(node.name); - if (node.name && node.namedBindings) { - write(", "); + function emitComputedPropertyName(node: ComputedPropertyName) { + write("["); + emit(node.expression); + write("]"); } - emit(node.namedBindings); - emitEnd(node); - write(" from "); - } - function emitNamespaceImport(node: NamespaceImport) { - emitStart(node); - write("* as "); - emit(node.name); - emitEnd(node); - } + // + // Signature elements + // - function emitNamedImports(node: NamedImports) { - emitNamedImportsOrExports(node); - } + function emitTypeParameter(node: TypeParameterDeclaration) { + emitStart(node); + emit(node.name); + emitWithPrefix("extends ", node.constraint); + emitEnd(node); + } - function emitImportSpecifier(node: ImportSpecifier) { - emitImportOrExportSpecifier(node); - } + function emitParameter(node: ParameterDeclaration) { + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + writeIfPresent(node.dotDotDotToken, "..."); + emit(node.name); + writeIfPresent(node.questionToken, "?"); + emitWithPrefix(" = ", node.initializer); + emitWithPrefix(": ", node.type); + emitEnd(node); + } - function emitExportAssignment(node: ExportAssignment) { - emitLeadingComments(node); - emitStart(node); - write(node.isExportEquals ? "export = " : "export default "); - emit(node.expression); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } + function emitDecorator(decorator: Decorator) { + emitLeadingComments(decorator); + emitStart(decorator); + write("@"); + emit(decorator.expression); + emitEnd(decorator); + emitTrailingComments(decorator); + } - function emitExportDeclaration(node: ExportDeclaration) { - emitLeadingComments(node); - emitStart(node); - write("export "); - if (node.exportClause) { - emit(node.exportClause); + // + // Type members + // + + function emitPropertySignature(node: PropertySignature) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + emit(node.name); + writeIfPresent(node.questionToken, "?"); + emitWithPrefix(": ", node.type); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitPropertyDeclaration(node: PropertyDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + emit(node.name); + emitWithPrefix(": ", node.type); + emitWithPrefix(" = ", node.initializer); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitMethodSignature(node: MethodSignature) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + emit(node.name); + writeIfPresent(node.questionToken, "?"); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.parameters, ListFormat.Parameters); + emitWithPrefix(": ", node.type); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitMethodDeclaration(node: MethodDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + writeIfPresent(node.asteriskToken, "*"); + emit(node.name); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.parameters, ListFormat.Parameters); + emitWithPrefix(": ", node.type); + emit(node.body); + writeIfMissing(node.body, ";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitConstructor(node: ConstructorDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitModifiers(node); + write("constructor"); + emitList(node, node.parameters, ListFormat.Parameters); + emit(node.body); + writeIfMissing(node.body, ";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitAccessorDeclaration(node: AccessorDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); + emit(node.name); + emitList(node, node.parameters, ListFormat.Parameters); + emitWithPrefix(": ", node.type); + emit(node.body); + writeIfMissing(node.body, ";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitCallSignature(node: CallSignatureDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.parameters, ListFormat.Parameters); + emitWithPrefix(": ", node.type); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitConstructSignature(node: ConstructSignatureDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + write("new "); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.parameters, ListFormat.Parameters); + emitWithPrefix(": ", node.type); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitIndexSignature(node: IndexSignatureDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + emitList(node, node.parameters, ListFormat.IndexSignatureParameters); + emitWithPrefix(": ", node.type); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + // + // Types + // + + function emitTypePredicate(node: TypePredicateNode) { + emitStart(node); + emit(node.parameterName); + write(" is "); + emit(node.type); + emitEnd(node); + } + + function emitTypeReference(node: TypeReferenceNode) { + emitStart(node); + emit(node.typeName); + emitList(node, node.typeArguments, ListFormat.TypeArguments); + emitEnd(node); + } + + function emitFunctionType(node: FunctionTypeNode) { + emitStart(node); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.parameters, ListFormat.ArrowParameters); + write(" => "); + emit(node.type); + emitEnd(node); + } + + function emitConstructorType(node: ConstructorTypeNode) { + emitStart(node); + write("new "); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.parameters, ListFormat.ArrowParameters); + write(" => "); + emit(node.type); + emitEnd(node); + } + + function emitTypeQuery(node: TypeQueryNode) { + emitStart(node); + write("typeof "); + emit(node.exprName); + emitEnd(node); + } + + function emitTypeLiteral(node: TypeLiteralNode) { + emitStart(node); + emitList(node, node.members, ListFormat.TypeElements); + emitEnd(node); + } + + function emitArrayType(node: ArrayTypeNode) { + emitStart(node); + emit(node.elementType); + write("[]"); + emitEnd(node); + } + + function emitTupleType(node: TupleTypeNode) { + emitStart(node); + emitList(node, node.elementTypes, ListFormat.TupleTypeElementTypes); + emitEnd(node); + } + + function emitUnionType(node: UnionTypeNode) { + emitStart(node); + emitList(node, node.types, ListFormat.UnionTypeConstituents) + emitEnd(node); + } + + function emitIntersectionType(node: IntersectionTypeNode) { + emitStart(node); + emitList(node, node.types, ListFormat.IntersectionTypeConstituents); + emitEnd(node); + } + + function emitParenthesizedType(node: ParenthesizedTypeNode) { + write("("); + emit(node.type); + write(")"); + } + + // + // Binding patterns + // + + function emitObjectBindingPattern(node: ObjectBindingPattern) { + emitList(node, node.elements, ListFormat.ObjectBindingPatternElements); + } + + function emitArrayBindingPattern(node: ArrayBindingPattern) { + emitList(node, node.elements, ListFormat.ArrayBindingPatternElements); + } + + function emitBindingElement(node: BindingElement) { + emitWithSuffix(node.propertyName, ": "); + writeIfPresent(node.dotDotDotToken, "..."); + emit(node.name); + emitWithPrefix(" = ", node.initializer); + } + + // + // Expressions + // + + function emitArrayLiteralExpression(node: ArrayLiteralExpression) { + emitList(node, node.elements, ListFormat.ArrayLiteralExpressionElements); + } + + function emitObjectLiteralExpression(node: ObjectLiteralExpression) { + emitList(node, node.properties, languageVersion < ScriptTarget.ES5 + ? ListFormat.ObjectLiteralExpressionProperties + : ListFormat.ObjectLiteralExpressionPropertiesForES5AndLater); + } + + function emitPropertyAccessExpression(node: PropertyAccessExpression) { + if (tryEmitConstantValue(node)) { + return; + } + + let indentBeforeDot = needsIndentation(node, node.expression, node.dotToken); + let indentAfterDot = needsIndentation(node, node.dotToken, node.name); + let shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); + + emit(node.expression); + increaseIndentIf(indentBeforeDot); + write(shouldEmitDotDot ? ".." : "."); + increaseIndentIf(indentAfterDot); + emit(node.name); + decreaseIndentIf(indentBeforeDot, indentAfterDot); + } + + // 1..toString is a valid property access, emit a dot after the literal + // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal + function needsDotDotForPropertyAccess(expression: Expression) { + if (isNumericLiteral(expression)) { + // check if numeric literal was originally written with a dot + let text = getLiteralText(expression, currentSourceFile, languageVersion); + return text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; + } + else { + // check if constant enum value is integer + let constantValue = tryGetConstEnumValue(expression); + // isFinite handles cases when constantValue is undefined + return isFinite(constantValue) && Math.floor(constantValue) === constantValue; + } + return false; + } + + function emitElementAccessExpression(node: ElementAccessExpression) { + if (tryEmitConstantValue(node)) { + return; + } + + emit(node.expression); + write("["); + emit(node.argumentExpression); + write("]"); + } + + function emitCallExpression(node: CallExpression) { + emit(node.expression); + emitList(node, node.arguments, ListFormat.CallExpressionArguments); + } + + function emitNewExpression(node: NewExpression) { + write("new "); + emit(node.expression); + emitList(node, node.arguments, ListFormat.NewExpressionArguments); + } + + function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { + emit(node.tag); + emit(node.template); + } + + function emitTypeAssertionExpression(node: TypeAssertion) { + write("<"); + emit(node.type); + write(">"); + emit(node.expression); + } + + function emitParenthesizedExpression(node: ParenthesizedExpression) { + write("("); + emit(node.expression); + write(")"); + } + + function emitFunctionExpression(node: FunctionExpression) { + emitFunctionDeclarationOrExpression(node); + } + + function emitArrowFunction(node: ArrowFunction) { + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.parameters, ListFormat.ArrowParameters); + emitWithPrefix(": ", node.type); + emit(node.body); + emitEnd(node); + } + + function emitDeleteExpression(node: DeleteExpression) { + write("delete "); + emit(node.expression); + } + + function emitTypeOfExpression(node: TypeOfExpression) { + write("typeof "); + emit(node.expression); + } + + function emitVoidExpression(node: VoidExpression) { + write("void "); + emit(node.expression); + } + + function emitAwaitExpression(node: AwaitExpression) { + write("await "); + emit(node.expression); + } + + function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { + writeToken(node.operator); + if (shouldEmitWhitespaceBeforeOperand(node)) { + write(" "); + } + emit(node.operand); + } + + function shouldEmitWhitespaceBeforeOperand(node: PrefixUnaryExpression) { + // In some cases, we need to emit a space between the operator and the operand. One obvious case + // is when the operator is an identifier, like delete or typeof. We also need to do this for plus + // and minus expressions in certain cases. Specifically, consider the following two cases (parens + // are just for clarity of exposition, and not part of the source code): + // + // (+(+1)) + // (+(++1)) + // + // We need to emit a space in both cases. In the first case, the absence of a space will make + // the resulting expression a prefix increment operation. And in the second, it will make the resulting + // expression a prefix increment whose operand is a plus expression - (++(+x)) + // The same is true of minus of course. + let operand = node.operand; + return isPrefixUnaryExpression(operand) + && ((node.operator === SyntaxKind.PlusToken && (operand.operator === SyntaxKind.PlusToken || operand.operator === SyntaxKind.PlusPlusToken)) + || (node.operator === SyntaxKind.MinusToken && (operand.operator === SyntaxKind.MinusToken || operand.operator === SyntaxKind.MinusMinusToken))); + } + + function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { + if (tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + + emit(node.operand); + writeToken(node.operator); + } + + function emitBinaryExpression(node: BinaryExpression) { + if (tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + + let isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; + let indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); + let indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); + + emit(node.left); + increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); + writeTokenNode(node.operatorToken); + increaseIndentIf(indentAfterOperator, " "); + emit(node.right); + decreaseIndentIf(indentBeforeOperator, indentAfterOperator); + } + + function emitConditionalExpression(node: ConditionalExpression) { + let indentBeforeQuestion = needsIndentation(node, node.condition, node.questionToken); + let indentAfterQuestion = needsIndentation(node, node.questionToken, node.whenTrue); + let indentBeforeColon = needsIndentation(node, node.whenTrue, node.colonToken); + let indentAfterColon = needsIndentation(node, node.colonToken, node.whenFalse); + + emit(node.condition); + increaseIndentIf(indentBeforeQuestion, " "); + write("?"); + increaseIndentIf(indentAfterQuestion, " "); + emit(node.whenTrue); + decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion); + + increaseIndentIf(indentBeforeColon, " "); + write(":"); + increaseIndentIf(indentAfterColon); + emit(node.whenFalse); + decreaseIndentIf(indentBeforeColon, indentAfterColon); + } + + function emitTemplateExpression(node: TemplateExpression) { + emit(node.head); + emitList(node, node.templateSpans, ListFormat.TemplateExpressionTemplateSpans); + } + + function emitYieldExpression(node: YieldExpression) { + write(node.asteriskToken ? "yield*" : "yield"); + emitWithPrefix(" ", node.expression); + } + + function emitSpreadElementExpression(node: SpreadElementExpression) { + write("..."); + emit(node.expression); + } + + function emitClassExpression(node: ClassExpression) { + emitClassDeclarationOrExpression(node); + } + + function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { + emitStart(node); + emit(node.expression); + emitList(node, node.typeArguments, ListFormat.TypeArguments); + emitEnd(node); + } + + function emitAsExpression(node: AsExpression) { + emit(node.expression); + write(" as "); + emit(node.type); + } + + // + // Misc + // + + function emitTemplateSpan(node: TemplateSpan) { + emit(node.expression); + emit(node.literal); + } + + // + // Statements + // + + function emitBlock(node: Block, isFunctionBody?: boolean) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.statements, addHelpers(node, isFunctionBody ? ListFormat.FunctionBodyStatements : ListFormat.BlockStatements)); + emitEnd(node); + emitTrailingComments(node); + } + + function emitVariableStatement(node: VariableStatement) { + emitLeadingComments(node); + emitStart(node); + emitModifiers(node); + emit(node.declarationList); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitExpressionStatement(node: ExpressionStatement) { + emit(node.expression); + write(";"); + } + + function emitIfStatement(node: IfStatement) { + write("if ("); + emit(node.expression); + write(")"); + emitEmbeddedStatement(node.thenStatement); + if (node.elseStatement) { + writeLine(); + write("else"); + emitEmbeddedStatement(node.elseStatement); + } + } + + function emitDoStatement(node: DoStatement) { + write("do"); + emitEmbeddedStatement(node.statement); + if (isBlock(node.statement)) { + write(" "); + } + else { + writeLine(); + } + + write("while ("); + emit(node.expression); + write(");"); + } + + function emitWhileStatement(node: WhileStatement) { + write("while ("); + emit(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitForStatement(node: ForStatement) { + write("for ("); + emit(node.initializer); + write(";"); + emitWithPrefix(" ", node.condition); + write(";"); + emitWithPrefix(" ", node.incrementor); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitForInStatement(node: ForInStatement) { + write("for ("); + emit(node.initializer); + write(" in "); + emit(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitForOfStatement(node: ForOfStatement) { + write("for ("); + emit(node.initializer); + write(" of "); + emit(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitContinueStatement(node: ContinueStatement) { + write("continue"); + emitWithPrefix(" ", node.label); + write(";"); + } + + function emitBreakStatement(node: BreakStatement) { + write("break"); + emitWithPrefix(" ", node.label); + write(";"); + } + + function emitReturnStatement(node: ReturnStatement) { + write("return"); + emitWithPrefix(" ", node.expression); + write(";"); + } + + function emitWithStatement(node: WithStatement) { + write("with ("); + emit(node.expression); + write(")"); + emitEmbeddedStatement(node.statement); + } + + function emitSwitchStatement(node: SwitchStatement) { + write("switch ("); + emit(node.expression); + write(") "); + emit(node.caseBlock); + } + + function emitLabeledStatement(node: LabeledStatement) { + emit(node.label); + write(":"); + emitEmbeddedStatement(node.statement); + } + + function emitThrowStatement(node: ThrowStatement) { + write("throw"); + emitWithPrefix(" ", node.expression); + write(";"); + } + + function emitTryStatement(node: TryStatement) { + write("try "); + emit(node.tryBlock); + emit(node.catchClause); + if (node.finallyBlock) { + writeLine(); + write("finally "); + emit(node.finallyBlock); + } + } + + function emitDebuggerStatement(node: DebuggerStatement) { + write("debugger;"); + } + + // + // Declarations + // + + function emitVariableDeclaration(node: VariableDeclaration) { + emit(node.name); + emitWithPrefix(" = ", node.initializer); + } + + function emitVariableDeclarationList(node: VariableDeclarationList) { + write(isLet(node) ? "let " : isConst(node) ? "const " : "var "); + emitList(node, node.declarations, ListFormat.VariableDeclarations); + } + + function emitFunctionDeclaration(node: FunctionDeclaration) { + emitFunctionDeclarationOrExpression(node); + } + + function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + write(node.asteriskToken ? "function* " : "function "); + emit(node.name); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.parameters, ListFormat.Parameters); + emitWithPrefix(": ", node.type); + emitFunctionBody(node.body); + emitEnd(node); + emitTrailingComments(node); + } + + function emitFunctionBody(node: Block) { + if (node) { + write(" "); + let wasPushed = tryPushNode(node); + emitBlock(node, /*isFunctionBody*/ true); + if (wasPushed) { + popNode(); + } + } + else { + write(";"); + } + } + + function emitClassDeclaration(node: ClassDeclaration) { + emitClassDeclarationOrExpression(node); + } + + function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + write("class"); + emitWithPrefix(" ", node.name); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.heritageClauses, ListFormat.HeritageClauses); + write(" "); + emitList(node, node.members, ListFormat.ClassMembers); + emitEnd(node); + emitTrailingComments(node); + } + + function emitInterfaceDeclaration(node: InterfaceDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + write("interface "); + emit(node.name); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + emitList(node, node.heritageClauses, ListFormat.HeritageClauses); + write(" "); + emitList(node, node.members, ListFormat.TypeElements); + emitEnd(node); + emitTrailingComments(node); + } + + function emitTypeAliasDeclaration(node: TypeAliasDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitList(node, node.decorators, ListFormat.Decorators); + emitModifiers(node); + write("type "); + emit(node.name); + emitList(node, node.typeParameters, ListFormat.TypeParameters); + write(" = "); + emit(node.type); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitEnumDeclaration(node: EnumDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitModifiers(node); + write("enum "); + emit(node.name); + write(" "); + emitList(node, node.members, ListFormat.EnumMembers); + emitEnd(node); + emitTrailingComments(node); + } + + function emitModuleDeclaration(node: ModuleDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitModifiers(node); + write(node.flags & NodeFlags.Namespace ? "namespace " : "module "); + emit(node.name); + + let body = node.body; + while (isModuleDeclaration(body)) { + write("."); + emit((body).name); + body = (body).body; + } + + write(" "); + emit(body); + emitEnd(node); + emitTrailingComments(node); + } + + function emitModuleBlock(node: ModuleBlock) { + emitList(node, node.statements, ListFormat.ModuleBlockStatements); + } + + function emitCaseBlock(node: CaseBlock) { + emitList(node, node.clauses, ListFormat.CaseBlockClauses); + } + + function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitModifiers(node); + write("import "); + emit(node.name); + write(" = "); + emit(node.moduleReference); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitImportDeclaration(node: ImportDeclaration) { + emitLeadingComments(node); + emitStart(node); + emitModifiers(node); + write("import "); + emit(node.importClause); + emit(node.moduleSpecifier); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitImportClause(node: ImportClause) { + emitStart(node); + emit(node.name); + if (node.name && node.namedBindings) { + write(", "); + } + emit(node.namedBindings); + emitEnd(node); write(" from "); } - else { - write("* from "); - } - emit(node.moduleSpecifier); - write(";"); - emitEnd(node); - emitTrailingComments(node); - } - function emitNamedExports(node: NamedExports) { - emitNamedImportsOrExports(node); - } - - function emitExportSpecifier(node: ExportSpecifier) { - emitImportOrExportSpecifier(node); - } - - function emitNamedImportsOrExports(node: NamedImportsOrExports) { - emitStart(node); - emitList(node, node.elements, ListFormat.ImportOrExportSpecifiers); - emitEnd(node); - } - - function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { - emitLeadingComments(node); - emitStart(node); - if (node.propertyName) { - emit(node.propertyName); - write(" as "); + function emitNamespaceImport(node: NamespaceImport) { + emitStart(node); + write("* as "); + emit(node.name); + emitEnd(node); } - emit(node.name); - emitEnd(node); - emitTrailingComments(node); - } - - // - // Module references - // - - function emitExternalModuleReference(node: ExternalModuleReference) { - write("require("); - emit(node.expression); - write(")"); - } - - // - // JSX - // - - function emitJsxElement(node: JsxElement) { - emit(node.openingElement); - emitList(node, node.children, ListFormat.JsxElementChildren); - emit(node.closingElement); - } - - function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { - write("<"); - emit(node.tagName); - write(" "); - emitList(node, node.attributes, ListFormat.JsxAttributes); - write("/>"); - } - - function emitJsxOpeningElement(node: JsxOpeningElement) { - write("<"); - emit(node.tagName); - writeIfAny(node.attributes, " "); - emitList(node, node.attributes, ListFormat.JsxAttributes); - write(">"); - } - - function emitJsxText(node: JsxText) { - writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true)); - } - - function emitJsxClosingElement(node: JsxClosingElement) { - write(""); - } - - function emitJsxAttribute(node: JsxAttribute) { - emit(node.name); - emitWithPrefix("=", node.initializer); - } - - function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { - write("{..."); - emit(node.expression); - write("}"); - } - - function emitJsxExpression(node: JsxExpression) { - write("{"); - emit(node.expression); - write("}"); - } - - // - // Clauses - // - - function emitCaseClause(node: CaseClause) { - write("case "); - emit(node.expression); - write(":"); - emitList(node, node.statements, ListFormat.CaseOrDefaultClauseStatements); - } - - function emitDefaultClause(node: DefaultClause) { - write("default:"); - emitList(node, node.statements, ListFormat.CaseOrDefaultClauseStatements); - } - - function emitHeritageClause(node: HeritageClause) { - emitStart(node); - writeToken(node.token); - write(" "); - emitList(node, node.types, ListFormat.HeritageClauseTypes); - emitEnd(node); - } - - function emitCatchClause(node: CatchClause) { - writeLine(); - write("catch ("); - emit(node.variableDeclaration); - write(") "); - emit(node.block); - } - - // - // Property assignments - // - - function emitPropertyAssignment(node: PropertyAssignment) { - emit(node.name); - write(": "); - // This is to ensure that we emit comment in the following case: - // For example: - // obj = { - // id: /*comment1*/ ()=>void - // } - // "comment1" is not considered to be leading comment for node.initializer - // but rather a trailing comment on the previous node. - emitTrailingCommentsOfPosition(node.initializer.pos); - emit(node.initializer); - } - - function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { - emit(node.name); - } - - // - // Enum - // - - function emitEnumMember(node: EnumMember) { - emitLeadingComments(node); - emitStart(node); - emit(node.name); - emitWithPrefix(" = ", node.initializer); - emitEnd(node); - emitTrailingComments(node); - } - - // - // Top-level nodes - // - - function emitSourceFile(node: SourceFile) { - currentSourceFile = node; - - writeLine(); - emitShebang(); - emitDetachedComments(node); - emitList(node, node.statements, ListFormat.SourceFileStatements); - - currentSourceFile = undefined; - } - - // - // Helpers - // - - function emitShebang() { - let shebang = getShebang(currentSourceFile.text); - if (shebang) { - write(shebang); + function emitNamedImports(node: NamedImports) { + emitNamedImportsOrExports(node); } - } - function emitModifiers(node: Node) { - if (node.flags & NodeFlags.Export) { + function emitImportSpecifier(node: ImportSpecifier) { + emitImportOrExportSpecifier(node); + } + + function emitExportAssignment(node: ExportAssignment) { + emitLeadingComments(node); + emitStart(node); + write(node.isExportEquals ? "export = " : "export default "); + emit(node.expression); + write(";"); + emitEnd(node); + emitTrailingComments(node); + } + + function emitExportDeclaration(node: ExportDeclaration) { + emitLeadingComments(node); + emitStart(node); write("export "); + if (node.exportClause) { + emit(node.exportClause); + write(" from "); + } + else { + write("* from "); + } + emit(node.moduleSpecifier); + write(";"); + emitEnd(node); + emitTrailingComments(node); } - if (node.flags & NodeFlags.Default) { - write("default "); - } - if (node.flags & NodeFlags.Ambient) { - write("declare "); - } - if (isConstEnumDeclaration(node)) { - write("const "); - } - if (node.flags & NodeFlags.Public) { - write("public "); - } - else if (node.flags & NodeFlags.Protected) { - write("protected "); - } - else if (node.flags & NodeFlags.Private) { - write("private "); - } - if (node.flags & NodeFlags.Static) { - write("static "); - } - if (node.flags & NodeFlags.Async) { - write("async "); - } - if (node.flags & NodeFlags.Abstract) { - write("abstract "); - } - } - function emitWithPrefix(prefix: string, node: Node) { - if (node) { - write(prefix); - emit(node); + function emitNamedExports(node: NamedExports) { + emitNamedImportsOrExports(node); } - } - function emitWithSuffix(node: Node, suffix: string) { - if (node) { - emit(node); - write(suffix); + function emitExportSpecifier(node: ExportSpecifier) { + emitImportOrExportSpecifier(node); } - } - function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { - let constantValue = tryGetConstEnumValue(node); - if (constantValue !== undefined) { - write(String(constantValue)); - if (!compilerOptions.removeComments) { - let propertyName = isPropertyAccessExpression(node) - ? declarationNameToString(node.name) - : getTextOfNode(node.argumentExpression); - write(` /* ${propertyName} */`); + function emitNamedImportsOrExports(node: NamedImportsOrExports) { + emitStart(node); + emitList(node, node.elements, ListFormat.ImportOrExportSpecifiers); + emitEnd(node); + } + + function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { + emitLeadingComments(node); + emitStart(node); + if (node.propertyName) { + emit(node.propertyName); + write(" as "); } - return true; + emit(node.name); + emitEnd(node); + emitTrailingComments(node); } - return false; - } + // + // Module references + // - function emitEmbeddedStatement(node: Statement) { - if (isBlock(node)) { + function emitExternalModuleReference(node: ExternalModuleReference) { + write("require("); + emit(node.expression); + write(")"); + } + + // + // JSX + // + + function emitJsxElement(node: JsxElement) { + emit(node.openingElement); + emitList(node, node.children, ListFormat.JsxElementChildren); + emit(node.closingElement); + } + + function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { + write("<"); + emit(node.tagName); write(" "); - emit(node); + emitList(node, node.attributes, ListFormat.JsxAttributes); + write("/>"); } - else { + + function emitJsxOpeningElement(node: JsxOpeningElement) { + write("<"); + emit(node.tagName); + writeIfAny(node.attributes, " "); + emitList(node, node.attributes, ListFormat.JsxAttributes); + write(">"); + } + + function emitJsxText(node: JsxText) { + writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true)); + } + + function emitJsxClosingElement(node: JsxClosingElement) { + write(""); + } + + function emitJsxAttribute(node: JsxAttribute) { + emit(node.name); + emitWithPrefix("=", node.initializer); + } + + function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { + write("{..."); + emit(node.expression); + write("}"); + } + + function emitJsxExpression(node: JsxExpression) { + write("{"); + emit(node.expression); + write("}"); + } + + // + // Clauses + // + + function emitCaseClause(node: CaseClause) { + write("case "); + emit(node.expression); + write(":"); + emitList(node, node.statements, ListFormat.CaseOrDefaultClauseStatements); + } + + function emitDefaultClause(node: DefaultClause) { + write("default:"); + emitList(node, node.statements, ListFormat.CaseOrDefaultClauseStatements); + } + + function emitHeritageClause(node: HeritageClause) { + emitStart(node); + writeToken(node.token); + write(" "); + emitList(node, node.types, ListFormat.HeritageClauseTypes); + emitEnd(node); + } + + function emitCatchClause(node: CatchClause) { + writeLine(); + write("catch ("); + emit(node.variableDeclaration); + write(") "); + emit(node.block); + } + + // + // Property assignments + // + + function emitPropertyAssignment(node: PropertyAssignment) { + emit(node.name); + write(": "); + // This is to ensure that we emit comment in the following case: + // For example: + // obj = { + // id: /*comment1*/ ()=>void + // } + // "comment1" is not considered to be leading comment for node.initializer + // but rather a trailing comment on the previous node. + emitTrailingCommentsOfPosition(node.initializer.pos); + emit(node.initializer); + } + + function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { + emit(node.name); + } + + // + // Enum + // + + function emitEnumMember(node: EnumMember) { + emitLeadingComments(node); + emitStart(node); + emit(node.name); + emitWithPrefix(" = ", node.initializer); + emitEnd(node); + emitTrailingComments(node); + } + + // + // Top-level nodes + // + + function emitSourceFile(node: SourceFile) { + currentSourceFile = node; + writeLine(); + emitShebang(); + emitDetachedComments(node); + emitList(node, node.statements, addHelpers(node, ListFormat.SourceFileStatements)); + currentSourceFile = undefined; + } + + function emitLexicalEnvironment(node: Statement) { writeLine(); - increaseIndent(); emit(node); - decreaseIndent(); - } - } - - function emitList(parentNode: Node, children: NodeArray, format: ListFormat) { - if (!children && format & ListFormat.Optional) { - return; } - let containingRange: TextRange = format & ListFormat.Fragment ? children : parentNode; - - // Shortcut for an empty list. - if (!children || children.length === 0) { - // If the list is bracketed, emit the brackets and any interceeding whitespace. - if (format & ListFormat.BracketsMask) { - write(getBracket(format, ListFormat.LeadingBracketOffset)); - if (containingRange && !positionsAreOnSameLine(containingRange.pos, containingRange.end)) { - writeLine(); - } - else if (format & ListFormat.Whitespace) { - write(" "); - } - write(getBracket(format, ListFormat.TrailingBracketOffset)); + function emitPrologueDirectives(statements: Node[], start?: number, count?: number) { + if (start === undefined) { + start = 0; } - return; - } - let previousSibling: Node; - let firstChild = firstOrUndefined(children); - let lastChild = lastOrUndefined(children); + if (count === undefined) { + count = statements.length - start; + } - // Shortcut for a simple arrow function. - if (format & ListFormat.Arrow && - children.length === 1 && - isParameter(firstChild) && - firstChild.type === undefined && - firstChild.pos === parentNode.pos) { - emit(firstChild); - return; - } + for (let i = 0; i < count; i++) { + let offset = start + i; + if (isPrologueDirective(statements[offset])) { + if (i > 0) { + writeLine(); + } - // Write the leading bracket. - write(getBracket(format, ListFormat.LeadingBracketOffset)); - - // Start the new scope, if requested. - if (format & ListFormat.Scoped) { - scopeEmitStart(parentNode); - } - - // Write the opening line terminator or leading whitespace. - if (shouldWriteLeadingLineTerminator(containingRange, firstChild, format)) { - writeLine(); - } - else if (format & ListFormat.LeadingWhitespace) { - write(" "); - } - - // Increase the indent, if requested. - if (format & ListFormat.Indented) { - increaseIndent(); - } - - // Emit each child. - let delimiter = getDelimiter(format); - for (let child of children) { - // Write the delimiter if this is not the first node. - if (previousSibling) { - write(delimiter); - - // Write either a line terminator or whitespace to separate the elements. - if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) { - writeLine(); + emit(statements[offset]); } - else if (previousSibling) { - write(" "); + else { + return i; } } - // Emit this child. - emit(child); - previousSibling = child; + return count; } - // Write a trailing comma, if requested. - let hasTrailingComma = (format & ListFormat.AllowTrailingComma) && children.hasTrailingComma; - if (format & ListFormat.CommaDelimited && hasTrailingComma) { - write(","); + function emitEmitHelpers(node: SourceFile) { + let flags = resolver.getNodeCheckFlags(node); + + // Only emit helpers if the user did not say otherwise. + if (!compilerOptions.noEmitHelpers) { + if (shouldEmitHelper(NodeCheckFlags.EmitExtends, flags) && languageVersion < ScriptTarget.ES6) { + writeLines(extendsHelper); + helpersEmitted |= NodeCheckFlags.EmitExtends; + } + + if (shouldEmitHelper(NodeCheckFlags.EmitDecorate, flags)) { + writeLines(decorateHelper); + if (compilerOptions.emitDecoratorMetadata) { + writeLines(metadataHelper); + } + + helpersEmitted |= NodeCheckFlags.EmitDecorate; + } + + if (shouldEmitHelper(NodeCheckFlags.EmitParam, flags)) { + writeLines(paramHelper); + helpersEmitted |= NodeCheckFlags.EmitParam; + } + + if (shouldEmitHelper(NodeCheckFlags.EmitAwaiter, flags)) { + writeLines(awaiterHelper); + helpersEmitted |= NodeCheckFlags.EmitAwaiter; + } + } } - // Decrease the indent, if requested. - if (format & ListFormat.Indented) { - decreaseIndent(); + function emitExportStar() { + writeLines(exportStarHelper); } - // Write the closing line terminator or closing whitespace. - if (shouldWriteClosingLineTerminator(containingRange, lastChild, format)) { - writeLine(); - } - else if (format & ListFormat.TrailingWhitespace && (previousSibling || hasTrailingComma)) { - write(" "); + function shouldEmitHelper(helper: NodeCheckFlags, requestedHelpers: NodeCheckFlags) { + return (requestedHelpers & helper) !== 0 + && !(helpersEmitted && helper); } - // End the scope, if requested. - if (format & ListFormat.Scoped) { - scopeEmitEnd(); + function emitRaw(node: RawStatement | RawExpression) { + writeLines(node.text); } - // Write the trailing bracket. - write(getBracket(format, ListFormat.TrailingBracketOffset)); - } - - function writeIfAny(nodes: NodeArray, text: string) { - if (nodes && nodes.length > 0) { - write(text); + function writeLines(text: string): void { + let lines = text.split(/\r\n|\r|\n/g); + for (let i = 0; i < lines.length; ++i) { + let line = lines[i]; + if (line.length) { + if (i > 0) { + writeLine(); + } + write(line); + } + } } - } - function writeIfPresent(node: Node, text: string) { - if (node !== undefined) { - write(text); - } - } + // + // Helpers + // - function writeIfMissing(node: Node, text: string) { - if (node === undefined) { - write(text); + function emitShebang() { + let shebang = getShebang(currentSourceFile.text); + if (shebang) { + write(shebang); + } } - } - function writeToken(token: SyntaxKind) { - write(tokenToString(token)); - } + function emitModifiers(node: Node) { + if (node.flags & NodeFlags.Export) { + write("export "); + } + if (node.flags & NodeFlags.Default) { + write("default "); + } + if (node.flags & NodeFlags.Ambient) { + write("declare "); + } + if (isConstEnumDeclaration(node)) { + write("const "); + } + if (node.flags & NodeFlags.Public) { + write("public "); + } + else if (node.flags & NodeFlags.Protected) { + write("protected "); + } + else if (node.flags & NodeFlags.Private) { + write("private "); + } + if (node.flags & NodeFlags.Static) { + write("static "); + } + if (node.flags & NodeFlags.Async) { + write("async "); + } + if (node.flags & NodeFlags.Abstract) { + write("abstract "); + } + } - function writeTokenNode(node: Node) { - if (node) { - writeToken(node.kind); + function emitWithPrefix(prefix: string, node: Node) { + if (node) { + write(prefix); + emit(node); + } } - } - function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) { - if (value) { - increaseIndent(); - writeLine(); + function emitWithSuffix(node: Node, suffix: string) { + if (node) { + emit(node); + write(suffix); + } } - else if (valueToWriteWhenNotIndenting) { - write(valueToWriteWhenNotIndenting); - } - } - // Helper function to decrease the indent if we previously indented. Allows multiple - // previous indent values to be considered at a time. This also allows caller to just - // call this once, passing in all their appropriate indent values, instead of needing - // to call this helper function multiple times. - function decreaseIndentIf(value1: boolean, value2?: boolean) { - if (value1) { - decreaseIndent(); - } - if (value2) { - decreaseIndent(); - } - } + function tryEmitSubstitute(node: Node, substitution: (node: Node) => Node) { + let substitute = substitution ? substitution(node) : node; + if (substitute && substitute !== node) { + setNode(substitute); + emitNodeWorker(substitute); + setNode(node); + return true; + } - function shouldWriteLeadingLineTerminator(containingRange: TextRange, firstChild: Node, format?: ListFormat) { - if (containingRange === undefined) { return false; } - else if (firstChild === undefined) { - return !positionsAreOnSameLine(getStartPos(containingRange), getEndPos(containingRange)); - } - else if (positionIsSynthesized(containingRange.pos)) { - return format & ListFormat.PreferNewLine ? true : synthesizedNodeStartsOnNewLine(firstChild); - } - else if (nodeIsSynthesized(firstChild)) { - return format & ListFormat.PreferNewLine ? true : (firstChild).startsOnNewLine; - } - else { - return !rangeStartPositionsAreOnSameLine(containingRange, firstChild); - } - } - function shouldWriteSeparatingLineTerminator(previousNode: Node, nextNode: Node, format?: ListFormat) { - if (previousNode === undefined || nextNode === undefined) { + function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { + let constantValue = tryGetConstEnumValue(node); + if (constantValue !== undefined) { + write(String(constantValue)); + if (!compilerOptions.removeComments) { + let propertyName = isPropertyAccessExpression(node) + ? declarationNameToString(node.name) + : getTextOfNode(node.argumentExpression); + write(` /* ${propertyName} */`); + } + + return true; + } + return false; } - else if (nodeIsSynthesized(previousNode) || nodeIsSynthesized(nextNode)) { - return format & ListFormat.PreferNewLine ? true : synthesizedNodeStartsOnNewLine(previousNode) || synthesizedNodeStartsOnNewLine(nextNode); - } - else { - return !rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode); - } - } - function shouldWriteClosingLineTerminator(containingRange: TextRange, lastChild: Node, format?: ListFormat) { - if (lastChild === undefined) { - return false; - } - else if (positionIsSynthesized(containingRange.pos)) { - return format & ListFormat.PreferNewLine ? true : synthesizedNodeStartsOnNewLine(lastChild); - } - else if (nodeIsSynthesized(lastChild)) { - return format & ListFormat.PreferNewLine ? true : (lastChild).startsOnNewLine; - } - else { - return !rangeEndPositionsAreOnSameLine(containingRange, lastChild); - } - } - - function synthesizedNodeStartsOnNewLine(node: Node) { - return nodeIsSynthesized(node) && (node).startsOnNewLine; - } - - function rangeStartPositionsAreOnSameLine(range1: TextRange, range2: TextRange) { - return positionsAreOnSameLine(getStartPos(range1), getStartPos(range2)); - } - - function rangeEndPositionsAreOnSameLine(range1: TextRange, range2: TextRange) { - return positionsAreOnSameLine(getEndPos(range1), getEndPos(range2)); - } - - function rangeEndIsOnSameLineAsRangeStart(range1: TextRange, range2: TextRange) { - return positionsAreOnSameLine(getEndPos(range1), getStartPos(range2)); - } - - function positionsAreOnSameLine(pos1: number, pos2: number) { - return pos1 === pos2 || - getLineOfLocalPosition(currentSourceFile, pos1) === getLineOfLocalPosition(currentSourceFile, pos2); - } - - function getStartPos(range: TextRange) { - return skipTrivia(currentSourceFile.text, range.pos); - } - - function getEndPos(range: TextRange) { - return range.end; - } - - function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { - // Always use a newline for synthesized code if the synthesizer desires it. - if (synthesizedNodeStartsOnNewLine(node2)) { - return true; + function emitEmbeddedStatement(node: Statement) { + if (isBlock(node)) { + write(" "); + emit(node); + } + else { + writeLine(); + increaseIndent(); + emit(node); + decreaseIndent(); + } } - return !nodeIsSynthesized(parent) - && !nodeIsSynthesized(node1) - && !nodeIsSynthesized(node2) - && !rangeEndIsOnSameLineAsRangeStart(node1, node2); - } + function emitList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { + if (!children && format & ListFormat.Optional) { + return; + } - function getTextOfNode(node: Node, includeTrivia?: boolean) { - if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) { - return node.text; + let containingRange: TextRange = format & ListFormat.Fragment ? children : parentNode; + + // Shortcut for an empty list. + if (!children || children.length === 0 || start >= children.length || count === 0) { + // If the list is bracketed, emit the brackets and any interceeding whitespace. + if (format & ListFormat.BracketsMask) { + write(getBracket(format, ListFormat.LeadingBracketOffset)); + if (containingRange && !positionsAreOnSameLine(containingRange.pos, containingRange.end)) { + writeLine(); + } + else if (format & ListFormat.Whitespace) { + write(" "); + } + write(getBracket(format, ListFormat.TrailingBracketOffset)); + } + return; + } + + if (start === undefined) { + start = 0; + } + + if (count === undefined) { + count = children.length - start; + } + + let previousSibling: Node; + let firstChild = start < children.length ? children[start] : undefined; + let lastChild = start + count <= children.length ? children[start + count - 1] : undefined; + + // Shortcut for a simple arrow function. + if (format & ListFormat.Arrow && + children.length === 1 && + isParameter(firstChild) && + firstChild.type === undefined && + firstChild.pos === parentNode.pos) { + emit(firstChild); + return; + } + + // Write the leading bracket. + write(getBracket(format, ListFormat.LeadingBracketOffset)); + + // Start the new scope, if requested. + if (format & ListFormat.Scoped) { + scopeEmitStart(parentNode); + } + + // Write the opening line terminator or leading whitespace. + if (shouldWriteLeadingLineTerminator(containingRange, firstChild, format)) { + writeLine(); + } + else if (format & ListFormat.LeadingWhitespace) { + write(" "); + } + + // Increase the indent, if requested. + let indented = format & ListFormat.Indented; + if (indented) { + increaseIndent(); + } + + if (format & ListFormat.LexicalEnvironment) { + startLexicalEnvironment(); + } + + // Print prologue directives, if needed + if (format & ListFormat.MustEmitPrologueDirectivesFirst) { + let directivesWritten = emitPrologueDirectives(children, start, count); + start += directivesWritten; + count -= directivesWritten; + } + + // Print the emit helpers, if requested. + if (format & ListFormat.PrintEmitHelpers) { + emitEmitHelpers(currentSourceFile); + } + + // Print the __export helper, if requested. + if (format & ListFormat.PrintExportStar) { + emitExportStar(); + } + + // Emit each child. + let canBeDedented = indented && canHaveDedentedChildren(parentNode); + let delimiter = getDelimiter(format); + for (let i = 0; i < count; i++) { + let child = children[start + i]; + + // Write the delimiter if this is not the first node. + if (previousSibling) { + write(delimiter); + + // Write either a line terminator or whitespace to separate the elements. + if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) { + writeLine(); + } + else if (previousSibling) { + write(" "); + } + } + + // For cleaner emit, decrease the indent level for function expressions in an argument list + let dedented = canBeDedented && shouldDedentChild(child); + if (dedented) { + decreaseIndent(); + } + + // Emit this child. + emit(child); + + if (dedented) { + increaseIndent(); + } + + previousSibling = child; + } + + // Write a trailing comma, if requested. + let hasTrailingComma = (format & ListFormat.AllowTrailingComma) && children.hasTrailingComma; + if (format & ListFormat.CommaDelimited && hasTrailingComma) { + write(","); + } + + if (format & ListFormat.LexicalEnvironment) { + endLexicalEnvironment(emitLexicalEnvironment); + } + + // Decrease the indent, if requested. + if (indented) { + decreaseIndent(); + } + + // Write the closing line terminator or closing whitespace. + if (shouldWriteClosingLineTerminator(containingRange, lastChild, format)) { + writeLine(); + } + else if (format & ListFormat.TrailingWhitespace && (previousSibling || hasTrailingComma)) { + write(" "); + } + + // End the scope, if requested. + if (format & ListFormat.Scoped) { + scopeEmitEnd(); + } + + // Write the trailing bracket. + write(getBracket(format, ListFormat.TrailingBracketOffset)); } - return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); - } - - function tryGetConstEnumValue(node: Node): number { - if (compilerOptions.isolatedModules) { - return undefined; + function canHaveDedentedChildren(node: Node) { + return isCallExpression(node) || isNewExpression(node) || isVariableDeclarationList(node); } - return isPropertyAccessExpression(node) || isElementAccessExpression(node) - ? resolver.getConstantValue(node) - : undefined; + function shouldDedentChild(node: Node): boolean { + return isFunctionExpression(node) || isArrowFunction(node) || isObjectLiteralExpression(node) || isArrayLiteralExpression(node) || + (isVariableDeclaration(node) && shouldDedentChild(node.initializer)); + } + + function writeIfAny(nodes: NodeArray, text: string) { + if (nodes && nodes.length > 0) { + write(text); + } + } + + function writeIfPresent(node: Node, text: string) { + if (node !== undefined) { + write(text); + } + } + + function writeIfMissing(node: Node, text: string) { + if (node === undefined) { + write(text); + } + } + + function writeToken(token: SyntaxKind) { + write(tokenToString(token)); + } + + function writeTokenNode(node: Node) { + if (node) { + writeToken(node.kind); + } + } + + function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) { + if (value) { + increaseIndent(); + writeLine(); + } + else if (valueToWriteWhenNotIndenting) { + write(valueToWriteWhenNotIndenting); + } + } + + // Helper function to decrease the indent if we previously indented. Allows multiple + // previous indent values to be considered at a time. This also allows caller to just + // call this once, passing in all their appropriate indent values, instead of needing + // to call this helper function multiple times. + function decreaseIndentIf(value1: boolean, value2?: boolean) { + if (value1) { + decreaseIndent(); + } + if (value2) { + decreaseIndent(); + } + } + + function shouldWriteLeadingLineTerminator(containingRange: TextRange, firstChild: Node, format?: ListFormat) { + if (containingRange === undefined) { + return false; + } + else if (firstChild === undefined) { + return !positionsAreOnSameLine(getStartPos(containingRange), getEndPos(containingRange)); + } + else if (positionIsSynthesized(containingRange.pos) || nodeIsSynthesized(firstChild)) { + return synthesizedNodeStartsOnNewLine(firstChild, format); + } + else { + return !rangeStartPositionsAreOnSameLine(containingRange, firstChild); + } + } + + function shouldWriteSeparatingLineTerminator(previousNode: Node, nextNode: Node, format?: ListFormat) { + if (previousNode === undefined || nextNode === undefined) { + return false; + } + else if (nodeIsSynthesized(previousNode) || nodeIsSynthesized(nextNode)) { + return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format); + } + else { + return !rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode); + } + } + + function shouldWriteClosingLineTerminator(containingRange: TextRange, lastChild: Node, format?: ListFormat) { + if (lastChild === undefined) { + return false; + } + else if (positionIsSynthesized(containingRange.pos) || nodeIsSynthesized(lastChild)) { + return synthesizedNodeStartsOnNewLine(lastChild, format); + } + else { + return !rangeEndPositionsAreOnSameLine(containingRange, lastChild); + } + } + + function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) { + if (nodeIsSynthesized(node)) { + let startsOnNewLine = (node).startsOnNewLine; + if (startsOnNewLine === undefined) { + return (format & ListFormat.PreferNewLine) !== 0; + } + + return startsOnNewLine; + } + return (format & ListFormat.PreferNewLine) !== 0; + } + + function rangeStartPositionsAreOnSameLine(range1: TextRange, range2: TextRange) { + return positionsAreOnSameLine(getStartPos(range1), getStartPos(range2)); + } + + function rangeEndPositionsAreOnSameLine(range1: TextRange, range2: TextRange) { + return positionsAreOnSameLine(getEndPos(range1), getEndPos(range2)); + } + + function rangeEndIsOnSameLineAsRangeStart(range1: TextRange, range2: TextRange) { + return positionsAreOnSameLine(getEndPos(range1), getStartPos(range2)); + } + + function positionsAreOnSameLine(pos1: number, pos2: number) { + return pos1 === pos2 || + getLineOfLocalPosition(currentSourceFile, pos1) === getLineOfLocalPosition(currentSourceFile, pos2); + } + + function getStartPos(range: TextRange) { + return skipTrivia(currentSourceFile.text, range.pos); + } + + function getEndPos(range: TextRange) { + return range.end; + } + + function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + // Always use a newline for synthesized code if the synthesizer desires it. + if (synthesizedNodeStartsOnNewLine(node2)) { + return true; + } + + return !nodeIsSynthesized(parent) + && !nodeIsSynthesized(node1) + && !nodeIsSynthesized(node2) + && !rangeEndIsOnSameLineAsRangeStart(node1, node2); + } + + function getTextOfNode(node: Node, includeTrivia?: boolean) { + if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) { + return node.text; + } + + return getSourceTextOfNodeFromSourceFile(currentSourceFile, node, includeTrivia); + } + + function tryGetConstEnumValue(node: Node): number { + if (compilerOptions.isolatedModules) { + return undefined; + } + + return isPropertyAccessExpression(node) || isElementAccessExpression(node) + ? resolver.getConstantValue(node) + : undefined; + } + + function addHelpers(node: Node, format: ListFormat) { + let flags = transformer.getGeneratedNodeFlags(node); + if (flags & GeneratedNodeFlags.EmitHelpers) { + format |= ListFormat.PrintEmitHelpers; + } + if (flags & GeneratedNodeFlags.EmitExportStar) { + format |= ListFormat.PrintExportStar; + } + if (flags & GeneratedNodeFlags.NoLexicalEnvironment) { + format &= ~ListFormat.LexicalEnvironment; + } + return format; + } } } @@ -1945,6 +2207,9 @@ namespace ts { Arrow = 1 << 13, // The list is a parameter list for an arrow function. Fragment = 1 << 14, // The list is a fragment of the node. Line terminators should be based on the node array positions. PreferNewLine = 1 << 15, // Prefer adding LineTerminators between synthesized nodes. + PrintEmitHelpers = 1 << 16, // Emit any emit helpers along with the list + PrintExportStar = 1 << 17, // Emit the __export helper for "export *" + LexicalEnvironment = 1 << 18, // Marks a lexical environment // List formats Decorators = Indented | TrailingWhitespace | Fragment, @@ -1970,13 +2235,17 @@ namespace ts { HeritageClauses = Indented | LeadingWhitespace | Fragment, ClassMembers = CurlyBrackets | Indented | Whitespace | Scoped | PreferNewLine, EnumMembers = CurlyBrackets | CommaDelimited | AllowTrailingComma | Indented | Scoped | Whitespace | PreferNewLine, - ModuleBlockStatements = CurlyBrackets | Indented | Whitespace | Scoped | PreferNewLine, + ModuleBlockStatements = CurlyBrackets | Indented | Whitespace | Scoped | PreferNewLine | LexicalEnvironment, CaseBlockClauses = CurlyBrackets | Indented | Whitespace | Scoped | PreferNewLine, ImportOrExportSpecifiers = CurlyBrackets | CommaDelimited | AllowTrailingComma | Indented | Whitespace, CaseOrDefaultClauseStatements = Indented | Whitespace | PreferNewLine, JsxElementChildren = PreferNewLine, JsxAttributes = Fragment, HeritageClauseTypes = CommaDelimited | Indented, - SourceFileStatements = PreferNewLine, + + FunctionBodyStatements = BlockStatements | LexicalEnvironment, + SourceFileStatements = PreferNewLine | LexicalEnvironment, + + MustEmitPrologueDirectivesFirst = PrintEmitHelpers | PrintExportStar } } \ No newline at end of file diff --git a/src/compiler/transform.ts b/src/compiler/transform.ts index f87d2add422..0e2f37d61f4 100644 --- a/src/compiler/transform.ts +++ b/src/compiler/transform.ts @@ -1,6 +1,9 @@ /// -/// /// +/// +/// +/// +/// /// const FORCE_TRANSFORMS = false; @@ -8,6 +11,16 @@ const FORCE_TRANSFORMS = false; namespace ts { export let transformTime = 0; + const moduleTransformationPhaseMap: Map = { + [ModuleKind.ES2015]: createES6ModuleTransformation, + [ModuleKind.ES6]: createES6ModuleTransformation, + [ModuleKind.System]: createSystemModuleTransformation, + [ModuleKind.AMD]: createModuleTransformation, + [ModuleKind.CommonJS]: createModuleTransformation, + [ModuleKind.UMD]: createModuleTransformation, + [ModuleKind.None]: createModuleTransformation, + }; + /** * Represents a phase in a transformation chain. Used to initialize the transformation and * return the transformation for the phase. @@ -21,23 +34,20 @@ namespace ts { */ export type TransformationChain = (transformer: Transformer) => Transformation; - /** - * Represents a transformation. - */ - export type Transformation = (node: SourceFile) => SourceFile; - /** * Gets the default transformation chain for the provided set of compiler options. */ - export function getTransformationChain(options: CompilerOptions): TransformationChain { - let jsx = options.jsx; - let languageVersion = options.target || ScriptTarget.ES3; - let moduleKind = options.module || ModuleKind.None; + export function getTransformationChain(compilerOptions: CompilerOptions): TransformationChain { + let jsx = compilerOptions.jsx; + let languageVersion = getLanguageVersion(compilerOptions); + let moduleKind = getModuleKind(compilerOptions); let phases: TransformationPhase[] = []; // Add the TypeScript and Module phases to the chain. phases.push(createTypeScriptTransformation); - // // transforms.push(transformModule); + + // Add the module transformation to the chain. + phases.push(moduleTransformationPhaseMap[moduleKind]); // Add the JSX transform to the chain. if (jsx === JsxEmit.React) { @@ -77,57 +87,6 @@ namespace ts { LexicalEnvironmentEnded = 1 << 5, } - // emit output for the __extends helper function - const extendsHelper = ` -var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -};`; - - // emit output for the __decorate helper function - const decorateHelper = ` -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); - switch (arguments.length) { - case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); - case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); - case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); - } -};`; - - // emit output for the __metadata helper function - const metadataHelper = ` -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -};`; - - // emit output for the __param helper function - const paramHelper = ` -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -};`; - - const awaiterHelper = ` -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) { - return new Promise(function (resolve, reject) { - generator = generator.apply(thisArg, _arguments); - function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); } - function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } } - function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } } - function step(verb, value) { - var result = generator[verb](value); - result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject); - } - step("next", void 0); - }); -};`; - - const exportStarHelper = ` -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -}`; - let compilerOptions = host.getCompilerOptions(); let languageVersion = compilerOptions.target || ScriptTarget.ES3; let transformFlags: TransformFlags; @@ -150,9 +109,8 @@ function __export(m) { let updatedNode: Node; let updatedNodes: Node[]; let helpersEmitted: NodeCheckFlags; - let assignmentSubstitution: (node: BinaryExpression) => Expression; - let bindingIdentifierSubstitution: (node: Identifier) => Identifier; - let expressionIdentifierSubstitution: (node: Identifier) => LeftHandSideExpression; + let expressionSubstitution: (node: Expression) => Expression; + let generatedNodeFlags: GeneratedNodeFlags[] = []; let transformer: Transformer = { getEmitResolver: () => resolver, getCompilerOptions: () => compilerOptions, @@ -163,6 +121,7 @@ function __export(m) { tryPushNode: node => nodeStack.tryPushNode(node), pushNode: node => nodeStack.pushNode(node), popNode: () => nodeStack.popNode(), + setNode: node => nodeStack.setNode(node), findAncestorNode: (match: (node: Node) => boolean) => nodeStack.findAncestorNode(match), getDeclarationName, getClassMemberPrefix, @@ -174,16 +133,13 @@ function __export(m) { declareLocal, hoistVariableDeclaration, hoistFunctionDeclaration, - emitEmitHelpers, - emitExportStarHelper, - getAssignmentSubstitution, - setAssignmentSubstitution, - getBindingIdentifierSubstitution, - setBindingIdentifierSubstitution, - getExpressionIdentifierSubstitution, - setExpressionIdentifierSubstitution, + getGeneratedNodeFlags, + setGeneratedNodeFlags, + getExpressionSubstitution, + setExpressionSubstitution, startLexicalEnvironment, endLexicalEnvironment, + createNodes, pipeNode, pipeNodes, mapNode, @@ -197,7 +153,12 @@ function __export(m) { visitFunctionBody, visitConciseBody, accept(node: Node, visitor: (node: Node, write: (node: Node) => void) => void) { - return acceptTransformer(transformer, node, visitor); + let wasPushed = node && nodeStack.tryPushNode(node); + node = acceptTransformer(transformer, node, visitor); + if (wasPushed) { + nodeStack.popNode(); + } + return node; } }; @@ -206,12 +167,30 @@ function __export(m) { return { sourceFiles: map(sourceFiles, transformSourceFile), substitutions: { - assignmentSubstitution, - bindingIdentifierSubstitution, - expressionIdentifierSubstitution, + getGeneratedNodeFlags, + expressionSubstitution, } }; + function getGeneratedNodeFlags(node: Node) { + let lastNode: Node; + while (node) { + let nodeId = getNodeId(node); + if (nodeId in generatedNodeFlags) { + return generatedNodeFlags[nodeId]; + } + + lastNode = node; + node = node.original; + } + + return undefined; + } + + function setGeneratedNodeFlags(node: Node, flags: GeneratedNodeFlags) { + generatedNodeFlags[getNodeId(node)] = flags; + } + function transformSourceFile(sourceFile: SourceFile) { if (isDeclarationFile(sourceFile)) { return sourceFile; @@ -240,28 +219,12 @@ function __export(m) { return visited; } - function getAssignmentSubstitution(): (node: BinaryExpression) => Expression { - return assignmentSubstitution; + function getExpressionSubstitution(): (node: Expression) => Expression { + return expressionSubstitution; } - function setAssignmentSubstitution(substitution: (node: BinaryExpression) => Expression) { - assignmentSubstitution = substitution; - } - - function getBindingIdentifierSubstitution(): (node: Identifier) => Identifier { - return bindingIdentifierSubstitution; - } - - function setBindingIdentifierSubstitution(substitution: (node: Identifier) => Identifier): void { - bindingIdentifierSubstitution = substitution; - } - - function getExpressionIdentifierSubstitution(): (node: Identifier) => LeftHandSideExpression { - return expressionIdentifierSubstitution; - } - - function setExpressionIdentifierSubstitution(substitution: (node: Identifier) => LeftHandSideExpression) { - expressionIdentifierSubstitution = substitution; + function setExpressionSubstitution(substitution: (node: Expression) => Expression) { + expressionSubstitution = substitution; } // Return the next available name in the pattern _a ... _z, _0, _1, ... @@ -467,43 +430,6 @@ function __export(m) { hoistedFunctionDeclarations.push(func); } - function emitEmitHelpers(write: (node: Statement) => void) { - if (compilerOptions.noEmitHelpers) { - return; - } - - if (languageVersion < ScriptTarget.ES6 && shouldEmitHelper(NodeCheckFlags.EmitExtends)) { - write(createRawStatement(extendsHelper)); - helpersEmitted |= NodeCheckFlags.EmitExtends; - } - - if (shouldEmitHelper(NodeCheckFlags.EmitDecorate)) { - write(createRawStatement(decorateHelper)); - helpersEmitted |= NodeCheckFlags.EmitDecorate; - } - - if (shouldEmitHelper(NodeCheckFlags.EmitParam)) { - write(createRawStatement(paramHelper)); - helpersEmitted |= NodeCheckFlags.EmitParam; - } - - if (shouldEmitHelper(NodeCheckFlags.EmitAwaiter)) { - write(createRawStatement(awaiterHelper)); - helpersEmitted |= NodeCheckFlags.EmitAwaiter; - } - } - - function emitExportStarHelper(write: (node: Statement) => void) { - if (shouldEmitHelper(NodeCheckFlags.EmitExportStar)) { - write(createRawStatement(exportStarHelper)); - } - } - - function shouldEmitHelper(flags: NodeCheckFlags) { - return !(helpersEmitted & flags) - && !!(resolver.getNodeCheckFlags(currentSourceFile) & flags); - } - function aggregateTransformFlags(node: Node) { if (!node) { return; @@ -656,7 +582,6 @@ function __export(m) { } function readNode(): Node { - Debug.assert(!!updatedNode, "Not enough nodes written to output."); return updatedNode; } @@ -672,6 +597,53 @@ function __export(m) { } } + function createNodes(callback: (write: (node: TOut) => void) => void, out?: ((node: TOut) => void) | TOut[]): NodeArray { + let write: (node: Node) => void; + let readNodes: () => NodeArray; + let outputArray: TOut[]; + if (typeof out === "function") { + write = out as (node: Node) => void; + } + else { + write = writeNodeToNodeArray; + readNodes = readNodeArray; + outputArray = out as TOut[] || []; + } + + // Preserve the previous environment on the call stack. + let savedOriginalNodes = originalNodes; + let savedWriteOffset = writeOffset; + let savedUpdatedNode = updatedNode; + let savedUpdatedNodes = updatedNodes; + let savedNodeTest = nodeTest; + let savedCurrentVisitFlags = currentVisitFlags; + + // Set the new environment. + originalNodes = undefined; + writeOffset = undefined; + updatedNode = undefined; + updatedNodes = outputArray; + nodeTest = undefined; + currentVisitFlags = undefined; + requestedVisitFlags = undefined; + + // Execute the callback + callback(write); + + // Read the result + let resultNodes = readNodes ? readNodes() : undefined; + + // Restore the previous environment. + originalNodes = savedOriginalNodes; + writeOffset = savedWriteOffset; + updatedNode = savedUpdatedNode; + updatedNodes = savedUpdatedNodes; + nodeTest = savedNodeTest; + currentVisitFlags = savedCurrentVisitFlags; + + return resultNodes as NodeArray; + } + function visitNodeOrNodes(node: Node, nodes: Node[], start: number, count: number, visitor: (node: Node, write: (node: Node) => void) => void, write: (node: Node) => void, readNode?: () => Node, readNodes?: () => NodeArray, out?: Node[], test?: (node: Node) => boolean): Node | NodeArray { let resultNode: Node; diff --git a/src/compiler/transforms/destructuring.ts b/src/compiler/transforms/destructuring.ts new file mode 100644 index 00000000000..fde6a0fb854 --- /dev/null +++ b/src/compiler/transforms/destructuring.ts @@ -0,0 +1,278 @@ +/// +/*@internal*/ +namespace ts { + /** + * Flattens binding patterns in a variable declaration. + * @param transformer The transformer context to use during destructuring. + * @param node The variable declaration to flatten. + * @param write The callback to execute to write variable declarations. + * @param visitor An optional visitor used to visit the initializers of a binding pattern. + */ + export function flattenVariableDestructuring(transformer: Transformer, node: VariableDeclaration, write: (node: VariableDeclaration) => void, visitor?: (node: Node, write: (node: Node) => void) => void): void { + transformDestructuring(transformer, node, /*writeExpression*/ undefined, write, visitor, node.initializer); + } + + /** + * Flattens binding patterns in a parameter declaration. + * @param transformer The transformer context to use during destructuring. + * @param node The parameter declaration to flatten. + * @param write The callback to execute to write variable declarations. + * @param visitor An optional visitor used to visit the initializers of a binding pattern. + */ + export function flattenParameterDestructuring(transformer: Transformer, node: ParameterDeclaration, write: (node: VariableDeclaration) => void, visitor?: (node: Node, write: (node: Node) => void) => void): void { + transformDestructuring(transformer, node, /*writeExpression*/ undefined, write, visitor, transformer.getGeneratedNameForNode(node)); + } + + /** + * Flattens a destructuring assignment. + * @param transformer The transformer context to use during destructuring. + * @param node The assignment expression to flatten. + * @param write The callback to execute to write the resulting expression. + * @param visitor An optional visitor used to visit the initializers and right-hand side of the destructuring assignment. + */ + export function flattenDestructuringAssignment(transformer: Transformer, node: BinaryExpression, write: (node: Expression) => void, visitor?: (node: Node, write: (node: Node) => void) => void): void { + transformDestructuring(transformer, node, write, /*writeDeclaration*/ undefined, visitor); + } + + /** + * Flattens binding patterns in a variable declaration and transforms them into an expression. + * @param transformer The transformer context to use during destructuring. + * @param node The variable declaration to flatten. + * @param write The callback to execute to write the resulting expression. + * @param visitor An optional visitor used to visit the initializers of a binding pattern. + */ + export function flattenVariableDestructuringAsExpression(transformer: Transformer, node: VariableDeclaration, write: (node: Expression) => void, visitor?: (node: Node, write: (node: Node) => void) => void): void { + transformDestructuring(transformer, node, write, /*writeDeclaration*/ undefined, visitor); + } + + function transformDestructuring( + transformer: Transformer, + root: BinaryExpression | VariableDeclaration | ParameterDeclaration, + writeExpression: (node: Expression) => void, + writeDeclaration: (node: VariableDeclaration) => void, + visitor?: (node: Node, write: (node: Node) => void) => void, + value?: Expression) { + let { + createTempVariable, + hoistVariableDeclaration, + mapNode, + flattenNode, + visitNode, + } = transformer; + + let parentNode = transformer.getParentNode(); + let isVariableDeclarationList = writeDeclaration !== undefined; + + if (isBinaryExpression(root)) { + return emitAssignmentExpression(root, writeExpression); + } + else { + return emitBindingElement(root, value, writeExpression || writeDeclaration); + } + + function emitAssignmentExpression(root: BinaryExpression, write: (node: Expression) => void) { + if (isEmptyObjectLiteralOrArrayLiteral(root.left)) { + return write(root.right); + } + else { + let expressions = flattenNode(root, emitDestructuringExpressions); + let expression = inlineExpressions(expressions); + if (!isExpressionStatement(parentNode) && !isParenthesizedExpression(parentNode)) { + expression = createParenthesizedExpression(expression); + } + + return write(expression); + } + } + + function emitBindingElement(target: BindingElement, value: Expression, write: (node: Expression | VariableDeclaration) => void): void { + if (target.initializer) { + // Combine value and initializer + let initializer = visitNode(target.initializer, visitor, isExpressionNode); + value = value ? createDefaultValueCheck(value, initializer, write) : initializer; + } + else if (!value) { + // Use 'void 0' in absence of value and initializer + value = createVoidZeroExpression(); + } + + let name = target.name; + if (isBindingPattern(name)) { + const elements = name.elements; + const numElements = elements.length; + if (numElements !== 1) { + // For anything other than a single-element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. Additionally, if we have zero elements + // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, + // so in that case, we'll intentionally create that temporary. + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, write); + } + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + if (name.kind === SyntaxKind.ObjectBindingPattern) { + // Rewrite element to a declaration with an initializer that fetches property + let propName = element.propertyName || element.name; + emitBindingElement(element, createPropertyOrElementAccessExpression(value, propName), write); + } + else if (element.kind !== SyntaxKind.OmittedExpression) { + if (!element.dotDotDotToken) { + // Rewrite element to a declaration that accesses array element at index i + emitBindingElement(element, createElementAccessExpression3(value, i), write); + } + else if (i === elements.length - 1) { + emitBindingElement(element, createSliceCall(value, i), write); + } + } + } + } + else { + emitAssignment(name, value, /*isTempVariable*/ false, write); + } + } + + function emitDestructuringExpressions(node: BinaryExpression, write: (node: Expression) => void): void { + let target = node.left; + let value = node.right; + if (isExpressionStatement(parentNode)) { + emitDestructuringAssignment(target, value, write); + } + else { + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, write); + emitDestructuringAssignment(target, value, write); + write(value); + } + } + + function emitDestructuringAssignment(target: Expression, value: Expression, write: (node: Expression) => void) { + if (isBinaryExpression(target) && target.operatorToken.kind === SyntaxKind.EqualsToken) { + let right = visitNode((target).right, visitor, isExpressionNode); + value = createDefaultValueCheck(value, right, write); + target = (target).left; + } + if (isObjectLiteralExpression(target)) { + emitObjectLiteralAssignment(target, value, write); + } + else if (isArrayLiteralExpression(target)) { + emitArrayLiteralAssignment(target, value, write); + } + else if (isIdentifier(target)) { + emitAssignment(target, value, /*isTempVariable*/ false, write); + } + else { + Debug.fail(); + } + } + + function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, write: (node: Expression) => void): void { + let properties = target.properties; + if (properties.length !== 1) { + // For anything but a single element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, write); + } + + for (let p of properties) { + if (isPropertyAssignment(p) || isShorthandPropertyAssignment(p)) { + let propName = (p).name; + let expr = createPropertyOrElementAccessExpression(value, propName); + emitDestructuringAssignment((p).initializer || propName, expr, write); + } + } + } + + function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, write: (node: Expression) => void): void { + let elements = target.elements; + if (elements.length !== 1) { + // For anything but a single element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, write); + } + + for (let i = 0; i < elements.length; i++) { + let e = elements[i]; + if (e.kind !== SyntaxKind.OmittedExpression) { + if (e.kind !== SyntaxKind.SpreadElementExpression) { + emitDestructuringAssignment(e, createElementAccessExpression3(value, i), write); + } + else if (i === elements.length - 1) { + emitDestructuringAssignment((e).expression, createSliceCall(value, i), write); + } + } + } + } + + function emitAssignment(left: Identifier, right: Expression, isTempVariable: boolean, write: (node: Expression | VariableDeclaration) => void): void { + if (isVariableDeclarationList) { + write(createVariableDeclaration2(left, right)); + } + else { + write(createAssignmentExpression(left, right)); + } + } + + function ensureIdentifier(value: Expression, reuseIdentifierExpressions: boolean, write: (node: Expression | VariableDeclaration) => void) { + if (isIdentifier(value) && reuseIdentifierExpressions) { + return value; + } + else { + let temp = createTempVariable(TempFlags.Auto); + if (!isVariableDeclarationList) { + hoistVariableDeclaration(temp); + } + + emitAssignment(temp, value, /*isTempVariable*/ true, write); + return temp; + } + } + + function createDefaultValueCheck(value: Expression, defaultValue: Expression, write: (node: Expression | VariableDeclaration) => void): Expression { + value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, write); + let equalityExpr = createStrictEqualityExpression(value, createVoidZeroExpression()); + return createConditionalExpression2(equalityExpr, defaultValue, value); + } + } + + export function convertBindingPatternToExpression(node: BindingPattern): Expression { + switch (node.kind) { + case SyntaxKind.ObjectBindingPattern: + return convertObjectBindingPatternToExpression(node); + + case SyntaxKind.ArrayBindingPattern: + return convertArrayBindingPatternToExpression(node); + } + } + + function convertBindingElementToExpression(node: BindingElement): Expression { + let name = node.name; + let expression = isIdentifier(name) ? name : convertBindingPatternToExpression(name); + if (node.initializer) { + expression = createAssignmentExpression(expression, node.initializer, node); + } + else if (node.dotDotDotToken) { + expression = createSpreadElementExpression(expression, node); + } + + return expression; + } + + function convertObjectBindingPatternToExpression(node: ObjectBindingPattern): Expression { + let properties = map(node.elements, convertBindingElementToObjectLiteralElement); + return createObjectLiteralExpression(properties); + } + + function convertArrayBindingPatternToExpression(node: ArrayBindingPattern): Expression { + let elements = map(node.elements, convertBindingElementToExpression); + return createArrayLiteralExpression(elements); + } + + function convertBindingElementToObjectLiteralElement(node: BindingElement): ObjectLiteralElement { + let name = node.name; + if (!node.propertyName && isIdentifier(name) && !node.initializer) { + return createShorthandPropertyAssignment(name, node); + } + + let propertyName = node.propertyName || name; + let expr = convertBindingElementToExpression(node); + return createPropertyAssignment(propertyName, expr); + } +} \ No newline at end of file diff --git a/src/compiler/transforms/es6.ts b/src/compiler/transforms/es6.ts index ac904a575db..5989e49ce6e 100644 --- a/src/compiler/transforms/es6.ts +++ b/src/compiler/transforms/es6.ts @@ -1,4 +1,5 @@ /// +/// /*@internal*/ namespace ts { export function createES6Transformation(transformer: Transformer): Transformation { @@ -329,83 +330,7 @@ namespace ts { } function emitParameterBindingElements(parameter: ParameterDeclaration, write: (node: VariableDeclaration) => void): void { - let tempName = getGeneratedNameForNode(parameter); - emitBindingElement(parameter, tempName, write); - } - - function emitBindingElement(target: BindingElement, value: Expression, write: (node: VariableDeclaration) => void): void { - if (target.initializer) { - // Combine value and initializer - let initializer = visitNode(target.initializer, visitor, isExpressionNode); - value = value ? createDefaultValueCheck(value, initializer, /*isVariableDeclarationList*/ true, write) : initializer; - } - else if (!value) { - // Use 'void 0' in absence of value and initializer - value = createVoidZeroExpression(); - } - - let name = target.name; - if (isBindingPattern(name)) { - const elements = name.elements; - const numElements = elements.length; - if (numElements !== 1) { - // For anything other than a single-element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. Additionally, if we have zero elements - // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, - // so in that case, we'll intentionally create that temporary. - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, /*isVariableDeclarationList*/ true, write); - } - for (let i = 0; i < elements.length; i++) { - let element = elements[i]; - if (name.kind === SyntaxKind.ObjectBindingPattern) { - // Rewrite element to a declaration with an initializer that fetches property - let propName = element.propertyName || element.name; - emitBindingElement(element, createPropertyOrElementAccessExpression(value, propName), write); - } - else if (element.kind !== SyntaxKind.OmittedExpression) { - if (!element.dotDotDotToken) { - // Rewrite element to a declaration that accesses array element at index i - emitBindingElement(element, createElementAccessExpression3(value, i), write); - } - else if (i === elements.length - 1) { - emitBindingElement(element, createSliceCall(value, i), write); - } - } - } - } - else { - emitAssignment(name, value, /*isTempVariable*/ false, /*isVariableDeclarationList*/ true, write); - } - } - - function emitAssignment(left: Identifier, right: Expression, isTempVariable: boolean, isVariableDeclarationList: boolean, write: (node: Expression | VariableDeclaration) => void): void { - if (isVariableDeclarationList) { - write(createVariableDeclaration2(left, right)); - } - else { - write(createAssignmentExpression(left, right)); - } - } - - function ensureIdentifier(value: Expression, reuseIdentifierExpressions: boolean, isVariableDeclarationList: boolean, write: (node: Expression | VariableDeclaration) => void) { - if (isIdentifier(value) && reuseIdentifierExpressions) { - return value; - } - else { - let temp = createTempVariable(TempFlags.Auto); - if (!isVariableDeclarationList) { - hoistVariableDeclaration(temp); - } - - emitAssignment(temp, value, /*isTempVariable*/ true, isVariableDeclarationList, write); - return temp; - } - } - - function createDefaultValueCheck(value: Expression, defaultValue: Expression, isVariableDeclarationList: boolean, write: (node: Expression | VariableDeclaration) => void): Expression { - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, isVariableDeclarationList, write); - let equalityExpr = createStrictEqualityExpression(value, createVoidZeroExpression()); - return createConditionalExpression2(equalityExpr, defaultValue, value); + flattenParameterDestructuring(transformer, parameter, write, visitor); } function emitDefaultValueAssignmentForInitializer(parameter: ParameterDeclaration, name: Identifier, initializer: Expression, write: (node: Statement) => void): void { @@ -421,7 +346,7 @@ namespace ts { } function shouldEmitRestParameter(node: ParameterDeclaration) { - return node.dotDotDotToken && !(node.flags & NodeFlags.GeneratedRest); + return node.dotDotDotToken && !(node.flags & NodeFlags.Generated); } function emitRestParameter(node: FunctionLikeDeclaration, write: (node: Statement) => void): void { @@ -560,89 +485,7 @@ namespace ts { function visitBinaryExpression(node: BinaryExpression, write: (node: Expression) => void): void { // If we are here it is because this is a destructuring assignment. - if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { - write(node.right); - } - else { - let expressions = flattenNode(node, emitDestructuringExpressions); - let expression = inlineExpressions(expressions); - let parentNode = getParentNode(); - if (!isExpressionStatement(parentNode) && !isParenthesizedExpression(parentNode)) { - expression = createParenthesizedExpression(expression); - } - write(expression); - } - } - - function emitDestructuringExpressions(node: BinaryExpression, write: (node: Expression) => void): void { - let target = node.left; - let value = node.right; - let parentNode = getParentNode(); - if (isExpressionStatement(parentNode)) { - emitDestructuringAssignment(target, value, write); - } - else { - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, /*isVariableDeclarationList*/ false, write); - emitDestructuringAssignment(target, value, write); - write(value); - } - } - - function emitDestructuringAssignment(target: Expression, value: Expression, write: (node: Expression) => void) { - if (isBinaryExpression(target) && target.operatorToken.kind === SyntaxKind.EqualsToken) { - value = createDefaultValueCheck(value, (target).right, /*isVariableDeclarationList*/ false, write); - target = (target).left; - } - if (isObjectLiteralExpression(target)) { - emitObjectLiteralAssignment(target, value, write); - } - else if (isArrayLiteralExpression(target)) { - emitArrayLiteralAssignment(target, value, write); - } - else if (isIdentifier(target)) { - emitAssignment(target, value, /*isTempVariable*/ false, /*isVariableDeclarationList*/ false, write); - } - else { - Debug.fail(); - } - } - - function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, write: (node: Expression) => void): void { - let properties = target.properties; - if (properties.length !== 1) { - // For anything but a single element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, /*isVariableDeclarationList*/ false, write); - } - - for (let p of properties) { - if (isPropertyAssignment(p) || isShorthandPropertyAssignment(p)) { - let propName = (p).name; - let expr = createPropertyOrElementAccessExpression(value, propName); - emitDestructuringAssignment((p).initializer || propName, expr, write); - } - } - } - - function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, write: (node: Expression) => void): void { - let elements = target.elements; - if (elements.length !== 1) { - // For anything but a single element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. - value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, /*isVariableDeclarationList*/ false, write); - } - - for (let i = 0; i < elements.length; i++) { - let e = elements[i]; - if (e.kind !== SyntaxKind.OmittedExpression) { - if (e.kind !== SyntaxKind.SpreadElementExpression) { - emitDestructuringAssignment(e, createElementAccessExpression3(value, i), write); - } - else if (i === elements.length - 1) { - emitDestructuringAssignment((e).expression, createSliceCall(value, i), write); - } - } - } + flattenDestructuringAssignment(transformer, node, write, visitor); } function visitVariableStatement(node: VariableStatement, write: (node: Statement) => void): void { @@ -658,7 +501,7 @@ namespace ts { function visitVariableDeclaration(node: VariableDeclaration, write: (node: VariableDeclaration) => void): void { if (isBindingPattern(node.name)) { - emitBindingElement(node, node.initializer, write); + flattenVariableDestructuring(transformer, node, write, visitor); } else { // TODO(rbuckton): Is there any reason we should hit this branch? @@ -1138,7 +981,7 @@ namespace ts { } function visitExpressionStatement(node: ExpressionStatement, write: (node: Statement) => void): void { - if (node.flags & NodeFlags.GeneratedSuper) { + if (node.flags & NodeFlags.Generated) { write(createDefaultSuperCall()); } else { diff --git a/src/compiler/transforms/module/es6.ts b/src/compiler/transforms/module/es6.ts new file mode 100644 index 00000000000..563b9ee98b6 --- /dev/null +++ b/src/compiler/transforms/module/es6.ts @@ -0,0 +1,11 @@ +/// +/*@internal*/ +namespace ts { + export function createES6ModuleTransformation(transformer: Transformer): Transformation { + return transformES6Module; + + function transformES6Module(node: SourceFile) { + return node; + } + } +} \ No newline at end of file diff --git a/src/compiler/transforms/module/module.ts b/src/compiler/transforms/module/module.ts new file mode 100644 index 00000000000..d9e9b502c02 --- /dev/null +++ b/src/compiler/transforms/module/module.ts @@ -0,0 +1,706 @@ +/// +/*@internal*/ +namespace ts { + export function createModuleTransformation(transformer: Transformer): Transformation { + const emitModuleDelegates: Map<(node: SourceFile, write: (node: Statement) => void) => void> = { + [ModuleKind.None]: emitCommonJSModule, + [ModuleKind.AMD]: emitAMDModule, + [ModuleKind.UMD]: emitUMDModule, + [ModuleKind.CommonJS]: emitCommonJSModule, + }; + + let { + makeUniqueName, + getGeneratedNameForNode, + hoistVariableDeclaration, + hoistFunctionDeclaration, + startLexicalEnvironment, + endLexicalEnvironment, + pipeNode, + pipeNodes, + mapNode, + mapNodes, + flattenNode, + visitNode, + visitNodes, + accept + } = transformer; + + let compilerOptions = transformer.getCompilerOptions(); + let resolver = transformer.getEmitResolver(); + let languageVersion = getLanguageVersion(compilerOptions); + let moduleKind = getModuleKind(compilerOptions); + let currentSourceFile: SourceFile; + let externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; + let exportSpecifiers: Map; + let exportEquals: ExportAssignment; + let hasExportStars: boolean; + let exportAssignmentWriter: (node: Statement) => void; + let savedExpressionSubstution = transformer.getExpressionSubstitution(); + transformer.setExpressionSubstitution(substituteExpressionWithFallback); + return transformModule; + + /** + * Transforms a source file via the provided module emit callback. + */ + function transformModule(node: SourceFile): SourceFile { + if (isExternalModule(node) || compilerOptions.isolatedModules) { + currentSourceFile = node; + + // collect information about the external module + ({ externalImports, exportSpecifiers, exportEquals, hasExportStars } = collectExternalModuleInfo(node, resolver)); + + let emitModule = emitModuleDelegates[moduleKind]; + node = updateSourceFileNode(node, flattenNode(node, emitModule), node.endOfFileToken); + + if (hasExportStars && emitModule === emitCommonJSModule) { + transformer.setGeneratedNodeFlags(node, GeneratedNodeFlags.EmitExportStar); + } + + currentSourceFile = undefined; + } + + return node; + } + + /** + * Emits file prologue directives prior to a module body. + */ + function emitPrologueDirectives(statements: NodeArray, write: (node: Statement) => void): number { + for (let i = 0; i < statements.length; ++i) { + if (isPrologueDirective(statements[i])) { + write(statements[i]); + } + else { + return i; + } + } + + return statements.length; + } + + /** + * Emits a CommonJS module. + */ + function emitCommonJSModule(node: SourceFile, write: (node: Statement) => void) { + startLexicalEnvironment(); + + let statementOffset = emitPrologueDirectives(node.statements, write); + + pipeNodes(node.statements, visitModuleElement, write, statementOffset); + endLexicalEnvironment(write); + + let exportEquals = tryCreateExportEquals(/*emitAsReturn*/ false); + if (exportEquals) { + write(exportEquals); + } + } + + /** + * Emits an AMD module. + */ + function emitAMDModule(node: SourceFile, write: (node: Statement) => void) { + let define = createIdentifier("define"); + let moduleName = node.moduleName ? createStringLiteral(node.moduleName) : undefined; + emitAsynchronousModule(node, write, define, moduleName, /*includeNonAmdDependencies*/ true); + } + + /** + * Emits an UMD module. + */ + function emitUMDModule(node: SourceFile, write: (node: Statement) => void) { + let define = createIdentifier("define"); + transformer.setGeneratedNodeFlags(define, GeneratedNodeFlags.UMDDefine); + emitAsynchronousModule(node, write, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false); + } + + /** + * Emits an asynchronous module (AMD/UMD). + * @param node The source file to transform. + * @param write The callback used to emit the module body. + * @param define The expression called to define the asynchronous module body. + * @param moduleName The expression + */ + function emitAsynchronousModule(node: SourceFile, write: (node: Statement) => void, define: Expression, moduleName: Expression, includeNonAmdDependencies: boolean) { + // Start the lexical environment for the source file. + startLexicalEnvironment(); + + let statementOffset = emitPrologueDirectives(node.statements, write); + + // An AMD define function has the following shape: + // define(id?, dependencies?, factory); + // + // This has the shape of + // define(name, ["module1", "module2"], function (module1Alias) { + // The location of the alias in the parameter list in the factory function needs to + // match the position of the module name in the dependency list. + // + // To ensure this is true in cases of modules with no aliases, e.g.: + // `import "module"` or `` + // we need to add modules without alias names to the end of the dependencies list + + let defineArguments: Expression[] = []; + if (moduleName) { + defineArguments.push(moduleName); + } + + let aliasedModuleNames: Expression[] = [ + createStringLiteral("require"), + createStringLiteral("exports") + ]; + + let unaliasedModuleNames: Expression[] = []; + + let importAliasNames: ParameterDeclaration[] = [ + createParameter3("require"), + createParameter3("exports") + ]; + + for (let amdDependency of node.amdDependencies) { + if (amdDependency.name) { + aliasedModuleNames.push(createStringLiteral(amdDependency.name)); + importAliasNames.push(createParameter3(amdDependency.name)); + } + else { + unaliasedModuleNames.push(createStringLiteral(amdDependency.path)); + } + } + + for (let importNode of externalImports) { + // Find the name of the external module + let externalModuleName = getExternalModuleNameLiteral(importNode); + // Find the name of the module alias, if there is one + let importAliasName = getLocalNameForExternalImport(importNode); + if (includeNonAmdDependencies && importAliasName) { + aliasedModuleNames.push(externalModuleName); + importAliasNames.push(createParameter2(importAliasName)); + } + else { + unaliasedModuleNames.push(externalModuleName); + } + } + + // Create the import names array. + let imports = createArrayLiteralExpression(concatenate(aliasedModuleNames, unaliasedModuleNames)); + defineArguments.push(imports); + + // Create the body of the module. + let statements: Statement[] = []; + + // Start the lexical environment for the module body. + startLexicalEnvironment(); + + // Pipe each statement of the source file through a visitor and out to the module body + pipeNodes(node.statements, visitModuleElement, statements, statementOffset); + + // End the lexical environment for the module body. + endLexicalEnvironment(statements); + + // Append the 'export =' statement if provided. + let exportEquals = tryCreateExportEquals(/*emitAsReturn*/ true); + if (exportEquals) { + statements.push(exportEquals); + } + + // Create the function for the module body. + let moduleBody = createBlock(statements); + + if (hasExportStars) { + transformer.setGeneratedNodeFlags(moduleBody, GeneratedNodeFlags.EmitExportStar); + } + + let moduleFunc = createFunctionExpression3(importAliasNames, moduleBody); + defineArguments.push(moduleFunc); + + // create the definition + let defineCall = createCallExpression2(define, defineArguments); + write(createExpressionStatement(defineCall)); + + // End the lexical environment for the source file. + endLexicalEnvironment(write); + } + + function visitModuleElement(node: Statement, write: (node: Statement) => void) { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + return visitImportDeclaration(node, write); + case SyntaxKind.ImportEqualsDeclaration: + return visitImportEqualsDeclaration(node, write); + case SyntaxKind.ExportDeclaration: + return visitExportDeclaration(node, write); + case SyntaxKind.ExportAssignment: + return visitExportAssignment(node, write); + case SyntaxKind.VariableStatement: + return visitVariableStatement(node, write); + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(node, write); + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node, write); + default: + return write(node); + } + } + + function visitImportDeclaration(node: ImportDeclaration, write: (node: Statement) => void): void { + if (contains(externalImports, node)) { + let namespaceDeclaration = getNamespaceDeclarationNode(node); + if (moduleKind !== ModuleKind.AMD) { + let require = createRequireCall(node); + if (!node.importClause) { + // import "mod"; + write(createExpressionStatement(require, /*location*/ node)); + } + else { + let variables: VariableDeclaration[] = []; + if (namespaceDeclaration && !isDefaultImport(node)) { + // import * as n from "mod"; + variables.push(createVariableDeclaration2(makeSynthesized(namespaceDeclaration.name), require)); + } + else { + // import d from "mod"; + // import { x, y } from "mod"; + // import d, { x, y } from "mod"; + // import d, * as n from "mod"; + variables.push(createVariableDeclaration2(getGeneratedNameForNode(node), require)); + if (namespaceDeclaration && isDefaultImport(node)) { + variables.push(createVariableDeclaration2(makeSynthesized(namespaceDeclaration.name), getGeneratedNameForNode(node))); + } + } + + write(createVariableStatement2(createVariableDeclarationList(variables), /*location*/ node)); + } + } + else if (namespaceDeclaration && isDefaultImport(node)) { + // import d, * as n from "mod"; + write(createVariableStatement3(makeSynthesized(namespaceDeclaration.name), getGeneratedNameForNode(node), /*location*/ node)); + } + + emitExportImportAssignments(node, write); + } + } + + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration, write: (node: Statement) => void): void { + if (contains(externalImports, node)) { + if (moduleKind !== ModuleKind.AMD) { + let require = createRequireCall(node); + if (node.flags & NodeFlags.Export) { + write(createExpressionStatement(createExportAssignment(node.name, require), /*location*/ node)); + } + else { + write(createVariableStatement3(makeSynthesized(node.name), require, /*location*/ node)); + } + } + else { + if (node.flags & NodeFlags.Export) { + write(createExpressionStatement(createExportAssignment(node.name, node.name), /*location*/ node)); + } + } + + emitExportImportAssignments(node, write); + } + } + + function visitExportDeclaration(node: ExportDeclaration, write: (node: Statement) => void): void { + if (contains(externalImports, node)) { + let generatedName = getGeneratedNameForNode(node); + if (node.exportClause) { + // export { x, y } from "mod"; + if (moduleKind !== ModuleKind.AMD) { + write(createVariableStatement3(generatedName, createRequireCall(node))); + } + for (let specifier of node.exportClause.elements) { + if (resolver.isValueAliasDeclaration(specifier)) { + let exportedValue = createPropertyAccessExpression2(generatedName, specifier.propertyName || specifier.name); + write(createExpressionStatement(createExportAssignment(specifier.name, exportedValue), /*location*/ specifier)); + } + } + } + else { + // export * from "mod"; + if (moduleKind !== ModuleKind.AMD) { + write(createExpressionStatement(createExportStarHelperCall(createRequireCall(node)), /*location*/ node)); + } + else { + write(createExpressionStatement(createExportStarHelperCall(generatedName), /*location*/ node)); + } + } + } + } + + function visitExportAssignment(node: ExportAssignment, write: (node: Statement) => void): void { + if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { + emitExportDefault(node.expression, /*location*/ node, write); + } + } + + function emitExportDefault(expression: Expression, location: TextRange, write: (node: Statement) => void): void { + emitExportDefaultCompat(write); + let defaultName = createIdentifier("default", SyntaxKind.DefaultKeyword); + write(createExpressionStatement(createExportAssignment(defaultName, expression), location)); + } + + function emitExportDefaultCompat(write: (node: Statement) => void): void { + let originalSourceFile = getOriginalNodeIf(currentSourceFile, isSourceFile); + if (!originalSourceFile.symbol.exports["___esModule"]) { + if (languageVersion === ScriptTarget.ES3) { + let esModule = createIdentifier("__esModule"); + write(createExpressionStatement(createExportAssignment(esModule, createTrueKeyword()))); + } + else { + let exports = createIdentifier("exports"); + let esModule = createStringLiteral("__esModule"); + let descriptor = createDataPropertyDescriptor(createTrueKeyword(), PropertyDescriptorFlags.Empty); + write(createExpressionStatement(createDefinePropertyCall(exports, esModule, descriptor))); + } + } + } + + function emitExportImportAssignments(node: Node, write: (node: Statement) => void) { + let savedExportImportAssignmentWriter = exportAssignmentWriter; + exportAssignmentWriter = write; + emitExportImportAssignmentsUsingCapturedWriter(node); + exportAssignmentWriter = savedExportImportAssignmentWriter; + } + + function emitExportImportAssignmentsUsingCapturedWriter(node: Node) { + if (isAliasSymbolDeclaration(node) && resolver.isValueAliasDeclaration(node)) { + emitExportMemberAssignmentsUsingCapturedWriter((node).name); + } + + forEachChild(node, emitExportImportAssignmentsUsingCapturedWriter); + } + + function emitExportMemberAssignmentsUsingCapturedWriter(name: Identifier) { + emitExportMemberAssignments(name, exportAssignmentWriter); + } + + function emitExportMemberAssignments(name: Identifier, write: (node: Statement) => void): void { + if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { + for (let specifier of exportSpecifiers[name.text]) { + write(createExpressionStatement(createExportAssignment(specifier.name, name), /*location*/ specifier.name)); + } + } + } + + function visitVariableStatement(node: VariableStatement, write: (node: Statement) => void): void { + if (node.flags & NodeFlags.Export) { + pipeNode(node.declarationList, transformVariableDeclarationListToExpressionStatement, write); + } + else { + write(node); + } + } + + function transformVariableDeclarationListToExpressionStatement(node: VariableDeclarationList, write: (node: Statement) => void): void { + let expressions = mapNodes(node.declarations, transformVariableDeclarationToExpression); + if (expressions.length) { + write(createExpressionStatement(inlineExpressions(expressions))); + } + + pipeNodes(node.declarations, emitVariableExportAssignments, write); + } + + function transformVariableDeclarationToExpression(node: VariableDeclaration, write: (node: Expression) => void): void { + if (!node.initializer) { + return; + } + + transformBindingElementToExpressionWithParenthesisIfNeeded(node, write, /*parenthesizeObjectLiteralAssignment*/ true); + } + + /** + * @remarks + * This function is intended to be called from either `transformVariableDeclarationToExpression` or + * `transformBindingElementToExpression` and should not be called otherwise. + */ + function transformBindingElementToExpressionWithParenthesisIfNeeded(node: BindingElement, write: (node: Expression) => void, parenthesizeObjectLiteralAssignment?: boolean) { + let name = node.name; + let expr = isBindingPattern(name) + ? mapNode(name, transformBindingPatternToExpression) + : createPropertyAccessExpression2(createIdentifier("exports"), makeSynthesized(name)); + + let initializer = node.initializer; + if (initializer) { + expr = createAssignmentExpression(expr, initializer, /*location*/ node); + } + + if (parenthesizeObjectLiteralAssignment && isObjectBindingPattern(name)) { + expr = createParenthesizedExpression(expr); + } + else if (node.dotDotDotToken) { + expr = createSpreadElementExpression(expr); + } + + write(expr); + } + + /** + * @remarks + * This function is intended to be called from `transformVariableDeclarationToExpression` and should not be called otherwise. + */ + function transformBindingPatternToExpression(node: BindingPattern, write: (node: Expression) => void) { + switch (node.kind) { + case SyntaxKind.ObjectBindingPattern: + return transformObjectBindingPatternToExpression(node, write); + + case SyntaxKind.ArrayBindingPattern: + return transformArrayBindingPatternToExpression(node, write); + } + } + + /** + * @remarks + * This function is intended to be called from `transformBindingPatternToExpression` and should not be called otherwise. + */ + function transformObjectBindingPatternToExpression(node: ObjectBindingPattern, write: (node: Expression) => void) { + let properties = mapNodes(node.elements, transformBindingElementToObjectLiteralElement); + write(createObjectLiteralExpression(properties, /*location*/ node)); + } + + /** + * @remarks + * This function is intended to be called from `transformBindingPatternToExpression` and should not be called otherwise. + */ + function transformArrayBindingPatternToExpression(node: ArrayBindingPattern, write: (node: Expression) => void) { + let elements = mapNodes(node.elements, transformBindingElementToExpression); + write(createArrayLiteralExpression(elements, /*location*/ node)); + } + + /** + * @remarks + * This function is intended to be called from `transformObjectBindingPatternToExpression` and should not be called otherwise. + */ + function transformBindingElementToObjectLiteralElement(node: BindingElement, write: (node: ObjectLiteralElement) => void) { + let propertyName = node.propertyName || node.name; + let expr = mapNode(node, transformBindingElementToExpression); + write(createPropertyAssignment(propertyName, expr, /*location*/ node)); + } + + /** + * @remarks + * This function is intended to be called from `transformArrayBindingPatternToExpression` or + * `transformBindingElementToObjectLiteralElement` and should not be called otherwise. + */ + function transformBindingElementToExpression(node: BindingElement, write: (node: Expression) => void) { + transformBindingElementToExpressionWithParenthesisIfNeeded(node, write, /*parenthesizeObjectLiteralAssignment*/ false); + } + + function emitVariableExportAssignments(node: VariableDeclaration | BindingElement, write: (node: Statement) => void): void { + let name = node.name; + if (isIdentifier(name)) { + emitExportMemberAssignments(name, write); + } + else if (isBindingPattern(name)) { + pipeNodes(name.elements, emitVariableExportAssignments, write); + } + } + + function visitFunctionDeclaration(node: FunctionDeclaration, write: (node: Statement) => void): void { + const location: TextRange = node; + if (node.name) { + if (node.flags & NodeFlags.Export) { + write(createFunctionDeclaration2(node.name, node.parameters, node.body, location)); + if (node.flags & NodeFlags.Default) { + emitExportDefault(makeSynthesized(node.name), location, write); + } + } + else { + write(node); + } + + emitExportMemberAssignments(node.name, write); + } + else if (node.flags & NodeFlags.Default) { + emitExportDefault(createFunctionExpression3(node.parameters, node.body), location, write); + } + } + + function visitClassDeclaration(node: ClassDeclaration, write: (node: Statement) => void): void { + const location: TextRange = node; + if (node.name) { + if (node.flags & NodeFlags.Export) { + write(createClassDeclaration2(node.name, getClassExtendsHeritageClauseElement(node), node.members, location)); + if (node.flags & NodeFlags.Default) { + emitExportDefault(makeSynthesized(node.name), location, write); + } + } + else { + write(node); + } + + emitExportMemberAssignments(node.name, write); + } + else if (node.flags & NodeFlags.Default) { + emitExportDefault(createClassExpression3(getClassExtendsHeritageClauseElement(node), node.members), location, write); + } + } + + /** + * Substitution for identifiers exported at the top level of a module. + */ + function substituteExpressionWithFallback(node: Expression): Expression { + let substitute = substituteExpression(node); + return savedExpressionSubstution ? savedExpressionSubstution(substitute) : substitute; + } + + function substituteExpression(node: Expression): Expression { + if (isIdentifier(node)) { + return substituteExpressionIdentifier(node); + } + return node; + } + + function substituteExpressionIdentifier(node: Identifier): Expression { + let container = resolver.getReferencedExportContainer(node); + if (isSourceFile(container)) { + return createPropertyAccessExpression2(createIdentifier("exports"), makeSynthesized(node), /*location*/ node); + } + + return node; + } + + function tryCreateExportEquals(emitAsReturn: boolean) { + if (exportEquals && resolver.isValueAliasDeclaration(exportEquals)) { + if (emitAsReturn) { + return createReturnStatement(exportEquals.expression); + } + else { + let moduleExports = createPropertyAccessExpression3(createIdentifier("module"), "exports"); + let exportExpression = createAssignmentExpression(moduleExports, exportEquals.expression); + return createExpressionStatement(exportExpression); + } + } + return undefined; + } + + function getExternalModuleNameLiteral(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration) { + let moduleName = getExternalModuleName(importNode); + if (isStringLiteral(moduleName)) { + return tryRenameExternalModule(moduleName) || makeSynthesized(moduleName); + } + + return undefined; + } + + /** + * Some bundlers (SystemJS builder) sometimes want to rename dependencies. + * Here we check if alternative name was provided for a given moduleName and return it if possible. + */ + function tryRenameExternalModule(moduleName: LiteralExpression) { + if (currentSourceFile.renamedDependencies && hasProperty(currentSourceFile.renamedDependencies, moduleName.text)) { + return createStringLiteral(currentSourceFile.renamedDependencies[moduleName.text]); + } + return undefined; + } + + function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): Identifier { + let namespaceDeclaration = getNamespaceDeclarationNode(node); + if (namespaceDeclaration && !isDefaultImport(node)) { + return createIdentifier(getSourceTextOfNodeFromSourceFile(currentSourceFile, namespaceDeclaration.name)); + } + if (node.kind === SyntaxKind.ImportDeclaration && (node).importClause) { + return getGeneratedNameForNode(node); + } + if (node.kind === SyntaxKind.ExportDeclaration && (node).moduleSpecifier) { + return getGeneratedNameForNode(node); + } + } + + function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + if (node.kind === SyntaxKind.ImportEqualsDeclaration) { + return node; + } + + let importClause = (node).importClause; + if (importClause && importClause.namedBindings && importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + return importClause.namedBindings; + } + } + + function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + return isImportDeclaration(node) && node.importClause && !!node.importClause.name; + } + + function createRequireCall(importNode: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + let moduleName = getExternalModuleNameLiteral(importNode); + return createCallExpression2(createIdentifier("require"), moduleName ? [moduleName] : []); + } + + function emitExportAssignment(name: Identifier, value: Expression, location: TextRange, write: (node: Statement) => void) { + write(createExpressionStatement(createExportAssignment(name, value), location)); + } + + function createExportAssignment(name: Identifier, value: Expression) { + let exports = createIdentifier("exports"); + let exportMember: LeftHandSideExpression; + if (name.originalKeywordKind && languageVersion === ScriptTarget.ES3) { + let exportName = createStringLiteral(name.text); + exportMember = createElementAccessExpression2(exports, exportName); + } + else { + let exportName = makeSynthesized(name); + exportMember = createPropertyAccessExpression2(exports, exportName); + } + + return createAssignmentExpression(exportMember, value); + } + } + + export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver) { + let externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; + let exportSpecifiers: Map = {}; + let exportEquals: ExportAssignment = undefined; + let hasExportStars = false; + for (let node of sourceFile.statements) { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + if (!(node).importClause || + resolver.isReferencedAliasDeclaration((node).importClause, /*checkChildren*/ true)) { + // import "mod" + // import x from "mod" where x is referenced + // import * as x from "mod" where x is referenced + // import { x, y } from "mod" where at least one import is referenced + externalImports.push(node); + } + break; + + case SyntaxKind.ImportEqualsDeclaration: + if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference && resolver.isReferencedAliasDeclaration(node)) { + // import x = require("mod") where x is referenced + externalImports.push(node); + } + break; + + case SyntaxKind.ExportDeclaration: + if ((node).moduleSpecifier) { + if (!(node).exportClause) { + // export * from "mod" + externalImports.push(node); + hasExportStars = true; + } + else if (resolver.isValueAliasDeclaration(node)) { + // export { x, y } from "mod" where at least one export is a value symbol + externalImports.push(node); + } + } + else { + // export { x, y } + for (let specifier of (node).exportClause.elements) { + let name = (specifier.propertyName || specifier.name).text; + (exportSpecifiers[name] || (exportSpecifiers[name] = [])).push(specifier); + } + } + break; + + case SyntaxKind.ExportAssignment: + if ((node).isExportEquals && !exportEquals) { + // export = x + exportEquals = node; + } + break; + } + } + + return { externalImports, exportSpecifiers, exportEquals, hasExportStars }; + } +} \ No newline at end of file diff --git a/src/compiler/transforms/module/system.ts b/src/compiler/transforms/module/system.ts new file mode 100644 index 00000000000..4c902ecfdef --- /dev/null +++ b/src/compiler/transforms/module/system.ts @@ -0,0 +1,1156 @@ +/// +/// +/*@internal*/ +namespace ts { + export function createSystemModuleTransformation(transformer: Transformer): Transformation { + interface DependencyGroup { + name: Identifier; + externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; + } + + let { + makeUniqueName, + createUniqueIdentifier, + getGeneratedNameForNode, + hoistVariableDeclaration, + hoistFunctionDeclaration, + startLexicalEnvironment, + endLexicalEnvironment, + createNodes, + pipeNode, + pipeNodes, + mapNode, + mapNodes, + flattenNode, + visitNode, + visitNodes, + visitStatement, + accept + } = transformer; + + let compilerOptions = transformer.getCompilerOptions(); + let resolver = transformer.getEmitResolver(); + let languageVersion = getLanguageVersion(compilerOptions); + let moduleKind = getModuleKind(compilerOptions); + let currentSourceFile: SourceFile; + let externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; + let exportSpecifiers: Map; + let exportEquals: ExportAssignment; + let hasExportStars: boolean; + let exportFunctionForFile: Identifier; + let dependencyGroups: DependencyGroup[]; + let statementOffset: number; + let exportStarFunction: Identifier; + let exportedLocalNames: Identifier[]; + let exportedFunctionDeclarations: ExpressionStatement[] = []; + let noSubstitution: boolean[] = []; + + // Set up a substitution callback + let savedExpressionSubstitution = transformer.getExpressionSubstitution(); + transformer.setExpressionSubstitution(substituteExpressionWithFallback); + + return transformSystemModule; + + /** + * Transforms a source file to a System module. + */ + function transformSystemModule(node: SourceFile) { + if (isExternalModule(node) || compilerOptions.isolatedModules) { + return transformSystemModuleWorker(node); + } + + return node; + } + + /** + * Transforms a source file to a System module. + */ + function transformSystemModuleWorker(node: SourceFile) { + currentSourceFile = node; + exportFunctionForFile = undefined; + dependencyGroups = undefined; + statementOffset = undefined; + + // Get an updated source file flattened into a SystemJS module. + return updateSourceFileNode(node, flattenNode(node, emitSystemModule), node.endOfFileToken); + } + + /** + * Emits file prologue directives prior to a module body. + */ + function emitPrologueDirectives(statements: NodeArray, write: (node: Statement) => void): number { + for (let i = 0; i < statements.length; ++i) { + if (isPrologueDirective(statements[i])) { + write(statements[i]); + } + else { + return i; + } + } + + return statements.length; + } + + /** + * Emits the source file as a System module. + */ + function emitSystemModule(node: SourceFile, write: (node: Statement) => void) { + // System modules has the following shape: + // + // System.register(['dep-1', ... 'dep-n'], function(exports) { + // + // }); + // + // 'exports' here is a function `exports(name: string, value: T): T` + // that is used to publish exported values. + // + // 'exports' returns its 'value' argument so in most cases expressions + // that mutate exported values can be rewritten as: + // + // expr -> exports('name', expr). + // + // The only exception in this rule is postfix unary operators, + // see comment to 'substitutePostfixUnaryExpression' for more details. + + // Collect information about the external module and dependency groups. + ({ externalImports, exportSpecifiers, exportEquals, hasExportStars } = collectExternalModuleInfo(node, resolver)); + dependencyGroups = collectDependencyGroups(externalImports); + + // Emit prologue directives before the call to `System.register`. + statementOffset = emitPrologueDirectives(node.statements, write); + + // Make sure that the name of the 'exports' function does not conflict with + // existing identifiers. + exportFunctionForFile = transformer.createUniqueIdentifier("exports"); + + // Map the dependency groups into an array of imports. + let imports = createArrayLiteralExpression(map(dependencyGroups, getNameOfDependencyGroup)); + + + // Create the module body function + let moduleBodyStatements = flattenNode(node, emitSystemModuleBody); + let moduleBodyFunction = createSystemModuleBodyFunction(node, moduleBodyStatements); + + // Write the call to `System.register` + write(createSystemRegisterCall(node.moduleName, imports, moduleBodyFunction)); + } + + /** + * Emits the statements for the module body function for the source file. + * @param node The source file for the module. + * @param write The callback used to emit statements. + */ + function emitSystemModuleBody(node: SourceFile, write: (node: Statement) => void) { + // Shape of the body in system modules: + // + // function (exports) { + // + // + // + // return { + // setters: [ + // + // ], + // execute: function() { + // + // } + // } + // + // } + // + // i.e: + // + // import {x} from 'file1' + // var y = 1; + // export function foo() { return y + x(); } + // console.log(y); + // + // Will be transformed to: + // + // function(exports) { + // var file_1; // local alias + // var y; + // function foo() { return y + file_1.x(); } + // exports("foo", foo); + // return { + // setters: [ + // function(v) { file_1 = v } + // ], + // execute(): function() { + // y = 1; + // console.log(y); + // } + // }; + // } + + // We start a new lexical environment in this function body, but *not* in the + // body of the execute function. This allows us to emit temporary declarations + // only in the outer module body and not in the inner one. + startLexicalEnvironment(); + + // Visit the statements of the source file, emitting any transformations into + // the `executeStatements` array. We do this *before* we fill the `setters` array + // as we both emit transformations as well as aggregate some data used when creating + // setters. This allows us to reduce the number of times we need to loop through the + // statements of the source file. + let executeStatements = visitNodes(node.statements, visitModuleElement, isStatementNode, statementOffset); + + // We emit the lexical environment (hoisted variables and function declarations) + // early to align roughly with our previous emit output. + // Two key differences in this approach are: + // - Temporary variables will appear at the top rather than at the bottom of the file + // - Calls to the exporter for exported function declarations are grouped after + // the declarations. + endLexicalEnvironment(write); + + // Emit early exports for function declarations. + forEach(exportedFunctionDeclarations, write); + + if (hasExportStars) { + emitExportStar(write); + } + + // Here we emit the setters for the source file to the `setters` array. + let setters = createNodes(emitSetters); + + // Write the module definition object. + write(createReturnStatement(createModuleDefinitionObject(setters, executeStatements))); + } + + function emitExportStar(write: (node: Statement) => void): void { + // when resolving exports local exported entries/indirect exported entries in the module + // should always win over entries with similar names that were added via star exports + // to support this we store names of local/indirect exported entries in a set. + // this set is used to filter names brought by star expors. + + // local names set should only be added if we have anything exported + if (!exportedLocalNames && isEmpty(exportSpecifiers)) { + // no exported declarations (export var ...) or export specifiers (export {x}) + // check if we have any non star export declarations. + let hasExportDeclarationWithExportClause = false; + for (let externalImport of externalImports) { + if (externalImport.kind === SyntaxKind.ExportDeclaration && (externalImport).exportClause) { + hasExportDeclarationWithExportClause = true; + break; + } + } + + if (!hasExportDeclarationWithExportClause) { + // we still need to emit exportStar helper + write(createExportStarFunction(/*localNames*/ undefined)); + return; + } + } + + let exportedNames: ObjectLiteralElement[] = []; + if (exportedLocalNames) { + for (let exportedLocalName of exportedLocalNames) { + // write name of exported declaration, i.e 'export var x...' + let exportedName = createStringLiteralForIdentifier(exportedLocalName); + exportedNames.push(createPropertyAssignment(exportedName, createTrueKeyword())); + } + } + + for (let externalImport of externalImports) { + if (externalImport.kind !== SyntaxKind.ExportDeclaration) { + continue; + } + + let exportDecl = externalImport; + if (!exportDecl.exportClause) { + // export * from ... + continue; + } + + for (let element of exportDecl.exportClause.elements) { + // write name of indirectly exported entry, i.e. 'export {x} from ...' + let exportedName = createStringLiteralForIdentifier(element.name || element.propertyName); + exportedNames.push(createPropertyAssignment(exportedName, createTrueKeyword())); + } + } + + let exportedNamesStorageRef = createUniqueIdentifier("exportedNames"); + write(createVariableStatement3(exportedNamesStorageRef, createObjectLiteralExpression(exportedNames))); + write(createExportStarFunction(exportedNamesStorageRef)); + } + + /** + * Emits a setter callback for each dependency group. + * @param write The callback used to write each callback. + */ + function emitSetters(write: (node: Expression) => void) { + for (let group of dependencyGroups) { + // derive a unique name for parameter from the first named entry in the group + let parameterName = createUniqueIdentifier(forEach(group.externalImports, getLocalNameTextForExternalImport) || ""); + let statements: Statement[] = []; + for (let entry of group.externalImports) { + let importVariableName = getLocalNameForExternalImport(entry); + switch (entry.kind) { + case SyntaxKind.ImportDeclaration: + if (!(entry).importClause) { + // 'import "..."' case + // module is imported only for side-effects, no emit required + break; + } + + // fall-through + case SyntaxKind.ImportEqualsDeclaration: + Debug.assert(importVariableName !== undefined); + // save import into the local + statements.push(createExpressionStatement(createAssignmentExpression(importVariableName, parameterName))); + break; + + case SyntaxKind.ExportDeclaration: + Debug.assert(importVariableName !== undefined); + if ((entry).exportClause) { + // export {a, b as c} from 'foo' + // + // emit as: + // + // exports_({ + // "a": _["a"], + // "c": _["b"] + // }); + let properties: PropertyAssignment[] = []; + for (let e of (entry).exportClause.elements) { + let exportName = createStringLiteralForIdentifier(e.name); + let importName = createStringLiteralForIdentifier(e.propertyName || e.name); + let importAccess = createElementAccessExpression2(parameterName, importName); + properties.push(createPropertyAssignment(exportName, importAccess)); + } + + let exportedValues = createObjectLiteralExpression(properties); + statements.push(createExpressionStatement(createCallExpression2(exportFunctionForFile, [exportedValues]))); + } + else { + // export * from 'foo' + // + // emit as: + // + // exportStar(foo_1_1); + statements.push(createExpressionStatement(createCallExpression2(exportStarFunction, [parameterName]))); + } + break; + } + } + + let setter = createFunctionExpression3([createParameter2(parameterName)], createBlock(statements)); + startOnNewLine(setter); + write(setter); + } + } + + function visitModuleElement(node: Node, write: (node: Node) => void): void { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + return visitImportDeclaration(node, write); + case SyntaxKind.ImportEqualsDeclaration: + return visitImportEqualsDeclaration(node, write); + case SyntaxKind.ExportDeclaration: + return visitExportDeclaration(node, write); + case SyntaxKind.ExportAssignment: + return visitExportAssignment(node, write); + default: + return visitNonModuleElement(node, write); + } + } + + function visitNonModuleElement(node: Node, write: (node: Node) => void): void { + switch (node.kind) { + case SyntaxKind.VariableStatement: + return visitVariableStatement(node, write); + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(node, write); + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node, write); + case SyntaxKind.ForStatement: + return visitForStatement(node, write); + case SyntaxKind.ForInStatement: + return visitForInStatement(node, write); + case SyntaxKind.ForOfStatement: + return visitForOfStatement(node, write); + case SyntaxKind.DoStatement: + return visitDoStatement(node, write); + case SyntaxKind.WhileStatement: + return visitWhileStatement(node, write); + case SyntaxKind.LabeledStatement: + return visitLabeledStatement(node, write); + case SyntaxKind.WithStatement: + return visitWithStatement(node, write); + case SyntaxKind.SwitchStatement: + return visitSwitchStatement(node, write); + case SyntaxKind.CaseBlock: + return visitCaseBlock(node, write); + case SyntaxKind.CaseClause: + return visitCaseClause(node, write); + case SyntaxKind.DefaultClause: + return visitDefaultClause(node, write); + case SyntaxKind.TryStatement: + return visitTryStatement(node, write); + case SyntaxKind.CatchClause: + return visitCatchClause(node, write); + case SyntaxKind.Block: + return visitBlock(node, write); + default: + return write(node); + } + } + + function visitImportDeclaration(node: ImportDeclaration, write: (node: Statement) => void): void { + if (node.importClause && contains(externalImports, node)) { + hoistVariableDeclaration(getLocalNameForExternalImport(node)); + } + } + + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration, write: (node: Statement) => void): void { + if (contains(externalImports, node)) { + hoistVariableDeclaration(getLocalNameForExternalImport(node)); + } + + // NOTE(rbuckton): Do we support export import = require('') in System? + } + + function visitExportDeclaration(node: ExportDeclaration, write: (node: Statement) => void): void { + if (!node.moduleSpecifier) { + pipeNodes(node.exportClause.elements, emitExportSpecifier, write); + } + } + + function emitExportSpecifier(specifier: ExportSpecifier, write: (node: Statement) => void): void { + if (!resolver.getReferencedValueDeclaration(specifier.propertyName || specifier.name) && + !resolver.isValueAliasDeclaration(specifier)) { + return; + } + + recordExportName(specifier.name); + write(createExpressionStatement(createExportExpression(specifier.name, specifier.propertyName || specifier.name))); + } + + function visitExportAssignment(node: ExportAssignment, write: (node: Statement) => void): void { + if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { + write(createExportStatement(createStringLiteral("default"), node.expression)); + } + } + + /** + * Visits a variable statement, hoisting declared names to the top-level module body. + * Each declaration is rewritten into an assignment expression. + * @param node The variable statement to visit. + * @param write The callback used to write any resulting statements. + */ + function visitVariableStatement(node: VariableStatement, write: (node: Statement) => void): void { + let through = node.flags & NodeFlags.Export + ? transformExportedVariableDeclarationListToStatement + : transformVariableDeclarationListToStatement; + pipeNode(node.declarationList, through, write); + } + + /** + * Transforms an exported VariableDeclarationList into an ExpressionStatement containing assignment expressions. + * @param node The list to transform + * @param write The callback used to write the resulting expression statement. + * @param isExported A value used to indicate whether the containing statement was exported. + */ + function transformExportedVariableDeclarationListToStatement(node: VariableDeclarationList, write: (node: Statement) => void): void { + transformVariableDeclarationListToStatement(node, write, /*isExported*/ true); + } + + /** + * Transforms a VariableDeclarationList into an ExpressionStatement containing assignment expressions. + * @param node The list to transform + * @param write The callback used to write the resulting expression statement. + * @param isExported A value used to indicate whether the containing statement was exported. + */ + function transformVariableDeclarationListToStatement(node: VariableDeclarationList, write: (node: Statement) => void, isExported?: boolean): void { + let expressions = mapNodes(node.declarations, isExported ? transformExportedVariableDeclarationToExpression : transformVariableDeclarationToExpression); + if (expressions.length) { + write(createExpressionStatement(inlineExpressions(expressions))); + } + } + + /** + * Transforms a VariableDeclarationList into an initializer Expression for a for loop. + * @param node The list to transform + * @param write The callback used to write the resulting expression statement. + */ + function transformVariableDeclarationListToExpression(node: VariableDeclarationList, write: (node: Expression) => void): void { + let expressions = mapNodes(node.declarations, transformVariableDeclarationToExpression); + if (expressions.length) { + write(inlineExpressions(expressions)); + } + else { + write(createOmittedExpression()); + } + } + + /** + * Transforms an exported VariableDeclaration into one or more assignment expressions. + * @param node The VariableDeclaration to transform. + * @param write The callback used to write any assignment expressions for the variable. + * @param isExported A value used to indicate whether the containing statement was exported. + */ + function transformExportedVariableDeclarationToExpression(node: VariableDeclaration, write: (node: Expression) => void): void { + transformVariableDeclarationToExpression(node, write, /*isExported*/ true); + } + + /** + * Transforms a VariableDeclaration into one or more assignment expressions. + * @param node The VariableDeclaration to transform. + * @param write The callback used to write any assignment expressions for the variable. + * @param isExported A value used to indicate whether the containing statement was exported. + */ + function transformVariableDeclarationToExpression(node: VariableDeclaration, write: (node: Expression) => void, isExported?: boolean): void { + // Hoist any bound names within the declaration. + hoistBindingElement(node, isExported); + + if (!node.initializer) { + // If the variable has no initializer, ignore it. + return; + } + + let name = node.name; + if (isIdentifier(name)) { + // If the variable has an IdentifierName, write out an assignment expression in its place. + write(createAssignmentExpression(name, node.initializer)); + } + else { + // If the variable has a BindingPattern, flatten the variable into multiple assignment expressions. + flattenVariableDestructuringAsExpression(transformer, node, write); + } + } + + /** + * Visits a FunctionDeclaration, hoisting it to the outer module body function. + * @param node The function declaration to visit. + * @param write A callback used to write statements to the inner module body function. + */ + function visitFunctionDeclaration(node: FunctionDeclaration, write: (node: Statement) => void): void { + let isExported = node.flags & NodeFlags.Export; + if (isExported) { + // If the function is exported, ensure it has a name and rewrite the function without any export flags. + let name = node.name || getGeneratedNameForNode(node); + node = createFunctionDeclaration3(node.asteriskToken, name, node.parameters, node.body, node); + + // Record a declaration export in the outer module body function. + recordExportedFunctionDeclaration(node); + + let isDefaultExport = node.flags & NodeFlags.Default; + if (!isDefaultExport) { + recordExportName(name); + } + } + + // Hoist the function declaration to the outer module body function. + hoistFunctionDeclaration(node); + } + + /** + * Visits a ClassDeclaration, hoisting its name to the outer module body function. + * @param node The class declaration to visit. + * @param write A callback used to write statements to the inner module body function. + */ + function visitClassDeclaration(node: ClassDeclaration, write: (node: Statement) => void): void { + // Hoist the name of the class declaration to the outer module body function. + let name = getDeclarationName(node); + hoistVariableDeclaration(name); + + // Rewrite the class declaration into an assignment of a class expression. + let baseTypeNode = getClassExtendsHeritageClauseElement(node); + let expression = createClassExpression2(node.name, baseTypeNode, node.members, node); + write(createAssignmentStatement(name, expression, node)); + + // If the class was exported, write a declaration export to the inner module body function. + let isExported = node.flags & NodeFlags.Export; + if (isExported) { + let isDefaultExport = node.flags & NodeFlags.Default; + if (!isDefaultExport) { + recordExportName(name); + } + + write(createDeclarationExport(node)); + } + } + + /** + * Visits the body of a ForStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitForStatement(node: ForStatement, write: (node: ForStatement) => void): void { + let initializer = node.initializer; + if (isVariableDeclarationList(initializer)) { + write(updateForStatement(node, + mapNode(initializer, transformVariableDeclarationListToExpression), + node.condition, + node.incrementor, + visitStatement(node.statement, visitNonModuleElement) + )); + } + else { + write(accept(node, visitNonModuleElement)); + } + } + + /** + * Transforms and hoists the declaration list of a ForInStatement or ForOfStatement into an expression. + * @param node The decalaration list to transform. + * @param write A callback used to write out the result. + */ + function transformForBinding(node: VariableDeclarationList, write: (node: Expression) => void): void { + let firstDeclaration = firstOrUndefined(node.declarations); + hoistBindingElement(firstDeclaration, /*isExported*/ false); + + let name = firstDeclaration.name; + write(isIdentifier(name) ? name : convertBindingPatternToExpression(name)); + } + + /** + * Visits the body of a ForInStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitForInStatement(node: ForInStatement, write: (node: ForInStatement) => void): void { + let initializer = node.initializer; + if (isVariableDeclarationList(initializer)) { + write(updateForInStatement( + node, + mapNode(initializer, transformForBinding), + node.expression, + visitStatement(node.statement, visitNonModuleElement) + )); + } + else { + write(accept(node, visitNonModuleElement)); + } + } + + /** + * Visits the body of a ForOfStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitForOfStatement(node: ForOfStatement, write: (node: ForOfStatement) => void): void { + let initializer = node.initializer; + if (isVariableDeclarationList(initializer)) { + write(updateForOfStatement( + node, + mapNode(initializer, transformForBinding), + node.expression, + visitStatement(node.statement, visitNonModuleElement) + )); + } + else { + write(accept(node, visitNonModuleElement)); + } + } + + /** + * Visits the body of a DoStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitDoStatement(node: DoStatement, write: (node: DoStatement) => void): void { + write(updateDoStatement( + node, + visitStatement(node.statement, visitNonModuleElement), + node.expression + )); + } + + /** + * Visits the body of a WhileStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitWhileStatement(node: WhileStatement, write: (node: WhileStatement) => void): void { + write(updateWhileStatement( + node, + node.expression, + visitStatement(node.statement, visitNonModuleElement) + )); + } + + /** + * Visits the body of a LabeledStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitLabeledStatement(node: LabeledStatement, write: (node: LabeledStatement) => void): void { + write(updateLabeledStatement( + node, + node.label, + visitStatement(node.statement, visitNonModuleElement) + )); + } + + /** + * Visits the body of a WithStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitWithStatement(node: WithStatement, write: (node: WithStatement) => void): void { + write(updateWithStatement( + node, + node.expression, + visitStatement(node.statement, visitNonModuleElement) + )); + } + + /** + * Visits the body of a SwitchStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitSwitchStatement(node: SwitchStatement, write: (node: SwitchStatement) => void): void { + write(updateSwitchStatement( + node, + node.expression, + visitNode(node.caseBlock, visitNonModuleElement, isCaseBlock) + )); + } + + /** + * Visits the body of a CaseBlock to hoist declarations. + * @param node The node to visit. + * @param write A callback used to write out the result. + */ + function visitCaseBlock(node: CaseBlock, write: (node: CaseBlock) => void): void { + write(updateCaseBlock( + node, + visitNodes(node.clauses, visitNonModuleElement, isCaseOrDefaultClause) + )); + } + + /** + * Visits the body of a CaseClause to hoist declarations. + * @param node The clause to visit. + * @param write A callback used to write out the result. + */ + function visitCaseClause(node: CaseClause, write: (node: CaseClause) => void): void { + write(updateCaseClause( + node, + node.expression, + visitNodes(node.statements, visitNonModuleElement, isStatementNode) + )); + } + + /** + * Visits the body of a DefaultClause to hoist declarations. + * @param node The clause to visit. + * @param write A callback used to write out the result. + */ + function visitDefaultClause(node: DefaultClause, write: (node: DefaultClause) => void): void { + write(accept(node, visitNonModuleElement)); + } + + /** + * Visits the body of a TryStatement to hoist declarations. + * @param node The statement to visit. + * @param write A callback used to write out the result. + */ + function visitTryStatement(node: TryStatement, write: (node: TryStatement) => void): void { + write(accept(node, visitNonModuleElement)); + } + + /** + * Visits the body of a CatchClause to hoist declarations. + * @param node The clause to visit. + * @param write A callback used to write out the result. + */ + function visitCatchClause(node: CatchClause, write: (node: CatchClause) => void): void { + write(updateCatchClause( + node, + node.variableDeclaration, + visitNode(node.block, visitNonModuleElement, isBlock) + )); + } + + /** + * Visits the body of a Block to hoist declarations. + * @param node The block to visit. + * @param write A callback used to write out the result. + */ + function visitBlock(node: Block, write: (node: Block) => void): void { + write(accept(node, visitNonModuleElement)); + } + + // + // Substitutions + // + + /** + * Attempt to subsitute the provided expression, falling back to a substitution from a previous phase. + * @param node The node to substitute. + * @param parentNode The parent of node. + * @param sourceFile The source file for the node. + */ + function substituteExpressionWithFallback(node: Expression): Expression { + let substitute = substituteExpression(node); + return savedExpressionSubstitution ? savedExpressionSubstitution(substitute) : substitute; + } + + /** + * Substitute the expression, if necessary. + * @param node The node to substitute. + * @param parentNode The parent of node. + * @param sourceFile The source file for the node. + */ + function substituteExpression(node: Expression): Expression { + if (isIdentifier(node)) { + return substituteExpressionIdentifier(node) + } + else if (isBinaryExpression(node)) { + return substituteBinaryExpression(node); + } + else if (isPostfixUnaryExpression(node)) { + return substitutePostfixUnaryExpression(node); + } + return node; + } + + /** + * Substitution for identifiers exported at the top level of a module. + */ + function substituteExpressionIdentifier(node: Identifier): Expression { + let importDeclaration = resolver.getReferencedImportDeclaration(node); + if (importDeclaration) { + return createImportBinding(importDeclaration); + } + return node; + } + + function substituteBinaryExpression(node: BinaryExpression): Expression { + if (isAssignmentOperator(node.operatorToken.kind)) { + return substituteAssignmentExpression(node); + } + return node; + } + + function substituteAssignmentExpression(node: BinaryExpression): Expression { + if (!noSubstitution[getNodeId(node)]) { + noSubstitution[getNodeId(node)] = true; + + let left = node.left; + if (isIdentifier(left)) { + let exportDeclaration = resolver.getReferencedExportContainer(left); + if (exportDeclaration) { + return createExportExpression(left, node); + } + } + else if (isObjectLiteralExpression(left) || isArrayLiteralExpression(left)) { + if (hasExportedReferenceInDestructuringPattern(left)) { + return mapNode(node, substituteDestructuring); + } + } + } + + return node; + } + + function isExportedBinding(name: Identifier) { + return isSourceFile(resolver.getReferencedExportContainer(name)); + } + + function hasExportedReferenceInDestructuringPattern(node: ObjectLiteralExpression | ArrayLiteralExpression | Identifier): boolean { + if (isObjectLiteralExpression(node)) { + return forEach(node.properties, hasExportedReferenceInObjectDestructuringElement); + } + else if (isArrayLiteralExpression(node)) { + return forEach(node.elements, hasExportedReferenceInArrayDestructuringElement); + } + else if (isIdentifier(node)) { + return isExportedBinding(node); + } + + return false; + } + + function hasExportedReferenceInObjectDestructuringElement(node: ObjectLiteralElement): boolean { + if (isShorthandPropertyAssignment(node)) { + return isExportedBinding(node.name); + } + else if (isPropertyAssignment(node)) { + return hasExportedReferenceInDestructuringElement(node.initializer); + } + else { + return false; + } + } + + function hasExportedReferenceInArrayDestructuringElement(node: Expression): boolean { + if (isSpreadElementExpression(node)) { + let expression = node.expression; + return isIdentifier(expression) && isExportedBinding(expression); + } + else { + return hasExportedReferenceInDestructuringElement(node); + } + } + + function hasExportedReferenceInDestructuringElement(node: Expression): boolean { + if (isBinaryExpression(node)) { + let left = node.left; + return node.operatorToken.kind === SyntaxKind.EqualsToken + && isDestructuringPattern(left) + && hasExportedReferenceInDestructuringPattern(left); + } + else if (isIdentifier(node)) { + return isExportedBinding(node); + } + else if (isSpreadElementExpression(node)) { + let expression = node.expression; + return isIdentifier(expression) && isExportedBinding(expression); + } + else if (isDestructuringPattern(node)) { + return hasExportedReferenceInDestructuringPattern(node); + } + else { + return false; + } + } + + function isDestructuringPattern(node: Expression): node is ObjectLiteralExpression | ArrayLiteralExpression | Identifier { + return isIdentifier(node) || isObjectLiteralExpression(node) || isArrayLiteralExpression(node); + } + + function substituteDestructuring(node: BinaryExpression, write: (node: Expression) => void): void { + flattenDestructuringAssignment(transformer, node, write); + } + + function substitutePostfixUnaryExpression(node: PostfixUnaryExpression): Expression { + let operand = node.operand; + if (isIdentifier(operand)) { + let exportDeclaration = resolver.getReferencedExportContainer(operand); + if (exportDeclaration) { + let prefixUnary = createPrefixUnaryExpression(node.operator, operand, node); + let exportName = createStringLiteralForIdentifier(operand); + let exportCall = createExportExpression(operand, prefixUnary); + let expression = node.operator === SyntaxKind.PlusPlusToken + ? createSubtractExpression(exportCall, createNumericLiteral2(1)) + : createAddExpression(exportCall, createNumericLiteral2(1)); + return createParenthesizedExpression(expression); + } + } + return node; + } + + function createSystemRegisterCall(moduleName: string, imports: ArrayLiteralExpression, moduleBodyFunction: FunctionExpression) { + let registerArguments: Expression[] = [imports, moduleBodyFunction]; + if (moduleName) { + registerArguments.unshift(createStringLiteral(moduleName)); + } + + let systemRegister = createPropertyAccessExpression3(createIdentifier("System"), "register"); + return createExpressionStatement(createCallExpression2(systemRegister, registerArguments)); + } + + /** + * Creates the module body function for a `System.register` call. + * @param node The source file for the module. + */ + function createSystemModuleBodyFunction(node: SourceFile, statements: Statement[]) { + // Flatten the source file into multiple statements for the new module body. + let moduleBody = createBlock(statements); + + // Add emit helpers if needed + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitHelpersMask) { + transformer.setGeneratedNodeFlags(moduleBody, GeneratedNodeFlags.EmitHelpers); + } + + return createFunctionExpression3([createParameter2(exportFunctionForFile)], moduleBody); + } + + function createModuleDefinitionObject(setters: Expression[], statements: Statement[]) { + return createObjectLiteralExpression2({ + "setters": createArrayLiteralExpression(setters), + "execute": createFunctionExpression3([], createBlock(statements)) + }); + } + + function getExternalModuleNameLiteral(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration) { + let moduleName = getExternalModuleName(importNode); + if (isStringLiteral(moduleName)) { + return tryRenameExternalModule(moduleName) || makeSynthesized(moduleName); + } + + return undefined; + } + + /** + * Some bundlers (SystemJS builder) sometimes want to rename dependencies. + * Here we check if alternative name was provided for a given moduleName and return it if possible. + */ + function tryRenameExternalModule(moduleName: LiteralExpression) { + if (currentSourceFile.renamedDependencies && hasProperty(currentSourceFile.renamedDependencies, moduleName.text)) { + return createStringLiteral(currentSourceFile.renamedDependencies[moduleName.text]); + } + return undefined; + } + + function getLocalNameTextForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string { + let name = getLocalNameForExternalImport(node); + return name ? name.text : undefined; + } + + function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): Identifier { + let namespaceDeclaration = getNamespaceDeclarationNode(node); + if (namespaceDeclaration && !isDefaultImport(node)) { + return createIdentifier(getSourceTextOfNodeFromSourceFile(currentSourceFile, namespaceDeclaration.name)); + } + if (node.kind === SyntaxKind.ImportDeclaration && (node).importClause) { + return getGeneratedNameForNode(node); + } + if (node.kind === SyntaxKind.ExportDeclaration && (node).moduleSpecifier) { + return getGeneratedNameForNode(node); + } + } + + /** + * Gets a name to use for a DeclarationStatement. + * @param node The declaration statement. + */ + function getDeclarationName(node: DeclarationStatement) { + return node.name ? makeSynthesized(node.name) : getGeneratedNameForNode(node); + } + + function createExportStarFunction(localNames: Identifier) { + exportStarFunction = createUniqueIdentifier("exportStar"); + let m = createIdentifier("m"); + let n = createIdentifier("n"); + let exports = createIdentifier("exports"); + let condition: Expression = createStrictInequalityExpression(n, createStringLiteral("default")); + if (localNames) { + condition = createLogicalAndExpression( + condition, + createLogicalNotExpression(createHasOwnPropertyCall(localNames, n)) + ); + } + + return createFunctionDeclaration2(exportStarFunction, [createParameter2(m)], createBlock([ + createVariableStatement3(exports, createObjectLiteralExpression([])), + createForInStatement(createVariableDeclarationList2(n), m, createBlock([ + createIfStatement(condition, createExpressionStatement(createAssignmentExpression( + createElementAccessExpression2(exports, n), + createElementAccessExpression2(m, n) + ))) + ])), + createExpressionStatement(createCallExpression2(exportFunctionForFile, [exports])) + ])); + } + + /** + * Creates a call to the current file's export function to export a value. + * @param name The bound name of the export. + * @param value The exported value. + */ + function createExportExpression(name: Identifier | StringLiteral, value: Expression) { + let exportName = isIdentifier(name) ? createStringLiteralForIdentifier(name) : name; + return createCallExpression2(exportFunctionForFile, [exportName, value]); + } + + /** + * Creates a call to the current file's export function to export a value. + * @param name The bound name of the export. + * @param value The exported value. + */ + function createExportStatement(name: Identifier | StringLiteral, value: Expression) { + return createExpressionStatement(createExportExpression(name, value)); + } + + /** + * Creates a call to the current file's export function to export a declaration. + * @param node The declaration to export. + */ + function createDeclarationExport(node: DeclarationStatement) { + let declarationName = getDeclarationName(node); + let exportName = node.flags & NodeFlags.Default ? createStringLiteral("default") : declarationName; + return createExportStatement(exportName, declarationName); + } + + function createImportBinding(importDeclaration: Declaration): LeftHandSideExpression { + let importAlias: Identifier; + let name: Identifier; + if (isImportClause(importDeclaration)) { + importAlias = getGeneratedNameForNode(importDeclaration.parent); + name = createIdentifier("default", SyntaxKind.DefaultKeyword); + } + else if (isImportSpecifier(importDeclaration)) { + importAlias = getGeneratedNameForNode(importDeclaration.parent.parent.parent); + name = importDeclaration.propertyName || importDeclaration.name; + } + + if (name.originalKeywordKind && languageVersion === ScriptTarget.ES3) { + return createElementAccessExpression2(importAlias, createStringLiteralForIdentifier(name)); + } + else { + return createPropertyAccessExpression2(importAlias, makeSynthesized(name)); + } + } + + function collectDependencyGroups(externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]) { + let groupIndices: Map = {}; + let dependencyGroups: DependencyGroup[] = []; + for (let i = 0; i < externalImports.length; ++i) { + let externalImport = externalImports[i]; + let externalModuleName = getExternalModuleNameLiteral(externalImport); + let text = externalModuleName.text; + if (hasProperty(groupIndices, text)) { + // deduplicate/group entries in dependency list by the dependency name + let groupIndex = groupIndices[text]; + dependencyGroups[groupIndex].externalImports.push(externalImport); + continue; + } + else { + groupIndices[text] = dependencyGroups.length; + dependencyGroups.push({ + name: externalModuleName, + externalImports: [externalImport] + }); + } + } + + return dependencyGroups; + } + + function getNameOfDependencyGroup(dependencyGroup: DependencyGroup) { + return dependencyGroup.name; + } + + function recordExportName(name: Identifier) { + if (!exportedLocalNames) { + exportedLocalNames = []; + } + + exportedLocalNames.push(name); + } + + function recordExportedFunctionDeclaration(node: FunctionDeclaration) { + if (!exportedFunctionDeclarations) { + exportedFunctionDeclarations = []; + } + + exportedFunctionDeclarations.push(createDeclarationExport(node)); + } + + function hoistBindingElement(node: VariableDeclaration | BindingElement, isExported: boolean) { + let name = node.name; + if (isIdentifier(name)) { + hoistVariableDeclaration(makeSynthesized(name)); + if (isExported) { + recordExportName(name); + } + } + else if (isBindingPattern(name)) { + forEach(name.elements, isExported ? hoistExportedBindingElement : hoistNonExportedBindingElement); + } + } + + function hoistExportedBindingElement(node: VariableDeclaration | BindingElement) { + hoistBindingElement(node, /*isExported*/ true); + } + + function hoistNonExportedBindingElement(node: VariableDeclaration | BindingElement) { + hoistBindingElement(node, /*isExported*/ false); + } + } +} \ No newline at end of file diff --git a/src/compiler/transforms/ts.ts b/src/compiler/transforms/ts.ts index afa035c044a..5bff2514047 100644 --- a/src/compiler/transforms/ts.ts +++ b/src/compiler/transforms/ts.ts @@ -1,4 +1,5 @@ /// +/// /*@internal*/ namespace ts { export function createTypeScriptTransformation(transformer: Transformer): Transformation { @@ -38,8 +39,8 @@ namespace ts { let exportSpecifiers: Map; let exportEquals: ExportAssignment; let exportFunctionForFile: string; - let savedSubstituteExpressionIdentifier = transformer.getExpressionIdentifierSubstitution(); - transformer.setExpressionIdentifierSubstitution(substituteExpressionIdentifier); + let savedExpressionSubstution = transformer.getExpressionSubstitution(); + transformer.setExpressionSubstitution(substituteExpressionWithFallback); return transformTypeScript; @@ -58,7 +59,10 @@ namespace ts { exportFunctionForFile = undefined; currentSourceFile = node; - node = visitSourceFile(node, visitor); + let visited = visitSourceFile(node, visitor); + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitHelpersMask) { + transformer.setGeneratedNodeFlags(visited, GeneratedNodeFlags.EmitHelpers); + } externalImports = undefined; exportSpecifiers = undefined; @@ -66,7 +70,7 @@ namespace ts { exportFunctionForFile = undefined; currentSourceFile = undefined; - return node; + return visited; } /** @@ -465,7 +469,7 @@ namespace ts { parameters = visitNodes(constructor.parameters, visitor, isParameter); } else if (baseTypeNode) { - parameters = [createRestParameter(createIdentifier("args"), /*location*/ undefined, NodeFlags.GeneratedRest)]; + parameters = [createRestParameter(createIdentifier("args"), /*location*/ undefined, NodeFlags.Generated)]; } // transform the body of the constructor @@ -996,7 +1000,7 @@ namespace ts { } let callExpr = createCallExpression2(parenExpr, [moduleParam]); - let callStmt = createExpressionStatement(callExpr, location, NodeFlags.GeneratedNamespace); + let callStmt = createExpressionStatement(callExpr, location, NodeFlags.Generated); callStmt.original = node; write(callStmt); } @@ -1030,13 +1034,24 @@ namespace ts { return !!(resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalModuleMergesWithClass); } - function substituteExpressionIdentifier(node: Identifier): LeftHandSideExpression { + function substituteExpressionWithFallback(node: Expression): Expression { + let substitute = substituteExpression(node) || node; + return savedExpressionSubstution ? savedExpressionSubstution(substitute) : substitute; + } + + function substituteExpression(node: Expression): Expression { + if (isIdentifier(node)) { + return substituteExpressionIdentifier(node); + } + return node; + } + + function substituteExpressionIdentifier(node: Identifier): Expression { let container = resolver.getReferencedExportContainer(node); if (isModuleDeclaration(container)) { return createPropertyAccessExpression2(getGeneratedNameForNode(container), node, node); } - - return savedSubstituteExpressionIdentifier ? savedSubstituteExpressionIdentifier(node) : node; + return node; } function visitImportEqualsDeclaration(node: ImportEqualsDeclaration, write: (node: Statement) => void): void { @@ -1646,7 +1661,7 @@ namespace ts { function createSynthesizedSuperCall() { let callExpr = createCallExpression2(createSuperKeyword(), [createSpreadElementExpression(createIdentifier("args"))]); - return startOnNewLine(createExpressionStatement(callExpr, /*location*/ undefined, NodeFlags.GeneratedSuper)); + return startOnNewLine(createExpressionStatement(callExpr, /*location*/ undefined, NodeFlags.Generated)); } } } \ No newline at end of file diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 04677470a7d..163910ac199 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -19,9 +19,12 @@ "binder.ts", "checker.ts", "transform.ts", - "transforms/module.ts", - "transforms/jsx.ts", + "transforms/destructuring.ts", "transforms/ts.ts", + "transforms/module/module.ts", + "transforms/module/system.ts", + "transforms/module/es6.ts", + "transforms/jsx.ts", "transforms/es6.ts", "emitter.ts", "program.ts", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d35335f0f8f..4d841b056b7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -383,9 +383,7 @@ namespace ts { Namespace = 0x00020000, // Namespace declaration ExportContext = 0x00040000, // Export context (initialized by binding) ContainsThis = 0x00080000, // Interface contains references to "this" - GeneratedRest = 0x00100000, // Synthetic node generated during transformation - GeneratedSuper = 0x00200000, // Synthetic node generated during transformation - GeneratedNamespace =0x00400000, // Synthetic node generated during transformation + Generated = 0x00100000, // Synthetic node generated during transformation Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async, AccessibilityModifier = Public | Private | Protected, @@ -433,14 +431,6 @@ namespace ts { HasAggregatedChildData = 1 << 7 } - /* @internal */ - export interface TransformResolver { - getGeneratedNameForNode(node: Node): string; - makeTempVariableName(loopVariable: boolean): string; - makeUniqueName(baseName: string): string; - getEmitResolver(): EmitResolver; - } - export const enum JsxFlags { None = 0, IntrinsicNamedElement = 1 << 0, @@ -702,6 +692,8 @@ namespace ts { // @factoryhidden("decorators", true) // @factoryhidden("modifiers", true) // @factoryhidden("questionToken", true) + // @factoryhidden("equalsToken", true) + // @factoryhidden("objectAssignmentInitializer", true) export interface ShorthandPropertyAssignment extends ObjectLiteralElement { name: Identifier; questionToken?: Node; @@ -2218,17 +2210,18 @@ namespace ts { EmitParam = 0x00000020, // Emit __param helper for decorators EmitAwaiter = 0x00000040, // Emit __awaiter EmitGenerator = 0x00000080, // Emit __generator - EmitExportStar = 0x00000100, // Emit __export - SuperInstance = 0x00000200, // Instance 'super' reference - SuperStatic = 0x00000400, // Static 'super' reference - ContextChecked = 0x00000800, // Contextual types have been assigned - LexicalArguments = 0x00001000, - CaptureArguments = 0x00002000, // Lexical 'arguments' used in body (for async functions) + SuperInstance = 0x00000100, // Instance 'super' reference + SuperStatic = 0x00000200, // Static 'super' reference + ContextChecked = 0x00000400, // Contextual types have been assigned + LexicalArguments = 0x00000800, + CaptureArguments = 0x00001000, // Lexical 'arguments' used in body (for async functions) // Values for enum members have been computed, and any errors have been reported for them. - EnumValuesComputed = 0x00004000, - BlockScopedBindingInLoop = 0x00008000, - LexicalModuleMergesWithClass= 0x00010000, // Instantiated lexical module declaration is merged with a previous class declaration. + EnumValuesComputed = 0x00002000, + BlockScopedBindingInLoop = 0x00004000, + LexicalModuleMergesWithClass= 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration. + + EmitHelpersMask = EmitExtends | EmitDecorate | EmitParam | EmitAwaiter | EmitGenerator, } /* @internal */ @@ -3002,11 +2995,13 @@ namespace ts { write(node: T): void; } + // @internal + export type Transformation = (node: SourceFile) => SourceFile; + // @internal export interface TransformationSubstitutions { - assignmentSubstitution: (node: BinaryExpression) => Expression; - expressionIdentifierSubstitution: (node: Identifier) => LeftHandSideExpression; - bindingIdentifierSubstitution: (node: Identifier) => Identifier; + getGeneratedNodeFlags(node: Node): GeneratedNodeFlags; + expressionSubstitution: (node: Expression) => Expression; } // @internal @@ -3015,60 +3010,372 @@ namespace ts { substitutions?: TransformationSubstitutions; } + // @internal + export const enum GeneratedNodeFlags { + EmitHelpers = 1 << 0, + EmitExportStar = 1 << 1, + UMDDefine = 1 << 2, + NoLexicalEnvironment = 1 << 3, + } + // @internal export interface Transformer { getEmitResolver(): EmitResolver; getCompilerOptions(): CompilerOptions; + + /** + * Generates a name unique to the source file given the provided base. + * @param baseName The base name. + */ makeUniqueName(baseName: string): string; + + /** + * Generates and caches a name unique to the node within the current lexical environment. + * @param The node for which to generate a name. + */ getGeneratedNameForNode(node: Node): Identifier; + + /** + * Gets a value indicating whether the provided node has a generated name. + * @param node The node to test. + */ nodeHasGeneratedName(node: Node): boolean; + + /** + * Generates an Identifier unique to the source file given the provided base. + * @param baseName The base name. + */ createUniqueIdentifier(baseName: string): Identifier; + + /** + * Generates an Identifier unique within the current lexical environment. + * @param flags A value that indicates flags used for temporary variable generation. + */ createTempVariable(flags?: TempFlags): Identifier; + + /** + * Creates a temporary variable using an optional base name and records it + * within the current lexical environment. + * + * This is the equivalent of: + * hoistVariableDeclaration(baseName ? createUniqueIdentifier(baseName) : createTempVariable()); + * + * @param baseName An optional base name for the temporary variable. + */ declareLocal(baseName?: string): Identifier; + + /** + * Hoists the name of a variable declaration within a lexical environment. + * + * All hoisted variables are written during a call to endLexicalEnvironment. + * + * @param name The name of the declaration to hoist. + */ hoistVariableDeclaration(name: Identifier): void; + + /** + * Hoists a function declaration within the current lexical environment. + * + * All hoisted function declarations are written during a call to endLexicalEnvironment. + * + * @param func The function to hoist. + */ hoistFunctionDeclaration(func: FunctionDeclaration): void; - createParentNavigator(): ParentNavigator; - getRootNode(): SourceFile; - getParentNode(): Node; - getCurrentNode(): Node; - tryPushNode(node: Node): boolean; - pushNode(node: Node): void; - popNode(): void; - findAncestorNode(match: (node: Node) => node is T): T; - findAncestorNode(match: (node: Node) => boolean): Node; + + createParentNavigator(): ParentNavigator; // from NodeStack + getRootNode(): SourceFile; // from NodeStack + getParentNode(): Node; // from NodeStack + getCurrentNode(): Node; // from NodeStack + tryPushNode(node: Node): boolean; // from NodeStack + pushNode(node: Node): void; // from NodeStack + popNode(): void; // from NodeStack + setNode(node: Node): void; // from NodeStack + findAncestorNode(match: (node: Node) => node is T): T; // from NodeStack + findAncestorNode(match: (node: Node) => boolean): Node; // from NodeStack + + // @deprecated getDeclarationName(node: DeclarationStatement): Identifier; + + // @deprecated getDeclarationName(node: ClassLikeDeclaration): Identifier; + + // @deprecated getDeclarationName(node: Declaration): DeclarationName; + + // @deprecated getClassMemberPrefix(node: ClassLikeDeclaration, member: ClassElement): LeftHandSideExpression; - emitEmitHelpers(write: (node: Statement) => void): void; - emitExportStarHelper(write: (node: Statement) => void): void; + setGeneratedNodeFlags(node: Node, flags: GeneratedNodeFlags): void; + getGeneratedNodeFlags(node: Node): GeneratedNodeFlags; - getAssignmentSubstitution(): (node: BinaryExpression) => Expression; - setAssignmentSubstitution(substitution: (node: BinaryExpression) => Expression): void; + /** + * Rather than recurse deeply into a syntax tree to rewrite identifiers for exports + * or block-scoped declarations, you can establish a general substitution function to use + * for identifiers used in an expression position that will be invoked by the emitter + * when writing the final output. + * + * It is important to establish substitutions when a transformation phase is initialized, + * prior to the application of a transformation to any SourceFile. + * + * It is also important to capture any existing substituion to use as a fallback if the + * current transformation does not have work to do for an expression. + * + * @param substitution The callback used to substitute an identifier during emit. + */ + setExpressionSubstitution(substitution: (node: Expression) => Expression): void; - getExpressionIdentifierSubstitution(): (node: Identifier) => LeftHandSideExpression; - setExpressionIdentifierSubstitution(substitution: (node: Identifier) => LeftHandSideExpression): void; - - getBindingIdentifierSubstitution(): (node: Identifier) => Identifier; - setBindingIdentifierSubstitution(substitution: (node: Identifier) => Identifier): void; + /** + * Gets any existing substitution for identifiers in an expression position. + */ + getExpressionSubstitution(): (node: Expression) => Expression; + /** + * Starts a new lexical environment, used to capture temporary and hoisted variables + * and hoisted function declarations. + * + * This function is the primary means to signify that the current transformation + * starts a new varible declaration scope. + * + * Lexical environments are implicitly started and ended by calls to the visitModuleBody, + * visitFunctionBody, visitConciseBody, and visitSourceFile functions. + */ startLexicalEnvironment(): void; + + /** + * Ends an existing lexical environment. Any temporary or hoisted variables or + * hoisted function declarations are written to the output. + * + * this function is the primary means to signify that the current transformation + * ends a variable declaration scope. + * + * Lexical environments are implicitly started and ended by calls to the visitModuleBody, + * visitFunctionBody, visitConciseBody, and visitSourceFile functions. + * + * @param out Either an array or a callback two which any declarations are written. + */ endLexicalEnvironment(out: ((node: Statement) => void) | Statement[]): void; - pipeNode(node: TIn, through: (node: TIn, write: (node: TOut) => void) => void, out: ((node: TOut) => void) | TOut[]): void; - pipeNodes(node: TIn[], through: (node: TIn, write: (node: TOut) => void) => void, out: ((node: TOut) => void) | TOut[], start?: number, count?: number): void; - mapNode(node: TIn, through: (node: TIn, write: (node: TOut) => void) => void): TOut; - mapNodes(nodes: TIn[], through: (node: TIn, write: (node: TOut) => void) => void, start?: number, count?: number): TOut[]; - flattenNode(node: TIn, through: (node: TIn, write: (node: TOut) => void) => void): TOut[]; + /** + * Visits a syntax node using a general-purpose visitor callback. + * + * When visiting a single node, it is an error to write more than one output node. + * + * This function also ensures that the node is pushed onto the node stack before calling the + * visitor, and popped off of the node stack once the visitor has returned. + * + * The cardinality of this function is expected to be 1:1 (one-to-one). + * + * The node test is used to enforce that the output node matches the expected kind of the + * result. + * + * @param node The Node to visit. + * @param visitor A callback executed to write the results of visiting the node or its children. + * @param test A node test used to validate the result of visiting the node. + */ visitNode(node: T, visitor: (node: Node, write: (node: Node) => void) => void, test: (node: Node) => node is T): T; + + /** + * Visits an array of syntax nodes using a general-purpose visitor callback. + * + * When visiting an array of nodes, it is acceptable to write more or less nodes to the output. + * + * If the same values are written in the same order as the original node array, the original + * node array is returned. If more or fewer values are written, or if any value differs from the source in order or reference + * equality, a new node array will be returned. This is to provide reference equality for the various update functions + * used by the accept function below. + * + * This function also ensures that each node is pushed onto the node stack before calling the + * visitor, and popped off of the node stack once the visitor has returned. + * + * The cardinality of this function is expected to be *:* (many-to-many). + * + * The node test is used to enforce that each output node matches the expected kind of the + * result. + * + * @param nodes The array of Nodes to visit. + * @param visitor A callback executed to write the results of visiting each node or its children. + * @param test A node test used to validate the result of visiting each node. + * @param start An offset into nodes at which to start visiting. + * @param count A the number of nodes to visit. + */ visitNodes(nodes: T[], visitor: (node: Node, write: (node: Node) => void) => void, test: (node: Node) => node is T, start?: number, count?: number): NodeArray; + + /** + * A specialized variant of visitNode that permits writing multiple @{link Statement} nodes to + * the output, which are then enclosed enclosed in a Block. + * + * The cardinality of this function is 1:* (one-to-many). + * + * @param node The Statement to visit. + * @param visitor A callback executed to write the results of visiting the node or its children. + */ visitStatement(node: Statement, visitor: (node: Node, write: (node: Node) => void) => void): Statement; + + /** + * A specialized variant of visitNode that introduces a new lexical environment while visiting + * the body of a module, emitting any newly introduced temporary variables at the end of the body. + * + * The cardinality of this function is 1:1 (one-to-one). + * + * @param node The ModuleBody to visit. + * @param visitor A callback executed to write the results of visiting the node or its children. + */ visitModuleBody(node: ModuleBody, visitor: (node: Node, write: (node: Node) => void) => void): ModuleBody; + + /** + * A specialized variant of visitNode that introduces a new lexical environment while visiting + * the body of a function, emitting any newly introduced temporary variables at the end of the body. + * + * The cardinality of this function is 1:1 (one-to-one). + * + * @param node The FunctionBody to visit. + * @param visitor A callback executed to write the results of visiting the node or its children. + */ visitFunctionBody(node: FunctionBody, visitor: (node: Node, write: (node: Node) => void) => void): FunctionBody; + + /** + * A specialized variant of visitNode that introduces a new lexical environment while visiting + * the body of an arrow function. + * + * If the body is a Block, any newly introduced temporary variables will be added to the at the end of the body. + * + * If the body is an Expression and there are newly introduced temporary variables, the body will be converted + * into a Block with a ReturnStatement followed by the temporary declarations. + * + * The cardinality of this function is 1:1 (one-to-one). + * + * @param node The ConciseBody to visit. + * @param visitor A callback executed to write the results of visiting the node or its children. + */ visitConciseBody(node: ConciseBody, visitor: (node: Node, write: (node: Node) => void) => void): ConciseBody; + + /** + * A specialized variant of visitNode that introduces a new lexical environment while visiting + * the body of a SourceFile, emitting any newly introduced temporary variables at the end of the file. + * + * The cardinality of this function is 1:1 (one-to-one). + * + * @param node The SourceFile to visit. + * @param visitor A callback executed to write the results of visiting the node or its children. + */ visitSourceFile(node: SourceFile, visitor: (node: Node, write: (node: Node) => void) => void): SourceFile; + + /** + * For a given Node, visits each child of the node. If the results of visiting each child result in the + * exact same references as their inputs, the original value of the node parameter is returned. If any + * result differs from the input, a new Node of the same kind is returned with its children set to + * new values. This is to preserve the immutability of a syntax tree. + * + * This function also ensures that the node is pushed onto the node stack before calling the + * visitor, and popped off of the node stack once the visitor has returned. + * + * The cardinality of this function is 1:1 (one-to-one). + * + * @param node The Node to visit. + * @param visitor A callback executed to write the results of visiting the children of the node. + */ accept(node: T, visitor: (node: Node, write: (node: Node) => void) => void): T; + + /** + * Pipes a single node through a callback used to write one or more nodes to the output. + * + * The output can be an array or a callback function used to write the output. Providing a + * callback as the output is the primary means to descend into a syntax tree while still + * writing output nodes to an outer visitor. + * + * This function also ensures that the node is pushed onto the node stack before calling the + * visitor, and popped off of the node stack once the visitor has returned. + * + * The cardinality of this function is 1:* (one-to-many). + * + * @param node The node to visit. + * @param through A special-purpose callback used to map a node of one kind to one or more nodes of another kind. + * @param out Either an array of nodes, or a callback used to write each node. + */ + pipeNode(node: TIn, through: (node: TIn, write: (node: TOut) => void) => void, out: ((node: TOut) => void) | TOut[]): void; + + /** + * Pipes a single node through a callback used to write one or more nodes to the output. + * + * The output can be an array or a callback function used to write the output. Providing a + * callback as the output is the primary means to descend into a syntax tree while still + * writing output nodes to an outer visitor. + * + * This function also ensures that the node is pushed onto the node stack before calling the + * visitor, and popped off of the node stack once the visitor has returned. + * + * The cardinality of this function is *:* (many-to-many). + * + * @param nodes The nodes to visit. + * @param through A special-purpose callback used to map a node of one kind to one or more nodes of another kind. + * @param out Either an array of nodes, or a callback used to write each node. + * @param start An offset into nodes at which to start visiting. + * @param count A the number of nodes to visit. + */ + pipeNodes(nodes: TIn[], through: (node: TIn, write: (node: TOut) => void) => void, out: ((node: TOut) => void) | TOut[], start?: number, count?: number): void; + + /** + * A specialized variant of visitNode that uses a special-purposed callback function. + * + * When visiting a single node, it is an error to write more than one output node. + * + * While essentially the same as visitNode, this function enforces type safety through generics + * rather than a node test function, as the provided callback is typed to ensure input and output + * types are consistent. The callback is not intended to be a general-purpose routing/matching visitor. + * + * This function also ensures that the node is pushed onto the node stack before calling the + * visitor, and popped off of the node stack once the visitor has returned. + * + * The cardinality of this function is 1:1 (one-to-one). + * + * The return value is the result written via the callback supplied to the "through" parameter. + * + * @param node The node to visit. + * @param through A special-purpose callback used to map a node of one kind to a node of another kind. + */ + mapNode(node: TIn, through: (node: TIn, write: (node: TOut) => void) => void): TOut; + + /** + * A specialized variant of visitNodes that uses a special-purposed callback function. + * + * While similar to visitNodes, this function always returns a new array in its output. + * Also, this function enforces type safety through generics rather than a node test + * function, as the provided callback is typed to ensure input and output types are + * consistent. The callback is not intended to be a general-purpose routing/matching visitor. + * + * This function also ensures that the node is pushed onto the node stack before calling the + * visitor, and popped off of the node stack once the visitor has returned. + * + * The cardinality of this function is 1:1 (one-to-one). + * + * @param nodes The nodes to visit. + * @param through A special-purpose callback used to map a node of one kind to one or more nodes of another kind. + * @param start An offset into nodes at which to start visiting. + * @param count A the number of nodes to visit. + */ + mapNodes(nodes: TIn[], through: (node: TIn, write: (node: TOut) => void) => void, start?: number, count?: number): TOut[]; + + /** + * A specialized version of visitNode/visitNodes that uses a special-purposed callback function. + * + * While similar to visitNode, it is acceptable to write multiple nodes to the output. + * + * Also, this function enforces type safety through generics rather than a node test + * function, as the provided callback is typed to ensure input and output types are + * consistent. The callback is not intended to be a general-purpose routing/matching visitor. + * + * This function also ensures that the node is pushed onto the node stack before calling the + * visitor, and popped off of the node stack once the visitor has returned. + * + * The cardinality of this function is 1:* (one-to-many). + * + * @param node The node to visit. + * @param through A special-purpose callback used to map a node of one kind to one or more nodes of another kind. + */ + flattenNode(node: TIn, through: (node: TIn, write: (node: TOut) => void) => void): TOut[]; + + createNodes(callback: (write: (node: TOut) => void) => void, out: ((node: TOut) => void) | TOut[]): void; + createNodes(callback: (write: (node: TOut) => void) => void): NodeArray; } export interface TextSpan { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fd2eefa6c3a..f87106d3e93 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -406,7 +406,7 @@ namespace ts { return !!(getCombinedNodeFlags(navigable) & NodeFlags.Let); } - export function isPrologueDirective(node: Statement): boolean { + export function isPrologueDirective(node: Node): boolean { return node.kind === SyntaxKind.ExpressionStatement && (node).expression.kind === SyntaxKind.StringLiteral; } @@ -419,6 +419,11 @@ namespace ts { return node; } + export function getOriginalNodeId(node: Node) { + node = getOriginalNode(node); + return node ? getNodeId(node) : 0; + } + export function getOriginalNodeIf(node: T, nodeTest: (node: Node) => node is T): T { while (node && node.original) { let original = node.original; @@ -2764,4 +2769,35 @@ namespace ts { return arrayIsEqualTo(array1.sort(), array2.sort()); } + + export function getLanguageVersion(compilerOptions: CompilerOptions) { + return compilerOptions.target || ScriptTarget.ES3; + } + + export function getModuleKind(compilerOptions: CompilerOptions) { + if (compilerOptions.module) { + return compilerOptions.module; + } + + switch (getLanguageVersion(compilerOptions)) { + case ScriptTarget.ES6: return ModuleKind.ES6; + case ScriptTarget.ES2015: return ModuleKind.ES2015; + } + return ModuleKind.None; + } + + export function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + if (node.kind === SyntaxKind.ImportEqualsDeclaration) { + return node; + } + + let importClause = (node).importClause; + if (importClause && importClause.namedBindings && importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + return importClause.namedBindings; + } + } + + export function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + return isImportDeclaration(node) && node.importClause && !!node.importClause.name; + } }