From 357171fb7cd194b37908d0db8eb078c9360e09ab Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 16 Feb 2016 16:12:00 -0800 Subject: [PATCH 1/9] PR feedback, switched to getEmitScriptTarget/getEmitModuleKind --- src/compiler/comments.ts | 6 +++--- src/compiler/printer.ts | 4 ++-- src/compiler/utilities.ts | 16 ---------------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index 2b51e937fad..b93b144bf01 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -140,12 +140,12 @@ namespace ts { function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) { emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments); - // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + // Leading comments are emitted as `/*leading comment1 */space/*leading comment*/space` emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); } function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) { - // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ + // Trailing comments are emitted as `space/*trailing comment1 */space/*trailing comment*/` emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); } @@ -182,7 +182,7 @@ namespace ts { // get the leading comments from detachedPos const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos; const leadingComments = getLeadingCommentRanges(currentText, pos); - if (detachedCommentsInfo.length - 1) { + if (detachedCommentsInfo.length > 1) { detachedCommentsInfo.pop(); } else { diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 0a707e335d1..84388d4fd88 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -76,8 +76,8 @@ function __export(m) { })`; const compilerOptions = host.getCompilerOptions(); - const languageVersion = getLanguageVersion(compilerOptions); - const moduleKind = getModuleKind(compilerOptions); + const languageVersion = getEmitScriptTarget(compilerOptions); + const moduleKind = getEmitModuleKind(compilerOptions); const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined; const emitterDiagnostics = createDiagnosticCollection(); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d8025b8d1b9..7026cafb065 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -102,22 +102,6 @@ namespace ts { return true; } - export function getLanguageVersion(compilerOptions: CompilerOptions) { - return compilerOptions.target || ScriptTarget.ES3; - } - - export function getModuleKind(compilerOptions: CompilerOptions) { - if (compilerOptions.module) { - return compilerOptions.module; - } - - if (getLanguageVersion(compilerOptions) === ScriptTarget.ES6) { - return ModuleKind.ES6; - } - - return ModuleKind.None; - } - export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText); } From ad64877dee83fd63a7b63a2789f8ca1d95717571 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 15:15:09 -0800 Subject: [PATCH 2/9] Merged some changes from other branches. --- src/compiler/core.ts | 104 +++++++++++++++++++++++++++++--------- src/compiler/emitter.ts | 2 +- src/compiler/factory.ts | 27 +++++++--- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 6 --- src/compiler/visitor.ts | 76 +++++++++++++++++++--------- 6 files changed, 155 insertions(+), 61 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index c7f05f4cf50..3d82c0efe1a 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -130,11 +130,12 @@ namespace ts { return -1; } - export function countWhere(array: T[], predicate: (x: T) => boolean): number { + export function countWhere(array: T[], predicate: (x: T, i: number) => boolean): number { let count = 0; if (array) { - for (const v of array) { - if (predicate(v)) { + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (predicate(v, i)) { count++; } } @@ -142,25 +143,49 @@ namespace ts { return count; } - export function filter(array: T[], f: (x: T) => boolean): T[] { + export function filter(array: T[], f: (x: T, i: number) => x is U): U[]; + export function filter(array: T[], f: (x: T, i: number) => boolean): T[]; + export function filter(array: T[], f: (x: T, i: number) => boolean): T[] { let result: T[]; if (array) { result = []; - for (const item of array) { - if (f(item)) { - result.push(item); + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (f(v, i)) { + result.push(v); } } } return result; } - export function map(array: T[], f: (x: T) => U): U[] { + export function map(array: T[], f: (x: T, i: number) => U): U[] { let result: U[]; if (array) { result = []; - for (const v of array) { - result.push(f(v)); + for (let i = 0; i < array.length; i++) { + const v = array[i]; + result.push(f(v, i)); + } + } + return result; + } + + /** + * Maps an array. If the mapped value is an array, it is spread into the result. + */ + export function flatMap(array: T[], f: (x: T, i: number) => U | U[]): U[] { + let result: U[]; + if (array) { + result = []; + for (let i = 0; i < array.length; i++) { + const v = array[i]; + const ar = f(v, i); + if (ar) { + // We cast to here to leverage the behavior of Array#concat + // which will append a single value here. + result = result.concat(ar); + } } } return result; @@ -170,7 +195,7 @@ namespace ts { if (!array2 || !array2.length) return array1; if (!array1 || !array1.length) return array2; - return array1.concat(array2); + return [...array1, ...array2]; } export function deduplicate(array: T[]): T[] { @@ -186,6 +211,27 @@ namespace ts { return result; } + /** + * Compacts an array, removing any falsey elements. + */ + export function compact(array: T[]): T[] { + let result: T[]; + if (array) { + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (result || !v) { + if (!result) { + result = array.slice(0, i); + } + if (v) { + result.push(v); + } + } + } + } + return result || array; + } + export function sum(array: any[], prop: string): number { let result = 0; for (const v of array) { @@ -212,15 +258,25 @@ namespace ts { return true; } + export function firstOrUndefined(array: T[]): T { + return array && array.length > 0 + ? array[0] + : undefined; + } + + export function singleOrUndefined(array: T[]): T { + return array && array.length === 1 + ? array[0] + : undefined; + } + /** * Returns the last element of an array if non-empty, undefined otherwise. */ export function lastOrUndefined(array: T[]): T { - if (array.length === 0) { - return undefined; - } - - return array[array.length - 1]; + return array && array.length > 0 + ? array[array.length - 1] + : undefined; } /** @@ -252,9 +308,9 @@ namespace ts { return ~low; } - export function reduceLeft(array: T[], f: (memo: U, value: T) => U, initial: U): U; - export function reduceLeft(array: T[], f: (memo: T, value: T) => T): T; - export function reduceLeft(array: T[], f: (memo: T, value: T) => T, initial?: T): T { + export function reduceLeft(array: T[], f: (memo: U, value: T, i: number) => U, initial: U): U; + export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T): T; + export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T): T { if (array) { const count = array.length; if (count > 0) { @@ -268,7 +324,7 @@ namespace ts { result = initial; } while (pos < count) { - result = f(result, array[pos]); + result = f(result, array[pos], pos); pos++; } return result; @@ -277,9 +333,9 @@ namespace ts { return initial; } - export function reduceRight(array: T[], f: (memo: U, value: T) => U, initial: U): U; - export function reduceRight(array: T[], f: (memo: T, value: T) => T): T; - export function reduceRight(array: T[], f: (memo: T, value: T) => T, initial?: T): T { + export function reduceRight(array: T[], f: (memo: U, value: T, i: number) => U, initial: U): U; + export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T): T; + export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T): T { if (array) { let pos = array.length - 1; if (pos >= 0) { @@ -292,7 +348,7 @@ namespace ts { result = initial; } while (pos >= 0) { - result = f(result, array[pos]); + result = f(result, array[pos], pos); pos--; } return result; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f0e32bdf96c..37a278aee7a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2728,7 +2728,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } function synthesizedNodeStartsOnNewLine(node: Node) { - return nodeIsSynthesized(node) && (node).startsOnNewLine; + return nodeIsSynthesized(node) && node.startsOnNewLine; } function emitConditionalExpression(node: ConditionalExpression) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index c41182d4e2b..f39076da247 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -22,8 +22,8 @@ namespace ts { return node; } - export function createNodeArray(elements?: T[], location?: TextRange): NodeArray { - if (elements !== undefined) { + export function createNodeArray(elements?: T[], location?: TextRange, hasTrailingComma?: boolean): NodeArray { + if (elements) { if (isNodeArray(elements)) { return elements; } @@ -33,7 +33,7 @@ namespace ts { } const array = >elements; - if (location !== undefined) { + if (location) { array.pos = location.pos; array.end = location.end; } @@ -42,13 +42,17 @@ namespace ts { array.end = -1; } + if (hasTrailingComma) { + array.hasTrailingComma = true; + } + array.arrayKind = ArrayKind.NodeArray; return array; } export function createModifiersArray(elements?: Modifier[], location?: TextRange): ModifiersArray { let flags: NodeFlags; - if (elements !== undefined) { + if (elements) { if (isModifiersArray(elements)) { return elements; } @@ -64,7 +68,7 @@ namespace ts { } const array = elements; - if (location !== undefined) { + if (location) { array.pos = location.pos; array.end = location.end; } @@ -79,7 +83,7 @@ namespace ts { } export function setModifiers(node: T, modifiers: Modifier[]) { - if (modifiers !== undefined) { + if (modifiers) { const array = createModifiersArray(modifiers); node.modifiers = array; node.flags |= array.flags; @@ -92,7 +96,7 @@ namespace ts { } export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node { - const node = createNode(kind, /*location*/ undefined); + const node = createNode(kind, /*location*/ undefined); node.startsOnNewLine = startsOnNewLine; return node; } @@ -145,6 +149,15 @@ namespace ts { return clone; } + /** + * Creates a shallow, memberwise clone of a node for mutation. + * + * @param node The node to clone. + */ + export function getMutableNode(node: T): T { + return cloneNode(node, node, node.flags, node.parent, node); + } + export function createNodeArrayNode(elements: T[]): NodeArrayNode { const node = >createSynthesizedNode(SyntaxKind.NodeArrayNode); node.nodes = createNodeArray(elements); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6e4f1b35d6c..95656cea08d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -447,6 +447,7 @@ namespace ts { /* @internal */ id?: number; // Unique id (used to look up NodeLinks) parent?: Node; // Parent node (initialized by binding) /* @internal */ original?: Node; // The original node if this is an updated node. + /* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms). /* @internal */ jsDocComment?: JSDocComment; // JSDoc for the node, if it has any. Only for .js files. /* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding) /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1cef5e8835a..b98e43f1a31 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -8,12 +8,6 @@ namespace ts { isNoDefaultLib?: boolean; } - export interface SynthesizedNode extends Node { - leadingCommentRanges?: CommentRange[]; - trailingCommentRanges?: CommentRange[]; - startsOnNewLine: boolean; - } - export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration { const declarations = symbol.declarations; if (declarations) { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 0c12eaa9be1..6031e398846 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -2,7 +2,7 @@ /* @internal */ namespace ts { - export type OneOrMore = T | NodeArrayNode; + export type OneOrMany = T | NodeArrayNode; /** * Describes an edge of a Node, used when traversing a syntax tree. @@ -528,21 +528,21 @@ namespace ts { // Visit each original node. for (let i = 0; i < count; i++) { const node = nodes[i + start]; - const visited = node && >visitor(node); + const visited = node && >visitor(node); if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { // Ensure we have a copy of `nodes`, up to the current index. updated = nodes.slice(0, i); } - addNode(updated, visited, test); + addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test); } } if (updated !== undefined) { return (isModifiersArray(nodes) ? createModifiersArray(updated, nodes) - : createNodeArray(updated, nodes)); + : createNodeArray(updated, nodes, nodes.hasTrailingComma)); } return nodes; @@ -571,21 +571,28 @@ namespace ts { const edgeTraversalPath = nodeEdgeTraversalMap[node.kind]; if (edgeTraversalPath) { + let modifiers: NodeFlags; for (const edge of edgeTraversalPath) { const value = >node[edge.name]; if (value !== undefined) { const visited = visitEdge(edge, value, visitor); + if (visited && isArray(visited) && isModifiersArray(visited)) { + modifiers = visited.flags; + } + if (updated !== undefined || visited !== value) { if (updated === undefined) { - updated = cloneNode(node, /*location*/ node, node.flags & ~NodeFlags.Modifier, /*parent*/ undefined, /*original*/ node); + updated = getMutableNode(node); + updated.flags &= ~NodeFlags.Modifier; } - if (visited && isArray(visited) && isModifiersArray(visited)) { - updated[edge.name] = visited; - updated.flags |= visited.flags; + if (modifiers) { + updated.flags |= modifiers; + modifiers = undefined; } - else { - updated[edge.name] = visited; + + if (visited !== value) { + setEdgeValue(updated, edge, visited); } } } @@ -603,9 +610,20 @@ namespace ts { } } + if (updated !== node) { + updated.original = node; + } + return updated; } + /** + * Sets the value of an edge, adjusting the value as necessary for cases such as expression precedence. + */ + function setEdgeValue(parentNode: Node & Map, edge: NodeEdge, value: Node | NodeArray) { + parentNode[edge.name] = value; + } + /** * Visits a node edge. * @@ -626,16 +644,8 @@ namespace ts { * @param from The source Node or NodeArrayNode. * @param test The node test used to validate each node. */ - export function addNode(to: T[], from: OneOrMore, test?: (node: Node) => boolean) { - if (to !== undefined && from !== undefined) { - if (isNodeArrayNode(from)) { - addNodes(to, from.nodes, test); - } - else { - Debug.assert(test === undefined || test(from), "Wrong node type after visit."); - to.push(from); - } - } + export function addNode(to: T[], from: OneOrMany, startOnNewLine?: boolean) { + addNodeWorker(to, from, startOnNewLine, /*test*/ undefined); } /** @@ -645,10 +655,30 @@ namespace ts { * @param from The source array of Node or NodeArrayNode. * @param test The node test used to validate each node. */ - export function addNodes(to: T[], from: OneOrMore[], test?: (node: Node) => boolean) { - if (to !== undefined && from !== undefined) { + export function addNodes(to: T[], from: OneOrMany[], startOnNewLine?: boolean) { + addNodesWorker(to, from, startOnNewLine, /*test*/ undefined); + } + + function addNodeWorker(to: T[], from: OneOrMany, startOnNewLine: boolean, test: (node: Node) => boolean) { + if (to && from) { + if (isNodeArrayNode(from)) { + addNodesWorker(to, from.nodes, startOnNewLine, test); + } + else { + Debug.assert(test === undefined || test(from), "Wrong node type after visit."); + if (startOnNewLine) { + from.startsOnNewLine = true; + } + + to.push(from); + } + } + } + + function addNodesWorker(to: T[], from: OneOrMany[], startOnNewLine: boolean, test: (node: Node) => boolean) { + if (to && from) { for (const node of from) { - addNode(to, node, test); + addNodeWorker(to, node, startOnNewLine, test); } } } From cd7c229324aa3dd8ea6a16a7841ab16f670b7bc1 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 15:23:30 -0800 Subject: [PATCH 3/9] Merged some changes from other branches. --- src/compiler/binder.ts | 42 +++++++++++++++++++++++++++--------------- src/compiler/types.ts | 11 ++++++----- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 9c1e0cfe4b9..6f1de3677e7 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1838,6 +1838,12 @@ namespace ts { transformFlags |= TransformFlags.AssertJsx; break; + case SyntaxKind.ExportKeyword: + // This node is both ES6 and TypeScript syntax. + transformFlags |= TransformFlags.AssertES6 | TransformFlags.TypeScript; + break; + + case SyntaxKind.DefaultKeyword: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateHead: case SyntaxKind.TemplateMiddle: @@ -1965,20 +1971,6 @@ namespace ts { break; - case SyntaxKind.ExpressionStatement: - if (nodeIsSynthesized(node)) { - const expression = (node).expression; - if (nodeIsSynthesized(expression) - && isCallExpression(expression) - && expression.expression.kind === SyntaxKind.SuperKeyword) { - // A synthesized call to `super` should be transformed to a cleaner emit - // when transpiling to ES5/3. - transformFlags |= TransformFlags.AssertES6; - } - } - - break; - case SyntaxKind.BinaryExpression: if (isDestructuringAssignment(node)) { // Destructuring assignments are ES6 syntax. @@ -2087,7 +2079,7 @@ namespace ts { case SyntaxKind.VariableDeclarationList: // If a VariableDeclarationList is `let` or `const`, then it is ES6 syntax. if (node.flags & NodeFlags.BlockScoped) { - transformFlags |= TransformFlags.AssertES6; + transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsBlockScopedBinding; } break; @@ -2100,6 +2092,26 @@ namespace ts { break; + case SyntaxKind.LabeledStatement: + // A labeled statement containing a block scoped binding *may* need to be transformed from ES6. + if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding + && isIterationStatement(this, /*lookInLabeledStatements*/ true)) { + transformFlags |= TransformFlags.AssertES6; + } + + break; + + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + // A loop containing a block scoped binding *may* need to be transformed from ES6. + if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding) { + transformFlags |= TransformFlags.AssertES6; + } + + break; + case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: // A ClassDeclarations or ClassExpression is ES6 syntax. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e3d6ecae27d..e3c44e51a20 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2760,6 +2760,7 @@ namespace ts { ContainsParameterPropertyAssignments = 1 << 13, ContainsSpreadElementExpression = 1 << 14, ContainsComputedPropertyName = 1 << 15, + ContainsBlockScopedBinding = 1 << 16, // Assertions // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. @@ -2772,12 +2773,12 @@ namespace ts { // - Bitmasks that exclude flags from propagating out of a specific context // into the subtree flags of their container. NodeExcludes = TypeScript | Jsx | ES7 | ES6, - ArrowFunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments, - FunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments, - ConstructorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsParameterPropertyAssignments, - MethodOrAccessorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis, + ArrowFunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding, + FunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding, + ConstructorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding, + MethodOrAccessorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding, ClassExcludes = ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments, - ModuleExcludes = ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis, + ModuleExcludes = ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding, TypeExcludes = ~ContainsTypeScript, ObjectLiteralExcludes = ContainsDecorators | ContainsComputedPropertyName, ArrayLiteralOrCallOrNewExcludes = ContainsSpreadElementExpression, From 11d54ba32224e57c32644f7f4e8eb094b5a79c69 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 15:52:16 -0800 Subject: [PATCH 4/9] Merged some changes from other branches. --- src/compiler/factory.ts | 1022 +++++++++++++++++++++++++++++++---- src/compiler/parser.ts | 4 + src/compiler/transformer.ts | 10 +- src/compiler/types.ts | 8 +- src/compiler/utilities.ts | 64 ++- src/compiler/visitor.ts | 76 ++- 6 files changed, 1023 insertions(+), 161 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 01d305d93e1..5b58d4b872d 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -151,8 +151,6 @@ namespace ts { /** * Creates a shallow, memberwise clone of a node for mutation. - * - * @param node The node to clone. */ export function getMutableNode(node: T): T { return cloneNode(node, node, node.flags, node.parent, node); @@ -164,42 +162,28 @@ namespace ts { return node; } - export function createReturn(expression?: Expression): ReturnStatement { - const node = createSynthesizedNode(SyntaxKind.ReturnStatement); - node.expression = expression; - return node; + // Literals + + export function createLiteral(value: string, location?: TextRange): StringLiteral; + export function createLiteral(value: number, location?: TextRange): LiteralExpression; + export function createLiteral(value: string | number | boolean, location?: TextRange): PrimaryExpression; + export function createLiteral(value: string | number | boolean, location?: TextRange): PrimaryExpression { + if (typeof value === "number") { + const node = createNode(SyntaxKind.NumericLiteral, location); + node.text = value.toString(); + return node; + } + else if (typeof value === "boolean") { + return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword, location); + } + else { + const node = createNode(SyntaxKind.StringLiteral, location); + node.text = String(value); + return node; + } } - export function createStatement(expression: Expression): ExpressionStatement { - const node = createSynthesizedNode(SyntaxKind.ExpressionStatement); - node.expression = expression; - return node; - } - - export function createVariableStatement(declarationList: VariableDeclarationList): VariableStatement { - const node = createSynthesizedNode(SyntaxKind.VariableStatement); - node.declarationList = declarationList; - return node; - } - - export function createVariableDeclarationList(declarations: VariableDeclaration[]): VariableDeclarationList { - const node = createSynthesizedNode(SyntaxKind.VariableDeclarationList); - node.declarations = createNodeArray(declarations); - return node; - } - - export function createBlock(statements: Statement[]): Block { - const block = createSynthesizedNode(SyntaxKind.Block); - block.statements = createNodeArray(statements); - return block; - } - - export function createVariableDeclaration(name: BindingPattern | Identifier, initializer?: Expression, location?: TextRange): VariableDeclaration { - const node = createNode(SyntaxKind.VariableDeclaration, location); - node.name = name; - node.initializer = initializer; - return node; - } + // Identifiers export function createIdentifier(text: string): Identifier { const node = createNode(SyntaxKind.Identifier); @@ -207,57 +191,211 @@ namespace ts { return node; } - export function createTempVariable(tempKind: TempVariableKind): Identifier { + export function createTempVariable(): Identifier { const name = createNode(SyntaxKind.Identifier); - name.tempKind = tempKind; + name.text = undefined; + name.tempKind = TempVariableKind.Auto; getNodeId(name); return name; } - export function createLiteral(value: string): StringLiteral; - export function createLiteral(value: number): LiteralExpression; - export function createLiteral(value: string | number | boolean): PrimaryExpression; - export function createLiteral(value: string | number | boolean): PrimaryExpression { - if (typeof value === "number") { - const node = createNode(SyntaxKind.NumericLiteral); - node.text = value.toString(); - return node; - } - else if (typeof value === "boolean") { - return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword); - } - else { - const node = createNode(SyntaxKind.StringLiteral); - node.text = String(value); - return node; - } + export function createLoopVariable(): Identifier { + const name = createNode(SyntaxKind.Identifier); + name.text = undefined; + name.tempKind = TempVariableKind.Loop; + getNodeId(name); + return name; } - export function createVoid(expression: UnaryExpression) { - const node = createNode(SyntaxKind.VoidExpression); + // Reserved words + + export function createSuper() { + const node = createNode(SyntaxKind.SuperKeyword); + return node; + } + + export function createThis(location?: TextRange) { + const node = createNode(SyntaxKind.ThisKeyword, location); + return node; + } + + export function createNull() { + const node = createNode(SyntaxKind.NullKeyword); + return node; + } + + // Names + + export function createComputedPropertyName(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ComputedPropertyName, location); node.expression = expression; return node; } - export function createVoidZero() { - return createVoid(createLiteral(0)); + // Type members + + export function createMethod(modifiers: Modifier[], name: string | PropertyName, parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.MethodDeclaration, location); + node.decorators = undefined; + setModifiers(node, modifiers); + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.typeParameters = undefined; + node.parameters = createNodeArray(parameters); + node.body = body; + return node; } - export function createPropertyAccess(expression: Expression, name: string | Identifier) { - const node = createNode(SyntaxKind.PropertyAccessExpression); + export function createConstructor(parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.Constructor, location); + node.decorators = undefined; + node.modifiers = undefined; + node.typeParameters = undefined; + node.parameters = createSynthesizedNodeArray(parameters); + node.type = undefined; + node.body = body; + return node; + } + + export function createGetAccessor(modifiers: Modifier[], name: string | PropertyName, body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.GetAccessor, location); + node.decorators = undefined; + setModifiers(node, modifiers); + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.typeParameters = undefined; + node.parameters = createNodeArray(); + node.body = body; + return node; + } + + export function createSetAccessor(modifiers: Modifier[], name: string | PropertyName, parameter: ParameterDeclaration, body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.SetAccessor, location); + node.decorators = undefined; + setModifiers(node, modifiers); + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.typeParameters = undefined; + node.parameters = createNodeArray([parameter]); + node.body = body; + return node; + } + + export function createParameter(name: string | Identifier | BindingPattern, initializer?: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.Parameter, location); + node.decorators = undefined; + node.modifiers = undefined; + node.dotDotDotToken = undefined; + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.questionToken = undefined; + node.type = undefined; + node.initializer = initializer; + return node; + } + + // Expression + + export function createArrayLiteral(elements?: Expression[]) { + const node = createNode(SyntaxKind.ArrayLiteralExpression); + node.elements = createNodeArray(elements); + return node; + } + + export function createObjectLiteral(properties?: ObjectLiteralElement[], location?: TextRange) { + const node = createNode(SyntaxKind.ObjectLiteralExpression, location); + node.properties = createNodeArray(properties); + return node; + } + + export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange) { + const node = createNode(SyntaxKind.PropertyAccessExpression, location); node.expression = parenthesizeForAccess(expression); node.dotToken = createSynthesizedNode(SyntaxKind.DotToken); node.name = typeof name === "string" ? createIdentifier(name) : name; return node; } - export function createElementAccess(expression: Expression, index: number | Expression) { - const node = createNode(SyntaxKind.ElementAccessExpression); + export function createElementAccess(expression: Expression, index: number | Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ElementAccessExpression, location); node.expression = parenthesizeForAccess(expression); node.argumentExpression = typeof index === "number" ? createLiteral(index) : index; return node; } + export function createCall(expression: Expression, argumentsArray: Expression[], location?: TextRange) { + const node = createNode(SyntaxKind.CallExpression, location); + node.expression = parenthesizeForAccess(expression); + node.arguments = createNodeArray(argumentsArray); + return node; + } + + export function createNew(expression: Expression, argumentsArray: Expression[], location?: TextRange) { + const node = createNode(SyntaxKind.NewExpression, location); + node.expression = parenthesizeForAccess(expression); + node.arguments = argumentsArray ? createNodeArray(argumentsArray) : undefined; + return node; + } + + export function createParen(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ParenthesizedExpression, location); + node.expression = expression; + return node; + } + + export function createFunctionExpression(asteriskToken: Node, name: string | Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.FunctionExpression, location); + node.modifiers = undefined; + node.asteriskToken = asteriskToken; + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.typeParameters = undefined; + node.parameters = createNodeArray(parameters); + node.type = undefined; + node.body = body; + return node; + } + + export function createArrowFunction(parameters: ParameterDeclaration[], body: Expression | Block, location?: TextRange) { + const node = createNode(SyntaxKind.ArrowFunction, location); + node.modifiers = undefined; + node.typeParameters = undefined; + node.parameters = createNodeArray(parameters); + node.type = undefined; + node.equalsGreaterThanToken = createNode(SyntaxKind.EqualsGreaterThanToken); + node.body = body; + return node; + } + + export function createTypeOf(expression: Expression) { + const node = createNode(SyntaxKind.TypeOfExpression); + node.expression = parenthesizePrefixOperand(expression); + return node; + } + + export function createVoid(expression: Expression) { + const node = createNode(SyntaxKind.VoidExpression); + node.expression = parenthesizePrefixOperand(expression); + return node; + } + + export function createPrefix(operator: SyntaxKind, operand: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.PrefixUnaryExpression, location); + node.operator = operator; + node.operand = parenthesizePrefixOperand(operand); + return node; + } + + export function createPostfix(operand: Expression, operator: SyntaxKind, location?: TextRange) { + const node = createNode(SyntaxKind.PostfixUnaryExpression, location); + node.operand = parenthesizePostfixOperand(operand); + node.operator = operator; + return node; + } + + export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.BinaryExpression, location); + node.left = parenthesizeBinaryOperand(operator, left, /*isLeftSideOfBinary*/ true); + node.operatorToken = createSynthesizedNode(operator); + node.right = parenthesizeBinaryOperand(operator, right, /*isLeftSideOfBinary*/ false); + return node; + } + export function createConditional(condition: Expression, whenTrue: Expression, whenFalse: Expression) { const node = createNode(SyntaxKind.ConditionalExpression); node.condition = condition; @@ -268,14 +406,223 @@ namespace ts { return node; } - export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) { - const node = createNode(SyntaxKind.BinaryExpression, location); - node.left = parenthesizeBinaryOperand(operator, left, /*isLeftSideOfBinary*/ true); - node.operatorToken = createSynthesizedNode(operator); - node.right = parenthesizeBinaryOperand(operator, right, /*isLeftSideOfBinary*/ false); + export function createYield(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.YieldExpression, location); + node.expression = expression; return node; } + export function createSpread(expression: Expression) { + const node = createNode(SyntaxKind.SpreadElementExpression); + node.expression = expression; + return node; + } + + export function createClassExpression(name: Identifier, heritageClauses: HeritageClause[], members: ClassElement[], location?: TextRange) { + const node = createNode(SyntaxKind.ClassExpression, location); + node.decorators = undefined; + node.modifiers = undefined; + node.name = name; + node.typeParameters = undefined; + node.heritageClauses = createSynthesizedNodeArray(heritageClauses); + node.members = createSynthesizedNodeArray(members); + return node; + } + + export function createExpressionWithTypeArguments(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ExpressionWithTypeArguments, location); + node.typeArguments = undefined; + node.expression = parenthesizeForAccess(expression); + return node; + } + + // Element + + export function createBlock(statements: Statement[], location?: TextRange): Block { + const block = createNode(SyntaxKind.Block, location); + block.statements = createNodeArray(statements); + return block; + } + + export function createVariableStatement(modifiers: Modifier[], declarationList: VariableDeclarationList, location?: TextRange): VariableStatement { + const node = createNode(SyntaxKind.VariableStatement, location); + node.decorators = undefined; + setModifiers(node, modifiers); + node.declarationList = declarationList; + return node; + } + + export function createVariableDeclarationList(declarations: VariableDeclaration[], location?: TextRange, flags?: NodeFlags): VariableDeclarationList { + const node = createNode(SyntaxKind.VariableDeclarationList, location, flags); + node.declarations = createNodeArray(declarations); + return node; + } + + export function createLetDeclarationList(declarations: VariableDeclaration[], location?: TextRange) { + return createVariableDeclarationList(declarations, location, NodeFlags.Let); + } + + export function createConstDeclarationList(declarations: VariableDeclaration[], location?: TextRange) { + return createVariableDeclarationList(declarations, location, NodeFlags.Const); + } + + export function createVariableDeclaration(name: string | BindingPattern | Identifier, initializer?: Expression, location?: TextRange): VariableDeclaration { + const node = createNode(SyntaxKind.VariableDeclaration, location); + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.initializer = initializer; + return node; + } + + export function createEmptyStatement(location: TextRange) { + return createNode(SyntaxKind.EmptyStatement, location); + } + + export function createStatement(expression: Expression, location?: TextRange): ExpressionStatement { + const node = createNode(SyntaxKind.ExpressionStatement, location); + node.expression = expression; + return node; + } + + export function createIf(expression: Expression, thenStatement: Statement, elseStatement?: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.IfStatement, location); + node.expression = expression; + node.thenStatement = thenStatement; + node.elseStatement = elseStatement; + return node; + } + + export function createFor(initializer: ForInitializer, condition: Expression, incrementor: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.ForStatement, location); + node.initializer = initializer; + node.condition = condition; + node.incrementor = incrementor; + node.statement = statement; + return node; + } + + export function createLabel(label: string | Identifier, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.LabeledStatement, location); + node.label = typeof label === "string" ? createIdentifier(label) : label; + node.statement = statement; + return node; + } + + export function createDo(expression: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.DoStatement, location); + node.expression = expression; + node.statement = statement; + return node; + } + + export function createWhile(statement: Statement, expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.WhileStatement, location); + node.statement = statement; + node.expression = expression; + return node; + } + + export function createForIn(initializer: ForInitializer, expression: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.ForInStatement, location); + node.initializer = initializer; + node.expression = expression; + node.statement = statement; + return node; + } + + export function createForOf(initializer: ForInitializer, expression: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.ForOfStatement, location); + node.initializer = initializer; + node.expression = expression; + node.statement = statement; + return node; + } + + export function createReturn(expression?: Expression, location?: TextRange): ReturnStatement { + const node = createNode(SyntaxKind.ReturnStatement, location); + node.expression = expression; + return node; + } + + export function createFunctionDeclaration(modifiers: Modifier[], asteriskToken: Node, name: string | Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange) { + const node = createNode(SyntaxKind.FunctionDeclaration, location); + node.decorators = undefined; + setModifiers(node, modifiers); + node.asteriskToken = asteriskToken; + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.typeParameters = undefined; + node.parameters = createNodeArray(parameters); + node.type = undefined; + node.body = body; + return node; + } + + export function createClassDeclaration(modifiers: Modifier[], name: Identifier, heritageClauses: HeritageClause[], members: ClassElement[], location?: TextRange) { + const node = createNode(SyntaxKind.ClassDeclaration, location); + node.decorators = undefined; + setModifiers(node, modifiers); + node.name = name; + node.typeParameters = undefined; + node.heritageClauses = createSynthesizedNodeArray(heritageClauses); + node.members = createSynthesizedNodeArray(members); + return node; + } + + export function createExportDefault(expression: Expression) { + const node = createNode(SyntaxKind.ExportAssignment); + node.isExportEquals = false; + node.expression = expression; + return node; + } + + export function createExportDeclaration(exportClause: NamedExports, moduleSpecifier?: Expression) { + const node = createNode(SyntaxKind.ExportDeclaration); + node.exportClause = exportClause; + node.moduleSpecifier = moduleSpecifier; + return node; + } + + export function createNamedExports(elements: ExportSpecifier[]) { + const node = createNode(SyntaxKind.NamedExports); + node.elements = createNodeArray(elements); + return node; + } + + export function createExportSpecifier(name: string | Identifier, propertyName?: string | Identifier) { + const node = createNode(SyntaxKind.ExportSpecifier); + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.propertyName = typeof propertyName === "string" ? createIdentifier(propertyName) : propertyName; + return node; + } + + // Clauses + + export function createHeritageClause(token: SyntaxKind, types: ExpressionWithTypeArguments[], location?: TextRange) { + const node = createNode(SyntaxKind.HeritageClause, location); + node.token = token; + node.types = createSynthesizedNodeArray(types); + return node; + } + + // Property assignments + + export function createPropertyAssignment(name: string | PropertyName, initializer: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.PropertyAssignment, location); + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.questionToken = undefined; + node.initializer = initializer; + return node; + } + + // Compound nodes + + export function createComma(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.CommaToken, right); + } + + export function createLessThan(left: Expression, right: Expression, location?: TextRange) { + return createBinary(left, SyntaxKind.LessThanToken, right, location); + } + export function createAssignment(left: Expression, right: Expression, location?: TextRange) { return createBinary(left, SyntaxKind.EqualsToken, right, location); } @@ -284,17 +631,72 @@ namespace ts { return createBinary(left, SyntaxKind.EqualsEqualsEqualsToken, right); } - export function createComma(left: Expression, right: Expression) { - return createBinary(left, SyntaxKind.CommaToken, right); + export function createStrictInequality(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.ExclamationEqualsEqualsToken, right); } - export function createCall(expression: Expression, argumentsArray: Expression[]) { - const node = createNode(SyntaxKind.CallExpression); - node.expression = parenthesizeForAccess(expression); - node.arguments = createNodeArray(argumentsArray); + export function createAdd(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.PlusToken, right); + } + + export function createSubtract(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.MinusToken, right); + } + + export function createPostfixIncrement(operand: Expression, location?: TextRange) { + return createPostfix(operand, SyntaxKind.PlusPlusToken, location); + } + + export function createLogicalAnd(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.AmpersandAmpersandToken, right); + } + + export function createLogicalOr(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.BarBarToken, right); + } + + export function createLogicalNot(operand: Expression) { + return createPrefix(SyntaxKind.ExclamationToken, operand); + } + + export function createVoidZero() { + return createVoid(createLiteral(0)); + } + + export function createMemberAccessForPropertyName(target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression { + return isIdentifier(memberName) + ? createPropertyAccess(target, cloneNode(memberName), location) + : createElementAccess(target, cloneNode(isComputedPropertyName(memberName) ? memberName.expression : memberName), location); + } + + export function createRestParameter(name: string | Identifier) { + const node = createParameter(name, /*initializer*/ undefined); + node.dotDotDotToken = createSynthesizedNode(SyntaxKind.DotDotDotToken); return node; } + export function createFunctionCall(func: Expression, thisArg: Expression, argumentsList: Expression[], location?: TextRange) { + return createCall( + createPropertyAccess(func, "call"), + [ + thisArg, + ...argumentsList + ], + location + ); + } + + export function createFunctionApply(func: Expression, thisArg: Expression, argumentsExpression: Expression, location?: TextRange) { + return createCall( + createPropertyAccess(func, "apply"), + [ + thisArg, + argumentsExpression + ], + location + ); + } + export function createArraySlice(array: Expression, start?: number | Expression) { const argumentsList: Expression[] = []; if (start !== undefined) { @@ -304,16 +706,353 @@ namespace ts { return createCall(createPropertyAccess(array, "slice"), argumentsList); } - export function parenthesizeExpression(expression: Expression) { - const node = createNode(SyntaxKind.ParenthesizedExpression); - node.expression = expression; - return node; + export function createArrayConcat(array: Expression, values: Expression[]) { + return createCall( + createPropertyAccess(array, "concat"), + values + ); + } + + export function createMathPow(left: Expression, right: Expression, location?: TextRange) { + return createCall( + createPropertyAccess(createIdentifier("Math"), "pow"), + [left, right], + location + ); + } + + export function createJsxSpread(reactNamespace: string, segments: Expression[]) { + return createCall( + createPropertyAccess( + createIdentifier(reactNamespace || "React"), + "__spread" + ), + segments + ); + } + + export function createJsxCreateElement(reactNamespace: string, tagName: Expression, props: Expression, children: Expression[]): LeftHandSideExpression { + const argumentsList = [tagName]; + if (props) { + argumentsList.push(props); + } + + if (children && children.length > 0) { + if (!props) { + argumentsList.push(createNull()); + } + + addRange(argumentsList, children); + } + + return createCall( + createPropertyAccess( + createIdentifier(reactNamespace || "React"), + "createElement" + ), + argumentsList + ); + } + + // Helpers + + export function createExtendsHelper(name: Identifier) { + return createCall( + createIdentifier("__extends"), + [ + name, + createIdentifier("_super") + ] + ); + } + + export function createParamHelper(expression: Expression, parameterOffset: number) { + return createCall( + createIdentifier("__param"), + [ + createLiteral(parameterOffset), + expression + ] + ); + } + + export function createMetadataHelper(metadataKey: string, metadataValue: Expression, defer?: boolean) { + return createCall( + createIdentifier("__metadata"), + [ + createLiteral(metadataKey), + defer + ? createArrowFunction([], metadataValue) + : metadataValue + ] + ); + } + + export function createDecorateHelper(decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression) { + const argumentsArray: Expression[] = []; + argumentsArray.push(createArrayLiteral(decoratorExpressions)); + argumentsArray.push(target); + if (memberName) { + argumentsArray.push(memberName); + if (descriptor) { + argumentsArray.push(descriptor); + } + } + + return createCall(createIdentifier("__decorate"), argumentsArray); + } + + export function createAwaiterHelper(hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + return createCall( + createIdentifier("__awaiter"), + [ + createThis(), + hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(), + promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(), + createFunctionExpression( + createNode(SyntaxKind.AsteriskToken), + /*name*/ undefined, + [], + body + ) + ] + ); + } + + export function createHasOwnProperty(target: LeftHandSideExpression, propertyName: Expression) { + return createCall( + createPropertyAccess(target, "hasOwnProperty"), + [propertyName] + ); + } + + function createPropertyDescriptor({ get, set, value, enumerable, configurable, writable }: PropertyDescriptorOptions, preferNewLine?: boolean, location?: TextRange) { + const properties: ObjectLiteralElement[] = []; + addPropertyAssignment(properties, "get", get, preferNewLine); + addPropertyAssignment(properties, "set", set, preferNewLine); + addPropertyAssignment(properties, "value", value, preferNewLine); + addPropertyAssignment(properties, "enumerable", enumerable, preferNewLine); + addPropertyAssignment(properties, "configurable", configurable, preferNewLine); + addPropertyAssignment(properties, "writable", writable, preferNewLine); + return createObjectLiteral(properties, location); + } + + function addPropertyAssignment(properties: ObjectLiteralElement[], name: string, value: boolean | Expression, preferNewLine: boolean) { + if (value !== undefined) { + const property = createPropertyAssignment( + name, + typeof value === "boolean" ? createLiteral(value) : value + ); + + if (preferNewLine) { + property.startsOnNewLine = true; + } + + addNode(properties, property); + } + } + + export interface PropertyDescriptorOptions { + get?: Expression; + set?: Expression; + value?: Expression; + enumerable?: boolean | Expression; + configurable?: boolean | Expression; + writable?: boolean | Expression; + } + + export function createObjectDefineProperty(target: Expression, memberName: Expression, descriptor: PropertyDescriptorOptions, preferNewLine?: boolean, location?: TextRange) { + return createCall( + createPropertyAccess( + createIdentifier("Object"), + "defineProperty" + ), + [ + target, + memberName, + createPropertyDescriptor(descriptor, preferNewLine) + ], + location + ); + } + + function createObjectCreate(prototype: Expression) { + return createCall( + createPropertyAccess(createIdentifier("Object"), "create"), + [prototype] + ); + } + + function createGeti(target: LeftHandSideExpression) { + // name => super[name] + return createArrowFunction( + [createParameter("name")], + createElementAccess( + target, + createIdentifier("name") + ) + ); + } + + function createSeti(target: LeftHandSideExpression) { + // (name, value) => super[name] = value + return createArrowFunction( + [ + createParameter("name"), + createParameter("value") + ], + createAssignment( + createElementAccess( + target, + createIdentifier("name") + ), + createIdentifier("value") + ) + ); + } + + export function createAdvancedAsyncSuperHelper() { + // const _super = (function (geti, seti) { + // const cache = Object.create(null); + // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); + // })(name => super[name], (name, value) => super[name] = value); + + // const cache = Object.create(null); + const createCache = createVariableStatement( + /*modifiers*/ undefined, + createConstDeclarationList([ + createVariableDeclaration( + "cache", + createObjectCreate(createNull()) + ) + ]) + ); + + // get value() { return geti(name); } + const getter = createGetAccessor( + /*modifiers*/ undefined, + "value", + createBlock([ + createReturn( + createCall( + createIdentifier("geti"), + [createIdentifier("name")] + ) + ) + ]) + ); + + // set value(v) { seti(name, v); } + const setter = createSetAccessor( + /*modifiers*/ undefined, + "value", + createParameter("v"), + createBlock([ + createStatement( + createCall( + createIdentifier("seti"), + [ + createIdentifier("name"), + createIdentifier("v") + ] + ) + ) + ]) + ); + + // return name => cache[name] || ... + const getOrCreateAccessorsForName = createReturn( + createArrowFunction( + [createParameter("name")], + createLogicalOr( + createElementAccess( + createIdentifier("cache"), + createIdentifier("name") + ), + createParen( + createAssignment( + createElementAccess( + createIdentifier("cache"), + createIdentifier("name") + ), + createObjectLiteral([ + getter, + setter + ]) + ) + ) + ) + ) + ); + + // const _super = (function (geti, seti) { + // const cache = Object.create(null); + // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); + // })(name => super[name], (name, value) => super[name] = value); + return createVariableStatement( + /*modifiers*/ undefined, + createConstDeclarationList([ + createVariableDeclaration( + "_super", + createCall( + createParen( + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + [ + createParameter("geti"), + createParameter("seti") + ], + createBlock([ + createCache, + getOrCreateAccessorsForName + ]) + ) + ), + [ + createGeti(createSuper()), + createSeti(createSuper()) + ] + ) + ) + ]) + ); + } + + export function createSimpleAsyncSuperHelper() { + return createVariableStatement( + /*modifiers*/ undefined, + createConstDeclarationList([ + createVariableDeclaration( + "_super", + createGeti(createSuper()) + ) + ]) + ); } export function inlineExpressions(expressions: Expression[]) { return reduceLeft(expressions, createComma); } + export function createExpressionFromEntityName(node: EntityName | Expression): Expression { + return isQualifiedName(node) + ? createPropertyAccess( + createExpressionFromEntityName(node.left), + cloneNode(node.right) + ) + : cloneNode(node); + } + + export function createExpressionForPropertyName(memberName: PropertyName, location?: TextRange): Expression { + return isIdentifier(memberName) ? createLiteral(memberName.text, location) + : isComputedPropertyName(memberName) ? cloneNode(memberName.expression, location) + : cloneNode(memberName, location); + } + + + // Utilities + /** * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended * order of operations. @@ -323,18 +1062,14 @@ namespace ts { * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the * BinaryExpression. */ - function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean) { - // When diagnosing whether the expression needs parentheses, the decision should be based - // on the innermost expression in a chain of nested type assertions. - operand = skipAssertions(operand); - + export function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean) { // If the resulting expression is already parenthesized, we do not need to do any further processing. if (operand.kind === SyntaxKind.ParenthesizedExpression) { return operand; } return binaryOperandNeedsParentheses(binaryOperator, operand, isLeftSideOfBinary) - ? parenthesizeExpression(operand) + ? createParen(operand) : operand; } @@ -441,11 +1176,7 @@ namespace ts { * * @param expr The expression node. */ - function parenthesizeForAccess(expr: Expression): LeftHandSideExpression { - // When diagnosing whether the expression needs parentheses, the decision should be based - // on the innermost expression in a chain of nested type assertions. - expr = skipAssertions(expr); - + export function parenthesizeForAccess(expression: Expression): LeftHandSideExpression { // isLeftHandSideExpression is almost the correct criterion for when it is not necessary // to parenthesize the expression before a dot. The known exceptions are: // @@ -454,25 +1185,112 @@ namespace ts { // NumericLiteral // 1.x -> not the same as (1).x // - if (isLeftHandSideExpression(expr) && - expr.kind !== SyntaxKind.NewExpression && - expr.kind !== SyntaxKind.NumericLiteral) { - return expr; + if (isLeftHandSideExpression(expression) && + expression.kind !== SyntaxKind.NewExpression && + expression.kind !== SyntaxKind.NumericLiteral) { + return expression; } - return parenthesizeExpression(expr); + return createParen(expression, /*location*/ expression); } - /** - * Skips past any TypeAssertionExpression or AsExpression nodes to their inner expression. - * - * @param node The expression node. - */ - function skipAssertions(node: Expression) { - while (node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) { - node = (node).expression; + export function parenthesizePostfixOperand(operand: Expression) { + return isLeftHandSideExpression(operand) + ? operand + : createParen(operand, /*location*/ operand); + } + + export function parenthesizePrefixOperand(operand: Expression) { + return isUnaryExpression(operand) + ? operand + : createParen(operand, /*location*/ operand); + } + + export function parenthesizeExpressionForList(expression: Expression) { + const expressionPrecedence = getExpressionPrecedence(expression); + const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken); + return expressionPrecedence > commaPrecedence + ? expression + : createParen(expression, /*location*/ expression); + } + + export function parenthesizeExpressionForExpressionStatement(expression: Expression) { + if (isCallExpression(expression)) { + const callee = expression.expression; + if (callee.kind === SyntaxKind.FunctionExpression + || callee.kind === SyntaxKind.ArrowFunction) { + const clone = cloneNode(expression, expression, expression.flags, expression.parent, expression); + clone.expression = createParen(callee, /*location*/ callee); + return clone; + } + } + else if (getLeftmostExpression(expression).kind === SyntaxKind.ObjectLiteralExpression) { + return createParen(expression, /*location*/ expression); + } + + return expression; + } + + function getLeftmostExpression(node: Expression): Expression { + while (true) { + switch (node.kind) { + case SyntaxKind.PostfixUnaryExpression: + node = (node).operand; + continue; + + case SyntaxKind.BinaryExpression: + node = (node).left; + continue; + + case SyntaxKind.ConditionalExpression: + node = (node).condition; + continue; + + case SyntaxKind.CallExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.PropertyAccessExpression: + node = (node).expression; + continue; + } + + return node; + } + } + + export function skipParentheses(node: Expression): Expression { + while (node.kind === SyntaxKind.ParenthesizedExpression + || node.kind === SyntaxKind.TypeAssertionExpression + || node.kind === SyntaxKind.AsExpression) { + node = (node).expression; } return node; } + + export function startOnNewLine(node: T): T { + node.startsOnNewLine = true; + return node; + } + + export function setOriginalNode(node: T, original: Node): T { + node.original = original; + return node; + } + + export function setTextRange(node: T, location: TextRange): T { + if (location) { + node.pos = location.pos; + node.end = location.end; + } + return node; + } + + export function setNodeFlags(node: T, flags: NodeFlags): T { + node.flags = flags; + return node; + } + + export function getSynthesizedNode(node: T): T { + return nodeIsSynthesized(node) ? node : cloneNode(node, /*location*/ undefined, node.flags, /*parent*/ undefined, /*original*/ node); + } } \ No newline at end of file diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b21f9919f45..c4813ac9932 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4069,6 +4069,10 @@ namespace ts { function parseBlock(ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block { const node = createNode(SyntaxKind.Block); if (parseExpected(SyntaxKind.OpenBraceToken, diagnosticMessage) || ignoreMissingOpenBrace) { + if (scanner.hasPrecedingLineBreak()) { + node.multiLine = true; + } + node.statements = parseList(ParsingContext.BlockStatements, parseStatement); parseExpected(SyntaxKind.CloseBraceToken); } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 106dd155c68..45b2ba0ee29 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -42,8 +42,12 @@ namespace ts { hoistFunctionDeclaration, startLexicalEnvironment, endLexicalEnvironment, + identifierSubstitution: node => node, + expressionSubstitution: node => node, enableExpressionSubstitution, isExpressionSubstitutionEnabled, + onBeforeEmitNode: node => { }, + onAfterEmitNode: node => { }, enableEmitNotification, isEmitNotificationEnabled, }; @@ -106,8 +110,9 @@ namespace ts { /** * Sets flags that control emit behavior of a node. */ - function setNodeEmitFlags(node: Node, flags: NodeEmitFlags) { + function setNodeEmitFlags(node: T, flags: NodeEmitFlags) { nodeEmitFlags[getNodeId(node)] = flags; + return node; } /** @@ -196,7 +201,7 @@ namespace ts { case SyntaxKind.ClassExpression: return generateNameForClassExpression(); default: - return createTempVariable(TempVariableKind.Auto); + return createTempVariable(); } } @@ -276,6 +281,7 @@ namespace ts { if (hoistedVariableDeclarations) { const statement = createVariableStatement( + /*modifiers*/ undefined, createVariableDeclarationList(hoistedVariableDeclarations) ); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6d7a743c560..1422b611651 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1125,6 +1125,7 @@ namespace ts { // @kind(SyntaxKind.Block) export interface Block extends Statement { statements: NodeArray; + /*@internal*/ multiLine?: boolean; } // @kind(SyntaxKind.VariableStatement) @@ -1302,7 +1303,7 @@ namespace ts { export interface EnumMember extends Declaration { // This does include ComputedPropertyName, but the parser will give an error // if it parses a ComputedPropertyName in an EnumMember - name: DeclarationName; + name: PropertyName; initializer?: Expression; } @@ -2799,8 +2800,7 @@ namespace ts { UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node, this is primarily used when printing a SystemJS module. SingleLine = 1 << 6, // The contents of this node should be emit on a single line. - MultiLine = 1 << 7, // The contents of this node should be emit on multiple lines. - AdviseOnEmitNode = 1 << 8, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. + AdviseOnEmitNode = 1 << 7, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. } /** Additional context provided to `visitEachChild` */ @@ -2817,7 +2817,7 @@ namespace ts { getCompilerOptions(): CompilerOptions; getEmitResolver(): EmitResolver; getNodeEmitFlags(node: Node): NodeEmitFlags; - setNodeEmitFlags(node: Node, flags: NodeEmitFlags): void; + setNodeEmitFlags(node: T, flags: NodeEmitFlags): T; hoistFunctionDeclaration(node: FunctionDeclaration): void; hoistVariableDeclaration(node: Identifier): void; isUniqueName(name: string): boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 19512581308..e20ba3ac4de 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -173,6 +173,10 @@ namespace ts { return node.pos; } + export function isDefined(value: any): boolean { + return value !== undefined; + } + // Returns true if this node is missing from the actual source code. A 'missing' node is different // from 'undefined/defined'. When a node is undefined (which can happen for optional nodes // in the tree), it is definitely missing. However, a node may be defined, but still be @@ -1654,7 +1658,7 @@ namespace ts { return getOperatorPrecedence(expression.kind, operator, hasArguments); } - function getOperator(expression: Expression) { + export function getOperator(expression: Expression) { if (expression.kind === SyntaxKind.BinaryExpression) { return (expression).operatorToken.kind; } @@ -2664,6 +2668,10 @@ namespace ts { // Literals + export function isNoSubstitutionTemplateLiteral(node: Node): node is LiteralExpression { + return node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; + } + export function isLiteralKind(kind: SyntaxKind): boolean { return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken; } @@ -2757,6 +2765,10 @@ namespace ts { // Type members + export function isMethodDeclaration(node: Node): node is MethodDeclaration { + return node.kind === SyntaxKind.MethodDeclaration; + } + export function isClassElement(node: Node): node is ClassElement { const kind = node.kind; return kind === SyntaxKind.Constructor @@ -2764,7 +2776,6 @@ namespace ts { || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor - || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.IndexSignature; } @@ -2830,6 +2841,10 @@ namespace ts { return node.kind === SyntaxKind.BinaryExpression; } + export function isConditionalExpression(node: Node): node is ConditionalExpression { + return node.kind === SyntaxKind.ConditionalExpression; + } + export function isCallExpression(node: Node): node is CallExpression { return node.kind === SyntaxKind.CallExpression; } @@ -2840,11 +2855,16 @@ namespace ts { || kind === SyntaxKind.NoSubstitutionTemplateLiteral; } + export function isSpreadElementExpression(node: Node): node is SpreadElementExpression { + return node.kind === SyntaxKind.SpreadElementExpression; + } + export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { return node.kind === SyntaxKind.ExpressionWithTypeArguments; } - function isLeftHandSideExpressionKind(kind: SyntaxKind) { + export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { + const kind = node.kind; return kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression || kind === SyntaxKind.NewExpression @@ -2870,11 +2890,8 @@ namespace ts { || kind === SyntaxKind.SuperKeyword; } - export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { - return isLeftHandSideExpressionKind(node.kind); - } - - function isUnaryExpressionKind(kind: SyntaxKind): boolean { + export function isUnaryExpression(node: Node): node is UnaryExpression { + const kind = node.kind; return kind === SyntaxKind.PrefixUnaryExpression || kind === SyntaxKind.PostfixUnaryExpression || kind === SyntaxKind.DeleteExpression @@ -2882,14 +2899,11 @@ namespace ts { || kind === SyntaxKind.VoidExpression || kind === SyntaxKind.AwaitExpression || kind === SyntaxKind.TypeAssertionExpression - || isLeftHandSideExpressionKind(kind); + || isLeftHandSideExpression(node); } - export function isUnaryExpression(node: Node): node is UnaryExpression { - return isUnaryExpressionKind(node.kind); - } - - function isExpressionKind(kind: SyntaxKind): boolean { + export function isExpression(node: Node): node is Expression { + const kind = node.kind; return kind === SyntaxKind.ConditionalExpression || kind === SyntaxKind.YieldExpression || kind === SyntaxKind.ArrowFunction @@ -2897,11 +2911,7 @@ namespace ts { || kind === SyntaxKind.SpreadElementExpression || kind === SyntaxKind.AsExpression || kind === SyntaxKind.OmittedExpression - || isUnaryExpressionKind(kind); - } - - export function isExpression(node: Node): node is Expression { - return isExpressionKind(node.kind); + || isUnaryExpression(node); } // Misc @@ -3053,7 +3063,8 @@ namespace ts { export function isStatement(node: Node): node is Statement { const kind = node.kind; return isStatementKindButNotDeclarationKind(kind) - || isDeclarationStatementKind(kind); + || isDeclarationStatementKind(kind) + || kind === SyntaxKind.Block; } // Module references @@ -3089,6 +3100,10 @@ namespace ts { || kind === SyntaxKind.JsxSpreadAttribute; } + export function isJsxSpreadAttribute(node: Node): node is JsxSpreadAttribute { + return node.kind === SyntaxKind.JsxSpreadAttribute; + } + // Clauses export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { @@ -3108,9 +3123,12 @@ namespace ts { // Property assignments - export function isShortHandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { - const kind = node.kind; - return kind === SyntaxKind.ShorthandPropertyAssignment; + export function isPropertyAssignment(node: Node): node is PropertyAssignment { + return node.kind === SyntaxKind.PropertyAssignment; + } + + export function isShorthandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { + return node.kind === SyntaxKind.ShorthandPropertyAssignment; } // Enum diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index f49e3ef6aaa..be5e5fd21cc 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -18,6 +18,9 @@ namespace ts { /** A callback used to lift a NodeArrayNode into a valid node. */ lift?: (nodes: NodeArray) => Node; + + /** A callback used to parenthesize a node to preserve the intended order of operations. */ + parenthesize?: (value: Node, parentNode: Node) => Node; }; /** @@ -51,7 +54,7 @@ namespace ts { { name: "modifiers", test: isModifier }, { name: "name", test: isBindingName }, { name: "type", test: isTypeNode, optional: true }, - { name: "initializer", test: isExpression, optional: true }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.Decorator]: [ { name: "expression", test: isLeftHandSideExpression }, @@ -107,34 +110,34 @@ namespace ts { [SyntaxKind.BindingElement]: [ { name: "propertyName", test: isPropertyName, optional: true }, { name: "name", test: isBindingName }, - { name: "initializer", test: isExpression, optional: true }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.ArrayLiteralExpression]: [ - { name: "elements", test: isExpression }, + { name: "elements", test: isExpression, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.ObjectLiteralExpression]: [ { name: "properties", test: isObjectLiteralElement }, ], [SyntaxKind.PropertyAccessExpression]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "name", test: isIdentifier }, ], [SyntaxKind.ElementAccessExpression]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "argumentExpression", test: isExpression }, ], [SyntaxKind.CallExpression]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "typeArguments", test: isTypeNode }, { name: "arguments", test: isExpression }, ], [SyntaxKind.NewExpression]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "typeArguments", test: isTypeNode }, { name: "arguments", test: isExpression }, ], [SyntaxKind.TaggedTemplateExpression]: [ - { name: "tag", test: isLeftHandSideExpression }, + { name: "tag", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "template", test: isTemplate }, ], [SyntaxKind.TypeAssertionExpression]: [ @@ -162,26 +165,26 @@ namespace ts { { name: "body", test: isConciseBody, lift: liftToBlock }, ], [SyntaxKind.DeleteExpression]: [ - { name: "expression", test: isUnaryExpression }, + { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.TypeOfExpression]: [ - { name: "expression", test: isUnaryExpression }, + { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.VoidExpression]: [ - { name: "expression", test: isUnaryExpression }, + { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.AwaitExpression]: [ - { name: "expression", test: isUnaryExpression }, + { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.PrefixUnaryExpression]: [ - { name: "operand", test: isUnaryExpression }, + { name: "operand", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand }, ], [SyntaxKind.PostfixUnaryExpression]: [ - { name: "operand", test: isLeftHandSideExpression }, + { name: "operand", test: isLeftHandSideExpression, parenthesize: parenthesizePostfixOperand }, ], [SyntaxKind.BinaryExpression]: [ - { name: "left", test: isExpression }, - { name: "right", test: isExpression }, + { name: "left", test: isExpression, parenthesize: (node: Expression, parent: BinaryExpression) => parenthesizeBinaryOperand(getOperator(parent), node, true) }, + { name: "right", test: isExpression, parenthesize: (node: Expression, parent: BinaryExpression) => parenthesizeBinaryOperand(getOperator(parent), node, false) }, ], [SyntaxKind.ConditionalExpression]: [ { name: "condition", test: isExpression }, @@ -196,7 +199,7 @@ namespace ts { { name: "expression", test: isExpression, optional: true }, ], [SyntaxKind.SpreadElementExpression]: [ - { name: "expression", test: isExpression }, + { name: "expression", test: isExpression, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.ClassExpression]: [ { name: "decorators", test: isDecorator }, @@ -207,7 +210,7 @@ namespace ts { { name: "members", test: isClassElement }, ], [SyntaxKind.ExpressionWithTypeArguments]: [ - { name: "expression", test: isLeftHandSideExpression }, + { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, { name: "typeArguments", test: isTypeNode }, ], [SyntaxKind.AsExpression]: [ @@ -227,7 +230,7 @@ namespace ts { { name: "declarationList", test: isVariableDeclarationList }, ], [SyntaxKind.ExpressionStatement]: [ - { name: "expression", test: isExpression }, + { name: "expression", test: isExpression, parenthesize: parenthesizeExpressionForExpressionStatement }, ], [SyntaxKind.IfStatement]: [ { name: "expression", test: isExpression }, @@ -290,7 +293,7 @@ namespace ts { [SyntaxKind.VariableDeclaration]: [ { name: "name", test: isBindingName }, { name: "type", test: isTypeNode, optional: true }, - { name: "initializer", test: isExpression, optional: true }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.VariableDeclarationList]: [ { name: "declarations", test: isVariableDeclaration }, @@ -419,7 +422,7 @@ namespace ts { ], [SyntaxKind.PropertyAssignment]: [ { name: "name", test: isPropertyName }, - { name: "initializer", test: isExpression }, + { name: "initializer", test: isExpression, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.ShorthandPropertyAssignment]: [ { name: "name", test: isIdentifier }, @@ -427,7 +430,7 @@ namespace ts { ], [SyntaxKind.EnumMember]: [ { name: "name", test: isPropertyName }, - { name: "initializer", test: isExpression, optional: true }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList }, ], [SyntaxKind.SourceFile]: [ { name: "statements", test: isStatement }, @@ -622,13 +625,6 @@ namespace ts { return updated; } - /** - * Sets the value of an edge, adjusting the value as necessary for cases such as expression precedence. - */ - function setEdgeValue(parentNode: Node & Map, edge: NodeEdge, value: Node | NodeArray) { - parentNode[edge.name] = value; - } - /** * Visits a node edge. * @@ -642,6 +638,26 @@ namespace ts { : visitNode(value, visitor, edge.test, edge.optional, edge.lift); } + /** + * Sets the value of an edge, adjusting the value as necessary for cases such as expression precedence. + */ + function setEdgeValue(parentNode: Node & Map, edge: NodeEdge, value: Node | NodeArray) { + if (value && edge.parenthesize && !isArray(value)) { + value = parenthesizeEdge(value, parentNode, edge.parenthesize, edge.test); + } + + parentNode[edge.name] = value; + } + + /** + * Applies parentheses to a node to ensure the correct precedence. + */ + function parenthesizeEdge(node: Node, parentNode: Node, parenthesize: (node: Node, parentNode: Node) => Node, test: (node: Node) => boolean) { + node = parenthesize(node, parentNode); + Debug.assert(test === undefined || test(node), "Unexpected node kind after visit."); + return node; + } + /** * Appends a node to an array. * @@ -848,7 +864,7 @@ namespace ts { * * @param nodes The NodeArray. */ - function liftToBlock(nodes: NodeArray) { + export function liftToBlock(nodes: NodeArray) { Debug.assert(every(nodes, isStatement), "Cannot lift nodes to a Block."); return createBlock(>nodes); } From 2ab8877859bf7381dcc9a1bfdb1c9e0137f42e19 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 15:52:58 -0800 Subject: [PATCH 5/9] Fix for new linter warning --- src/server/protocol.d.ts | 116 +++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index 3a669753323..19ede892daf 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -1,54 +1,54 @@ -/** - * Declaration module describing the TypeScript Server protocol +/** + * Declaration module describing the TypeScript Server protocol */ declare namespace ts.server.protocol { - /** - * A TypeScript Server message + /** + * A TypeScript Server message */ export interface Message { - /** - * Sequence number of the message + /** + * Sequence number of the message */ seq: number; /** - * One of "request", "response", or "event" + * One of "request", "response", or "event" */ type: string; } - /** - * Client-initiated request message + /** + * Client-initiated request message */ export interface Request extends Message { /** - * The command to execute + * The command to execute */ command: string; - /** - * Object containing arguments for the command + /** + * Object containing arguments for the command */ arguments?: any; } /** - * Request to reload the project structure for all the opened files + * Request to reload the project structure for all the opened files */ export interface ReloadProjectsRequest extends Message { } - /** - * Server-initiated event message + /** + * Server-initiated event message */ export interface Event extends Message { - /** - * Name of event + /** + * Name of event */ event: string; - /** - * Event-specific information + /** + * Event-specific information */ body?: any; } @@ -62,18 +62,18 @@ declare namespace ts.server.protocol { */ request_seq: number; - /** - * Outcome of the request. + /** + * Outcome of the request. */ success: boolean; - /** + /** * The command requested. */ command: string; - /** - * Contains error message if success === false. + /** + * Contains error message if success === false. */ message?: string; @@ -83,7 +83,7 @@ declare namespace ts.server.protocol { body?: any; } - /** + /** * Arguments for FileRequest messages. */ export interface FileRequestArgs { @@ -93,7 +93,7 @@ declare namespace ts.server.protocol { file: string; } - /** + /** * Arguments for ProjectInfoRequest request. */ export interface ProjectInfoRequestArgs extends FileRequestArgs { @@ -110,7 +110,7 @@ declare namespace ts.server.protocol { arguments: ProjectInfoRequestArgs; } - /** + /** * Response message body for "projectInfo" request */ export interface ProjectInfo { @@ -125,7 +125,7 @@ declare namespace ts.server.protocol { fileNames?: string[]; } - /** + /** * Response message for "projectInfo" request */ export interface ProjectInfoResponse extends Response { @@ -144,12 +144,12 @@ declare namespace ts.server.protocol { * (file, line, character offset), where line and character offset are 1-based. */ export interface FileLocationRequestArgs extends FileRequestArgs { - /** + /** * The line number for the request (1-based). */ line: number; - /** + /** * The character offset (on the line) for the request (1-based). */ offset: number; @@ -216,7 +216,7 @@ declare namespace ts.server.protocol { * Object found in response messages defining a span of text in a specific source file. */ export interface FileSpan extends TextSpan { - /** + /** * File containing text span. */ file: string; @@ -300,14 +300,14 @@ declare namespace ts.server.protocol { */ lineText: string; - /** + /** * True if reference is a write location, false otherwise. */ isWriteAccess: boolean; } /** - * The body of a "references" response message. + * The body of a "references" response message. */ export interface ReferencesResponseBody { /** @@ -325,7 +325,7 @@ declare namespace ts.server.protocol { */ symbolStartOffset: number; - /** + /** * The full display name of the symbol. */ symbolDisplayString: string; @@ -355,7 +355,7 @@ declare namespace ts.server.protocol { } /** - * Information about the item to be renamed. + * Information about the item to be renamed. */ export interface RenameInfo { /** @@ -373,7 +373,7 @@ declare namespace ts.server.protocol { */ displayName: string; - /** + /** * Full display name of item to be renamed. */ fullDisplayName: string; @@ -383,7 +383,7 @@ declare namespace ts.server.protocol { */ kind: string; - /** + /** * Optional modifiers for the kind (such as 'public'). */ kindModifiers: string; @@ -469,7 +469,7 @@ declare namespace ts.server.protocol { placeOpenBraceOnNewLineForControlBlocks?: boolean; /** Index operator */ - [key: string] : string | number | boolean; + [key: string]: string | number | boolean; } /** @@ -477,7 +477,7 @@ declare namespace ts.server.protocol { */ export interface ConfigureRequestArguments { - /** + /** * Information about the host, for example 'Emacs 24.4' or * 'Sublime Text version 3075' */ @@ -495,7 +495,7 @@ declare namespace ts.server.protocol { } /** - * Configure request; value of command field is "configure". Specifies + * Configure request; value of command field is "configure". Specifies * host information, such as host type, tab size, and indent size. */ export interface ConfigureRequest extends Request { @@ -514,8 +514,8 @@ declare namespace ts.server.protocol { */ export interface OpenRequestArgs extends FileRequestArgs { /** - * Used when a version of the file content is known to be more up to date than the one on disk. - * Then the known content will be used upon opening instead of the disk copy + * Used when a version of the file content is known to be more up to date than the one on disk. + * Then the known content will be used upon opening instead of the disk copy */ fileContent?: string; } @@ -751,7 +751,7 @@ declare namespace ts.server.protocol { * Optional modifiers for the kind (such as 'public'). */ kindModifiers: string; - /** + /** * A string that is used for comparing completion items so that they can be ordered. This * is often the same as the name but may be different in certain circumstances. */ @@ -794,7 +794,7 @@ declare namespace ts.server.protocol { } /** - * Signature help information for a single parameter + * Signature help information for a single parameter */ export interface SignatureHelpParameter { @@ -814,18 +814,18 @@ declare namespace ts.server.protocol { displayParts: SymbolDisplayPart[]; /** - * Whether the parameter is optional or not. + * Whether the parameter is optional or not. */ isOptional: boolean; } /** - * Represents a single signature to show in signature help. + * Represents a single signature to show in signature help. */ export interface SignatureHelpItem { /** - * Whether the signature accepts a variable number of arguments. + * Whether the signature accepts a variable number of arguments. */ isVariadic: boolean; @@ -845,7 +845,7 @@ declare namespace ts.server.protocol { separatorDisplayParts: SymbolDisplayPart[]; /** - * The signature helps items for the parameters. + * The signature helps items for the parameters. */ parameters: SignatureHelpParameter[]; @@ -861,17 +861,17 @@ declare namespace ts.server.protocol { export interface SignatureHelpItems { /** - * The signature help items. + * The signature help items. */ items: SignatureHelpItem[]; /** - * The span for which signature help should appear on a signature + * The span for which signature help should appear on a signature */ applicableSpan: TextSpan; /** - * The item selected in the set of available help items. + * The item selected in the set of available help items. */ selectedItemIndex: number; @@ -895,7 +895,7 @@ declare namespace ts.server.protocol { /** * Signature help request; value of command field is "signatureHelp". - * Given a file location (file, line, col), return the signature + * Given a file location (file, line, col), return the signature * help. */ export interface SignatureHelpRequest extends FileLocationRequest { @@ -926,8 +926,8 @@ declare namespace ts.server.protocol { } /** - * GeterrForProjectRequest request; value of command field is - * "geterrForProject". It works similarly with 'Geterr', only + * GeterrForProjectRequest request; value of command field is + * "geterrForProject". It works similarly with 'Geterr', only * it request for every file in this project. */ export interface GeterrForProjectRequest extends Request { @@ -997,7 +997,7 @@ declare namespace ts.server.protocol { diagnostics: Diagnostic[]; } - /** + /** * Event message for "syntaxDiag" and "semanticDiag" event types. * These events provide syntactic and semantic errors for a file. */ @@ -1033,7 +1033,7 @@ declare namespace ts.server.protocol { export interface ReloadResponse extends Response { } - /** + /** * Arguments for saveto request. */ export interface SavetoRequestArgs extends FileRequestArgs { @@ -1109,7 +1109,7 @@ declare namespace ts.server.protocol { */ kindModifiers?: string; - /** + /** * The file in which the symbol is found. */ file: string; @@ -1156,7 +1156,7 @@ declare namespace ts.server.protocol { /** * Change request message; value of command field is "change". - * Update the server's view of the file named by argument 'file'. + * Update the server's view of the file named by argument 'file'. * Server does not currently send a response to a change request. */ export interface ChangeRequest extends FileLocationRequest { From 80f89a1e5b3e124cb25eb778aadbbd1d7c8cfa1d Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 15:55:07 -0800 Subject: [PATCH 6/9] PR feedback --- src/compiler/binder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6f1de3677e7..4ea2489de88 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1798,7 +1798,7 @@ namespace ts { * @param node The node to analyze * @param subtreeFlags Transform flags computed for this node's subtree */ - export function computeTransformFlagsForNode(node: Node, subtreeFlags: TransformFlags) { + export function computeTransformFlagsForNode(node: Node, subtreeFlags: TransformFlags): TransformFlags { // Ambient nodes are TypeScript syntax and the flags of their subtree are ignored. if (node.flags & NodeFlags.Ambient) { return (node.transformFlags = TransformFlags.AssertTypeScript) From 6d273366d3d380b3e4bf0b2e70b5bde20955b06b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 16:10:32 -0800 Subject: [PATCH 7/9] Merged some changes from other branches. --- Jakefile.js | 9 + src/compiler/comments.ts | 144 ++++++++----- src/compiler/declarationEmitter.ts | 8 +- src/compiler/emitter.ts | 58 ++++-- src/compiler/printer.ts | 324 ++++++++++++++++++----------- src/compiler/program.ts | 3 +- src/compiler/sourcemap.ts | 37 +++- src/compiler/types.ts | 20 +- src/compiler/utilities.ts | 56 +++-- 9 files changed, 436 insertions(+), 223 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 44cb1a61dbe..7b9c4806e0a 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -222,6 +222,7 @@ function concatenateFiles(destinationFile, sourceFiles) { } var useDebugMode = true; +var useTransforms = process.env.USE_TRANSFORMS || false; var host = (process.env.host || process.env.TYPESCRIPT_HOST || "node"); var compilerFilename = "tsc.js"; var LKGCompiler = path.join(LKGDirectory, compilerFilename); @@ -281,6 +282,10 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOu options += " --stripInternal" } + if (useBuiltCompiler && useTransforms) { + options += " --experimentalTransforms" + } + var cmd = host + " " + compilerPath + " " + options + " "; cmd = cmd + sources.join(" "); console.log(cmd + "\n"); @@ -404,6 +409,10 @@ task("setDebugMode", function() { useDebugMode = true; }); +task("setTransforms", function() { + useTransforms = true; +}); + task("configure-nightly", [configureNightlyJs], function() { var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + programTs; console.log(cmd); diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index b93b144bf01..a680d01e102 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -5,11 +5,15 @@ namespace ts { export interface CommentWriter { reset(): void; setSourceFile(sourceFile: SourceFile): void; - getLeadingCommentsToEmit(node: TextRange): CommentRange[]; - getTrailingCommentsToEmit(node: TextRange): CommentRange[]; - emitDetachedComments(node: TextRange): void; - emitLeadingComments(node: TextRange, comments?: CommentRange[]): void; - emitTrailingComments(node: TextRange, comments?: CommentRange[]): void; + getLeadingComments(range: Node, getAdditionalRange?: (range: Node) => Node): CommentRange[]; + getLeadingComments(range: TextRange): CommentRange[]; + getLeadingCommentsOfPosition(pos: number): CommentRange[]; + getTrailingComments(range: Node, getAdditionalRange?: (range: Node) => Node): CommentRange[]; + getTrailingComments(range: TextRange): CommentRange[]; + getTrailingCommentsOfPosition(pos: number): CommentRange[]; + emitLeadingComments(range: TextRange, comments?: CommentRange[]): void; + emitTrailingComments(range: TextRange, comments?: CommentRange[]): void; + emitDetachedComments(range: TextRange): void; } export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter { @@ -25,8 +29,8 @@ namespace ts { // This maps start->end for a comment range. See `hasConsumedCommentRange` and // `consumeCommentRange` for usage. let consumedCommentRanges: number[]; - let leadingCommentRangeNodeStarts: boolean[]; - let trailingCommentRangeNodeEnds: boolean[]; + let leadingCommentRangePositions: boolean[]; + let trailingCommentRangePositions: boolean[]; return compilerOptions.removeComments ? createCommentRemovingWriter() @@ -36,11 +40,13 @@ namespace ts { return { reset, setSourceFile, - getLeadingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; }, - getTrailingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; }, + getLeadingComments(range: TextRange, getAdditionalRange?: (range: TextRange) => TextRange): CommentRange[] { return undefined; }, + getLeadingCommentsOfPosition(pos: number): CommentRange[] { return undefined; }, + getTrailingComments(range: TextRange, getAdditionalRange?: (range: TextRange) => TextRange): CommentRange[] { return undefined; }, + getTrailingCommentsOfPosition(pos: number): CommentRange[] { return undefined; }, + emitLeadingComments(range: TextRange, comments?: CommentRange[]): void { }, + emitTrailingComments(range: TextRange, comments?: CommentRange[]): void { }, emitDetachedComments, - emitLeadingComments(node: TextRange, comments?: CommentRange[]): void { }, - emitTrailingComments(node: TextRange, comments?: CommentRange[]): void { }, }; function emitDetachedComments(node: TextRange): void { @@ -53,41 +59,85 @@ namespace ts { return { reset, setSourceFile, - getLeadingCommentsToEmit, - getTrailingCommentsToEmit, - emitDetachedComments, + getLeadingComments, + getLeadingCommentsOfPosition, + getTrailingComments, + getTrailingCommentsOfPosition, emitLeadingComments, emitTrailingComments, + emitDetachedComments, }; - function getLeadingCommentsToEmit(node: TextRange) { - if (nodeIsSynthesized(node)) { - return; + function getLeadingComments(range: TextRange | Node, getAdditionalRange?: (range: Node) => Node) { + let comments = getLeadingCommentsOfPosition(range.pos); + if (getAdditionalRange) { + let additionalRange = getAdditionalRange(range); + while (additionalRange) { + comments = concatenate( + getLeadingCommentsOfPosition(additionalRange.pos), + comments + ); + + additionalRange = getAdditionalRange(additionalRange); + } } - if (!leadingCommentRangeNodeStarts[node.pos]) { - leadingCommentRangeNodeStarts[node.pos] = true; - const comments = hasDetachedComments(node.pos) - ? getLeadingCommentsWithoutDetachedComments() - : getLeadingCommentRanges(currentText, node.pos); - return consumeCommentRanges(comments); - } - - return noComments; + return comments; } - function getTrailingCommentsToEmit(node: TextRange) { - if (nodeIsSynthesized(node)) { - return; + function getTrailingComments(range: TextRange | Node, getAdditionalRange?: (range: Node) => Node) { + let comments = getTrailingCommentsOfPosition(range.end); + if (getAdditionalRange) { + let additionalRange = getAdditionalRange(range); + while (additionalRange) { + comments = concatenate( + comments, + getTrailingCommentsOfPosition(additionalRange.end) + ); + + additionalRange = getAdditionalRange(additionalRange); + } } - if (!trailingCommentRangeNodeEnds[node.end]) { - trailingCommentRangeNodeEnds[node.end] = true; - const comments = getTrailingCommentRanges(currentText, node.end); - return consumeCommentRanges(comments); + return comments; + } + + function getLeadingCommentsOfPosition(pos: number) { + if (positionIsSynthesized(pos) || leadingCommentRangePositions[pos]) { + return undefined; } - return noComments; + leadingCommentRangePositions[pos] = true; + const comments = hasDetachedComments(pos) + ? getLeadingCommentsWithoutDetachedComments() + : getLeadingCommentRanges(currentText, pos); + return consumeCommentRanges(comments); + } + + function getTrailingCommentsOfPosition(pos: number) { + if (positionIsSynthesized(pos) || trailingCommentRangePositions[pos]) { + return undefined; + } + + trailingCommentRangePositions[pos] = true; + const comments = getTrailingCommentRanges(currentText, pos); + return consumeCommentRanges(comments); + } + + function emitLeadingComments(range: TextRange, comments = getLeadingComments(range)) { + emitNewLineBeforeLeadingComments(currentLineMap, writer, range, comments); + + // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + emitComments(currentText, currentLineMap, writer, comments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); + } + + function emitTrailingComments(range: TextRange, comments = getTrailingComments(range)) { + // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ + emitComments(currentText, currentLineMap, writer, comments, /*leadingSeparator*/ true, /*trailingSeparator*/ false, newLine, writeComment); + } + + function emitDetachedComments(range: TextRange) { + emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false); } function hasConsumedCommentRange(comment: CommentRange) { @@ -136,22 +186,6 @@ namespace ts { return noComments; } - - function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) { - emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments); - - // Leading comments are emitted as `/*leading comment1 */space/*leading comment*/space` - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); - } - - function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) { - // Trailing comments are emitted as `space/*trailing comment1 */space/*trailing comment*/` - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); - } - - function emitDetachedComments(range: TextRange) { - emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false); - } } function reset() { @@ -160,8 +194,8 @@ namespace ts { currentLineMap = undefined; detachedCommentsInfo = undefined; consumedCommentRanges = undefined; - trailingCommentRangeNodeEnds = undefined; - leadingCommentRangeNodeStarts = undefined; + trailingCommentRangePositions = undefined; + leadingCommentRangePositions = undefined; } function setSourceFile(sourceFile: SourceFile) { @@ -170,8 +204,8 @@ namespace ts { currentLineMap = getLineStarts(sourceFile); detachedCommentsInfo = undefined; consumedCommentRanges = []; - leadingCommentRangeNodeStarts = []; - trailingCommentRangeNodeEnds = []; + leadingCommentRangePositions = []; + trailingCommentRangePositions = []; } function hasDetachedComments(pos: number) { @@ -182,7 +216,7 @@ namespace ts { // get the leading comments from detachedPos const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos; const leadingComments = getLeadingCommentRanges(currentText, pos); - if (detachedCommentsInfo.length > 1) { + if (detachedCommentsInfo.length - 1) { detachedCommentsInfo.pop(); } else { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index ab0b16947fc..c89b7579d52 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -91,7 +91,7 @@ namespace ts { // Emit reference in dts, if the file reference was not already emitted if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) { // Add a reference to generated dts file, - // global file reference is added only + // global file reference is added only // - if it is not bundled emit (because otherwise it would be self reference) // - and it is not already added if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference)) { @@ -144,7 +144,7 @@ namespace ts { if (!isBundledEmit && isExternalModule(sourceFile) && sourceFile.moduleAugmentations.length && !resultHasExternalModuleIndicator) { // if file was external module with augmentations - this fact should be preserved in .d.ts as well. - // in case if we didn't write any external module specifiers in .d.ts we need to emit something + // in case if we didn't write any external module specifiers in .d.ts we need to emit something // that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here. write("export {};"); writeLine(); @@ -349,7 +349,7 @@ namespace ts { const jsDocComments = getJsDocCommentsFromText(declaration, currentText); emitNewLineBeforeLeadingComments(currentLineMap, writer, declaration, jsDocComments); // jsDoc comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, jsDocComments, /*trailingSeparator*/ true, newLine, writeCommentRange); + emitComments(currentText, currentLineMap, writer, jsDocComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeCommentRange); } } @@ -736,7 +736,7 @@ namespace ts { function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) { // emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations). - // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered + // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered // external modules since they are indistingushable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}' // so compiler will treat them as external modules. resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index fb9c70f9a7c..3919e68aba5 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1915,7 +1915,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (multiLine) { decreaseIndent(); - writeLine(); } write(")"); @@ -2242,6 +2241,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return node; } + function skipAssertions(node: Expression): Expression { + while (node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) { + node = (node).expression; + } + return node; + } + function emitCallTarget(node: Expression): Expression { if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword) { emit(node); @@ -3308,8 +3314,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge // we can't reuse 'arr' because it might be modified within the body of the loop. const counter = createTempVariable(TempFlags._i); const rhsReference = createSynthesizedNode(SyntaxKind.Identifier) as Identifier; - rhsReference.text = node.expression.kind === SyntaxKind.Identifier ? - makeUniqueName((node.expression).text) : + const expressionWithoutAssertions = skipAssertions(node.expression); + rhsReference.text = expressionWithoutAssertions.kind === SyntaxKind.Identifier ? + makeUniqueName((expressionWithoutAssertions).text) : makeTempVariableName(TempFlags.Auto); // This is the let keyword for the counter and rhsReference. The let keyword for @@ -4324,7 +4331,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge writeLine(); emitStart(restParam); emitNodeWithCommentsAndWithoutSourcemap(restParam.name); - write("[" + tempName + " - " + restIndex + "] = arguments[" + tempName + "];"); + write(restIndex > 0 + ? `[${tempName} - ${restIndex}] = arguments[${tempName}];` + : `[${tempName}] = arguments[${tempName}];`); emitEnd(restParam); decreaseIndent(); writeLine(); @@ -5340,6 +5349,18 @@ const _super = (function (geti, seti) { write(" = "); } + const staticProperties = getInitializedProperties(node, /*isStatic*/ true); + const isClassExpressionWithStaticProperties = staticProperties.length > 0 && node.kind === SyntaxKind.ClassExpression; + let tempVariable: Identifier; + + if (isClassExpressionWithStaticProperties) { + tempVariable = createAndRecordTempVariable(TempFlags.Auto); + write("("); + increaseIndent(); + emit(tempVariable); + write(" = "); + } + write("(function ("); const baseTypeNode = getClassExtendsHeritageClauseElement(node); if (baseTypeNode) { @@ -5369,9 +5390,6 @@ const _super = (function (geti, seti) { writeLine(); emitConstructor(node, baseTypeNode); emitMemberFunctionsForES5AndLower(node); - emitPropertyDeclarations(node, getInitializedProperties(node, /*isStatic*/ true)); - writeLine(); - emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); writeLine(); emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => { write("return "); @@ -5398,7 +5416,23 @@ const _super = (function (geti, seti) { write("))"); if (node.kind === SyntaxKind.ClassDeclaration) { write(";"); + emitPropertyDeclarations(node, staticProperties); + writeLine(); + emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined); } + else if (isClassExpressionWithStaticProperties) { + for (const property of staticProperties) { + write(","); + writeLine(); + emitPropertyDeclaration(node, property, /*receiver*/ tempVariable, /*isExpression*/ true); + } + write(","); + writeLine(); + emit(tempVariable); + decreaseIndent(); + write(")"); + } + emitEnd(node); if (node.kind === SyntaxKind.ClassDeclaration) { @@ -7937,7 +7971,7 @@ const _super = (function (geti, seti) { emitNewLineBeforeLeadingComments(currentLineMap, writer, node, leadingComments); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, leadingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitTrailingComments(node: Node) { @@ -7949,7 +7983,7 @@ const _super = (function (geti, seti) { const trailingComments = getTrailingCommentsToEmit(node); // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, trailingComments, /*leadingSeparator*/ true, /*trailingSeparator*/ false, newLine, writeComment); } /** @@ -7964,8 +7998,8 @@ const _super = (function (geti, seti) { const trailingComments = getTrailingCommentRanges(currentText, pos); - // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ - emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ true, newLine, writeComment); + // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space + emitComments(currentText, currentLineMap, writer, trailingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitLeadingCommentsOfPositionWorker(pos: number) { @@ -7986,7 +8020,7 @@ const _super = (function (geti, seti) { emitNewLineBeforeLeadingComments(currentLineMap, writer, { pos: pos, end: pos }, leadingComments); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(currentText, currentLineMap, writer, leadingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); } function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange) { diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 84388d4fd88..c6901244186 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -75,6 +75,15 @@ function __export(m) { } })`; + const superHelper = ` +const _super = name => super[name];`; + + const advancedSuperHelper = ` +const _super = (function (geti, seti) { + const cache = Object.create(null); + return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); +})(name => super[name], (name, value) => super[name] = value);`; + const compilerOptions = host.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -110,20 +119,41 @@ function __export(m) { const transformers = getTransformers(compilerOptions).concat(initializePrinter); const writer = createTextWriter(newLine); - const { write, writeTextOfNode, writeLine, increaseIndent, decreaseIndent } = writer; + const { + write, + writeTextOfNode, + writeLine, + increaseIndent, + decreaseIndent + } = writer; const sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? createSourceMapWriter(host, writer) : getNullSourceMapWriter(); - const { emitStart, emitEnd, emitPos } = sourceMap; + const { + emitStart, + emitEnd, + emitPos + } = sourceMap; const comments = createCommentWriter(host, writer, sourceMap); - const { emitDetachedComments, emitLeadingComments, emitTrailingComments, getLeadingCommentsToEmit, getTrailingCommentsToEmit } = comments; + const { + getLeadingComments, + getTrailingComments, + getTrailingCommentsOfPosition, + emitLeadingComments, + emitTrailingComments, + emitDetachedComments + } = comments; let context: TransformationContext; let startLexicalEnvironment: () => void; let endLexicalEnvironment: () => Statement[]; let getNodeEmitFlags: (node: Node) => NodeEmitFlags; + let isExpressionSubstitutionEnabled: (node: Node) => boolean; + let isEmitNotificationEnabled: (node: Node) => boolean; let expressionSubstitution: (node: Expression) => Expression; let identifierSubstitution: (node: Identifier) => Identifier; + let onBeforeEmitNode: (node: Node) => void; + let onAfterEmitNode: (node: Node) => void; let isUniqueName: (name: string) => boolean; let temporaryVariables: string[] = []; let tempFlags: TempFlags; @@ -177,8 +207,12 @@ function __export(m) { startLexicalEnvironment = undefined; endLexicalEnvironment = undefined; getNodeEmitFlags = undefined; + isExpressionSubstitutionEnabled = undefined; + isEmitNotificationEnabled = undefined; expressionSubstitution = undefined; identifierSubstitution = undefined; + onBeforeEmitNode = undefined; + onAfterEmitNode = undefined; isUniqueName = undefined; temporaryVariables = undefined; tempFlags = 0; @@ -196,8 +230,12 @@ function __export(m) { startLexicalEnvironment = context.startLexicalEnvironment; endLexicalEnvironment = context.endLexicalEnvironment; getNodeEmitFlags = context.getNodeEmitFlags; + isExpressionSubstitutionEnabled = context.isExpressionSubstitutionEnabled; + isEmitNotificationEnabled = context.isEmitNotificationEnabled; expressionSubstitution = context.expressionSubstitution; identifierSubstitution = context.identifierSubstitution; + onBeforeEmitNode = context.onBeforeEmitNode; + onAfterEmitNode = context.onAfterEmitNode; isUniqueName = context.isUniqueName; return printSourceFile; } @@ -212,18 +250,46 @@ function __export(m) { } function emit(node: Node) { + emitWithWorker(node, emitWorker); + } + + function emitExpression(node: Expression) { + emitWithWorker(node, emitExpressionWorker); + } + + function emitWithWorker(node: Node, emitWorker: (node: Node) => void) { if (node) { - const leadingComments = getLeadingCommentsToEmit(node); - const trailingComments = getTrailingCommentsToEmit(node); + const adviseOnEmit = isEmitNotificationEnabled(node); + if (adviseOnEmit && onBeforeEmitNode) { + onBeforeEmitNode(node); + } + + const leadingComments = getLeadingComments(node, getNotEmittedParent); + const trailingComments = getTrailingComments(node, getNotEmittedParent); emitLeadingComments(node, leadingComments); emitStart(node); emitWorker(node); emitEnd(node); emitTrailingComments(node, trailingComments); + + if (adviseOnEmit && onAfterEmitNode) { + onAfterEmitNode(node); + } } } - function emitWorker(node: Node) { + function getNotEmittedParent(node: Node): Node { + if (getNodeEmitFlags(node) & NodeEmitFlags.EmitCommentsOfNotEmittedParent) { + const parent = getOriginalNode(node).parent; + if (getNodeEmitFlags(parent) & NodeEmitFlags.IsNotEmittedNode) { + return parent; + } + } + + return undefined; + } + + function emitWorker(node: Node): void { const kind = node.kind; switch (kind) { // Pseudo-literals @@ -234,7 +300,11 @@ function __export(m) { // Identifiers case SyntaxKind.Identifier: - return emitIdentifier(node, identifierSubstitution); + if (tryEmitSubstitute(node, identifierSubstitution)) { + return; + } + + return emitIdentifier(node); // Reserved words case SyntaxKind.ConstKeyword: @@ -324,7 +394,7 @@ function __export(m) { case SyntaxKind.ExpressionWithTypeArguments: return emitExpressionWithTypeArguments(node); case SyntaxKind.ThisType: - return write("this"); + return emitThisType(node); case SyntaxKind.StringLiteralType: return emitLiteral(node); @@ -340,7 +410,7 @@ function __export(m) { case SyntaxKind.TemplateSpan: return emitTemplateSpan(node); case SyntaxKind.SemicolonClassElement: - return write(";"); + return emitSemicolonClassElement(node); // Statements case SyntaxKind.Block: @@ -348,7 +418,7 @@ function __export(m) { case SyntaxKind.VariableStatement: return emitVariableStatement(node); case SyntaxKind.EmptyStatement: - return write(";"); + return emitEmptyStatement(node); case SyntaxKind.ExpressionStatement: return emitExpressionStatement(node); case SyntaxKind.IfStatement: @@ -467,25 +537,17 @@ function __export(m) { // JSDoc nodes (ignored) } - if (isExpressionKind(kind)) { + if (isExpression(node)) { return emitExpressionWorker(node); } } - function emitExpression(node: Expression) { - if (node) { - const leadingComments = getLeadingCommentsToEmit(node); - const trailingComments = getTrailingCommentsToEmit(node); - emitLeadingComments(node, leadingComments); - emitStart(node); - emitExpressionWorker(node); - emitEnd(node); - emitTrailingComments(node, trailingComments); - } - } - function emitExpressionWorker(node: Node) { const kind = node.kind; + if (isExpressionSubstitutionEnabled(node) && tryEmitSubstitute(node, expressionSubstitution)) { + return; + } + switch (kind) { // Literals case SyntaxKind.NumericLiteral: @@ -496,16 +558,15 @@ function __export(m) { // Identifiers case SyntaxKind.Identifier: - return emitIdentifier(node, expressionSubstitution); + return emitIdentifier(node); // Reserved words case SyntaxKind.FalseKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.TrueKeyword: - return writeTokenNode(node); case SyntaxKind.ThisKeyword: - return emitThisKeyword(node); + return writeTokenNode(node); // Expressions case SyntaxKind.ArrayLiteralExpression: @@ -597,17 +658,14 @@ function __export(m) { // Identifiers // - function emitIdentifier(node: Identifier, substitution: (node: Node) => Node) { - if (tryEmitSubstitute(node, substitution)) { - return; - } - else if (node.text === undefined) { + function emitIdentifier(node: Identifier) { + if (node.text === undefined) { // Emit a temporary variable name for this node. const nodeId = getOriginalNodeId(node); const text = temporaryVariables[nodeId] || (temporaryVariables[nodeId] = makeTempVariableName(tempKindToFlags(node.tempKind))); write(text); } - else if (nodeIsSynthesized(node)) { + else if (nodeIsSynthesized(node) || !node.parent) { if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) { writeLines(umdHelper); } @@ -620,15 +678,6 @@ function __export(m) { } } - function emitThisKeyword(node: PrimaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - else { - writeTokenNode(node); - } - } - // // Names // @@ -716,13 +765,13 @@ function __export(m) { emitModifiers(node, node.modifiers); writeIfPresent(node.asteriskToken, "*"); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitConstructor(node: ConstructorDeclaration) { emitModifiers(node, node.modifiers); write("constructor"); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitAccessorDeclaration(node: AccessorDeclaration) { @@ -730,7 +779,7 @@ function __export(m) { emitModifiers(node, node.modifiers); write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } function emitCallSignature(node: CallSignatureDeclaration) { @@ -760,6 +809,10 @@ function __export(m) { write(";"); } + function emitSemicolonClassElement(node: SemicolonClassElement) { + write(";"); + } + // // Types // @@ -826,6 +879,10 @@ function __export(m) { write(")"); } + function emitThisType(node: ThisTypeNode) { + write("this"); + } + // // Binding patterns // @@ -871,7 +928,7 @@ function __export(m) { write("[]"); } else { - const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None; + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); } } @@ -882,7 +939,7 @@ function __export(m) { write("{}"); } else { - const preferNewLine = getNodeEmitFlags(node) & NodeEmitFlags.MultiLine ? ListFormat.PreferNewLine : ListFormat.None; + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); } @@ -973,27 +1030,8 @@ function __export(m) { function emitArrowFunction(node: ArrowFunction) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); - const body = node.body; - if (isBlock(body)) { - const savedTempFlags = tempFlags; - tempFlags = 0; - startLexicalEnvironment(); - emitArrowFunctionHead(node); - write(" {"); + emitSignatureAndBody(node, emitArrowFunctionHead); - const startingLine = writer.getLine(); - emitBlockFunctionBody(node, body); - - const endingLine = writer.getLine(); - emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); - tempFlags = savedTempFlags; - write("}"); - } - else { - emitArrowFunctionHead(node); - write(" "); - emitExpression(body); - } } function emitArrowFunctionHead(node: ArrowFunction) { @@ -1051,19 +1089,11 @@ function __export(m) { } function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - emitExpression(node.operand); writeToken(node.operator); } function emitBinaryExpression(node: BinaryExpression) { - if (tryEmitSubstitute(node, expressionSubstitution)) { - return; - } - const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); @@ -1169,6 +1199,10 @@ function __export(m) { write(";"); } + function emitEmptyStatement(node: EmptyStatement) { + write(";"); + } + function emitExpressionStatement(node: ExpressionStatement) { emitExpression(node.expression); write(";"); @@ -1336,25 +1370,27 @@ function __export(m) { emitModifiers(node, node.modifiers); write(node.asteriskToken ? "function* " : "function "); emit(node.name); - emitSignatureAndBody(node); + emitSignatureAndBody(node, emitSignatureHead); } - function emitSignatureAndBody(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { + function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { const body = node.body; if (body) { - const savedTempFlags = tempFlags; - tempFlags = 0; - startLexicalEnvironment(); - emitSignatureHead(node); - write(" {"); - - const startingLine = writer.getLine(); - emitBlockFunctionBody(node, body); - - const endingLine = writer.getLine(); - emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); - write("}"); - tempFlags = savedTempFlags; + if (isBlock(body)) { + const savedTempFlags = tempFlags; + tempFlags = 0; + startLexicalEnvironment(); + emitSignatureHead(node); + write(" {"); + emitBlockFunctionBody(node, body); + write("}"); + tempFlags = savedTempFlags; + } + else { + emitSignatureHead(node); + write(" "); + emitExpression(body); + } } else { emitSignatureHead(node); @@ -1371,34 +1407,49 @@ function __export(m) { function shouldEmitBlockFunctionBodyOnSingleLine(parentNode: Node, body: Block) { const originalNode = getOriginalNode(parentNode); - if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode) && rangeEndIsOnSameLineAsRangeStart(originalNode.body, originalNode.body)) { - for (const statement of body.statements) { - if (synthesizedNodeStartsOnNewLine(statement)) { - return false; + if (isFunctionLike(originalNode) && !nodeIsSynthesized(originalNode)) { + const body = originalNode.body; + if (isBlock(body)) { + if (rangeEndIsOnSameLineAsRangeStart(body, body)) { + for (const statement of body.statements) { + if (synthesizedNodeStartsOnNewLine(statement)) { + return false; + } + } + + return true; } } - - if (originalNode.kind === SyntaxKind.ArrowFunction && !rangeEndIsOnSameLineAsRangeStart((originalNode).equalsGreaterThanToken, originalNode.body)) { - return false; + else { + return rangeEndIsOnSameLineAsRangeStart((originalNode).equalsGreaterThanToken, originalNode.body); } - - return true; } return false; } function emitBlockFunctionBody(parentNode: Node, body: Block) { + const startingLine = writer.getLine(); + increaseIndent(); + emitDetachedComments(body.statements); + // Emit all the prologue directives (like "use strict"). - const statements = body.statements; - const statementOffset = emitPrologueDirectives(statements, /*startWithNewLine*/ true, /*indented*/ true); + const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); const helpersEmitted = emitHelpers(body); + if (statementOffset === 0 && !helpersEmitted && shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body)) { - emitList(body, statements, ListFormat.SingleLineFunctionBodyStatements); + decreaseIndent(); + emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); + increaseIndent(); } else { - emitList(body, statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); } + + const endingLine = writer.getLine(); + emitLexicalEnvironment(endLexicalEnvironment(), /*newLine*/ startingLine !== endingLine); + emitLeadingComments(collapseTextRange(body.statements, TextRangeCollapse.CollapseToEnd)); + decreaseIndent(); } function emitClassDeclaration(node: ClassDeclaration) { @@ -1668,6 +1719,8 @@ function __export(m) { write("case "); emitExpression(node.expression); write(":"); + + debugger; emitCaseOrDefaultClauseStatements(node, node.statements); } @@ -1688,6 +1741,7 @@ function __export(m) { function emitHeritageClause(node: HeritageClause) { emitStart(node); + write(" "); writeToken(node.token); write(" "); emitList(node, node.types, ListFormat.HeritageClauseTypes); @@ -1783,8 +1837,7 @@ function __export(m) { } } - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, indented?: boolean) { - increaseIndentIf(indented); + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean) { for (let i = 0; i < statements.length; i++) { if (isPrologueDirective(statements[i])) { if (startWithNewLine || i > 0) { @@ -1794,12 +1847,10 @@ function __export(m) { } else { // return index of the first non prologue directive - decreaseIndentIf(indented); return i; } } - decreaseIndentIf(indented); return statements.length; } @@ -1811,7 +1862,17 @@ function __export(m) { } if (emitFlags & NodeEmitFlags.EmitExportStar) { - emitExportStarHelper(); + writeLines(exportStarHelper); + helpersEmitted = true; + } + + if (emitFlags & NodeEmitFlags.EmitSuperHelper) { + writeLines(superHelper); + helpersEmitted = true; + } + + if (emitFlags & NodeEmitFlags.EmitAdvancedSuperHelper) { + writeLines(advancedSuperHelper); helpersEmitted = true; } @@ -1861,10 +1922,6 @@ function __export(m) { return helpersEmitted; } - function emitExportStarHelper() { - writeLines(exportStarHelper); - } - function writeLines(text: string): void { const lines = text.split(/\r\n|\r|\n/g); for (let i = 0; i < lines.length; i++) { @@ -2027,8 +2084,10 @@ function __export(m) { } else { // Write the opening line terminator or leading whitespace. + let shouldEmitInterveningComments = true; if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { writeLine(); + shouldEmitInterveningComments = false; } else if (format & ListFormat.SpaceBetweenBraces) { write(" "); @@ -2052,12 +2111,20 @@ function __export(m) { // Write either a line terminator or whitespace to separate the elements. if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) { writeLine(); + shouldEmitInterveningComments = false; } else if (previousSibling) { write(" "); } } + if (shouldEmitInterveningComments) { + emitLeadingComments(child, getTrailingCommentsOfPosition(child.pos)); + } + else { + shouldEmitInterveningComments = true; + } + // Emit this child. emit(child); @@ -2151,7 +2218,7 @@ function __export(m) { return true; } else if (format & ListFormat.PreserveLines) { - if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) { + if (format & ListFormat.PreferNewLine) { return true; } @@ -2193,10 +2260,10 @@ function __export(m) { function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { if (format & ListFormat.MultiLine) { - return true; + return (format & ListFormat.NoTrailingNewLine) === 0; } else if (format & ListFormat.PreserveLines) { - if (getNodeEmitFlags(parentNode) & NodeEmitFlags.MultiLine) { + if (format & ListFormat.PreferNewLine) { return true; } @@ -2218,7 +2285,7 @@ function __export(m) { function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) { if (nodeIsSynthesized(node)) { - const startsOnNewLine = (node).startsOnNewLine; + const startsOnNewLine = node.startsOnNewLine; if (startsOnNewLine === undefined) { return (format & ListFormat.PreferNewLine) !== 0; } @@ -2250,8 +2317,12 @@ function __export(m) { } function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + parent = skipSynthesizedParentheses(parent); + node1 = skipSynthesizedParentheses(node1); + node2 = skipSynthesizedParentheses(node2); + // Always use a newline for synthesized code if the synthesizer desires it. - if (synthesizedNodeStartsOnNewLine(node2)) { + if (node2.startsOnNewLine) { return true; } @@ -2261,6 +2332,14 @@ function __export(m) { && !rangeEndIsOnSameLineAsRangeStart(node1, node2); } + function skipSynthesizedParentheses(node: Node) { + while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { + node = (node).expression; + } + + return node; + } + function getTextOfNode(node: Node, includeTrivia?: boolean) { if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) { return node.text; @@ -2280,9 +2359,9 @@ function __export(m) { } function isSingleLineEmptyBlock(block: Block) { - return (getNodeEmitFlags(block) & NodeEmitFlags.MultiLine) === 0 && - block.statements.length === 0 && - rangeEndIsOnSameLineAsRangeStart(block, block); + return !block.multiLine + && block.statements.length === 0 + && rangeEndIsOnSameLineAsRangeStart(block, block); } function tempKindToFlags(kind: TempVariableKind) { @@ -2383,6 +2462,7 @@ function __export(m) { // Other PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes. + NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. // Precomputed Formats TypeLiteralMembers = MultiLine | Indented, @@ -2400,7 +2480,7 @@ function __export(m) { MultiLineBlockStatements = Indented | MultiLine, VariableDeclarationList = CommaDelimited | SingleLine, SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenBraces, - MultiLineFunctionBodyStatements = MultiLine | Indented, + MultiLineFunctionBodyStatements = MultiLine, ClassHeritageClauses = SingleLine, ClassMembers = Indented | MultiLine, InterfaceMembers = Indented | MultiLine, @@ -2409,9 +2489,9 @@ function __export(m) { NamedImportsOrExportsElements = CommaDelimited | AllowTrailingComma | SingleLine | SpaceBetweenBraces, JsxElementChildren = SingleLine, JsxElementAttributes = SingleLine, - CaseOrDefaultClauseStatements = Indented | MultiLine, + CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, HeritageClauseTypes = CommaDelimited | SingleLine, - SourceFileStatements = MultiLine, + SourceFileStatements = MultiLine | NoTrailingNewLine, Decorators = MultiLine | Optional, TypeArguments = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional, TypeParameters = CommaDelimited | SingleLine | Indented | AngleBrackets | Optional, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4a3d0cbc164..f10c327faa7 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -968,7 +968,8 @@ namespace ts { const start = new Date().getTime(); - const emitResult = emitFiles( + const fileEmitter = options.experimentalTransforms ? printFiles : emitFiles; + const emitResult = fileEmitter( emitResolver, getEmitHost(writeFileCallback), sourceFile); diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 86bad38cb6e..3866e4f9566 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -7,12 +7,15 @@ namespace ts { setSourceFile(sourceFile: SourceFile): void; emitPos(pos: number): void; emitStart(range: TextRange): void; - emitEnd(range: TextRange, stopOverridingSpan?: boolean): void; - changeEmitSourcePos(): void; + emitEnd(range: TextRange): void; + /*@deprecated*/ emitEnd(range: TextRange, stopOverridingSpan: boolean): void; + /*@deprecated*/ changeEmitSourcePos(): void; getText(): string; getSourceMappingURL(): string; initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void; reset(): void; + enable(): void; + disable(): void; } let nullSourceMapWriter: SourceMapWriter; @@ -38,6 +41,8 @@ namespace ts { getSourceMappingURL(): string { return undefined; }, initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { }, reset(): void { }, + enable(): void { }, + disable(): void { } }; } @@ -62,6 +67,8 @@ namespace ts { // Source map data let sourceMapData: SourceMapData; + let disableDepth: number; + return { getSourceMapData: () => sourceMapData, setSourceFile, @@ -73,6 +80,8 @@ namespace ts { getSourceMappingURL, initialize, reset, + enable, + disable, }; function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { @@ -81,6 +90,7 @@ namespace ts { } currentSourceFile = undefined; + disableDepth = 0; // Current source map file and its index in the sources list sourceMapSourceIndex = -1; @@ -147,6 +157,17 @@ namespace ts { lastEncodedSourceMapSpan = undefined; lastEncodedNameIndex = undefined; sourceMapData = undefined; + disableDepth = 0; + } + + function enable() { + if (disableDepth > 0) { + disableDepth--; + } + } + + function disable() { + disableDepth++; } function updateLastEncodedAndRecordedSpans() { @@ -168,7 +189,7 @@ namespace ts { sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] : defaultLastEncodedSourceMapSpan; - // TODO: Update lastEncodedNameIndex + // TODO: Update lastEncodedNameIndex // Since we dont support this any more, lets not worry about it right now. // When we start supporting nameIndex, we will get back to this @@ -236,7 +257,7 @@ namespace ts { } function emitPos(pos: number) { - if (pos === -1) { + if (positionIsSynthesized(pos) || disableDepth > 0) { return; } @@ -288,9 +309,17 @@ namespace ts { function emitStart(range: TextRange) { emitPos(getStartPos(range)); + + if (range.disableSourceMap) { + disable(); + } } function emitEnd(range: TextRange, stopOverridingEnd?: boolean) { + if (range.disableSourceMap) { + enable(); + } + emitPos(range.end); stopOverridingSpan = stopOverridingEnd; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1422b611651..2348c4d2b03 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -20,6 +20,7 @@ namespace ts { export interface TextRange { pos: number; end: number; + /* @internal */ disableSourceMap?: boolean; // Whether a synthesized text range disables source maps for its contents (used by transforms). } // token > SyntaxKind.Identifer => token is a keyword @@ -2468,6 +2469,7 @@ namespace ts { allowSyntheticDefaultImports?: boolean; allowJs?: boolean; /* @internal */ stripInternal?: boolean; + /* @internal */ experimentalTransforms?: boolean; // Skip checking lib.d.ts to help speed up tests. /* @internal */ skipDefaultLibCheck?: boolean; @@ -2793,14 +2795,16 @@ namespace ts { /* @internal */ export const enum NodeEmitFlags { - EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. - EmitExportStar = 1 << 1, // The export * helper should be written to this node. - EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. - EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods. - UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. - NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node, this is primarily used when printing a SystemJS module. - SingleLine = 1 << 6, // The contents of this node should be emit on a single line. - AdviseOnEmitNode = 1 << 7, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. + EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. + EmitExportStar = 1 << 1, // The export * helper should be written to this node. + EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. + EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods. + UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper. + NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node, this is primarily used when printing a SystemJS module. + SingleLine = 1 << 6, // The contents of this node should be emit on a single line. + AdviseOnEmitNode = 1 << 7, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. + IsNotEmittedNode = 1 << 8, // Is a node that is not emitted but whose comments should be preserved if possible. + EmitCommentsOfNotEmittedParent = 1 << 8, // Emits comments of missing parent nodes. } /** Additional context provided to `visitEachChild` */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 003b12280d3..10b5b467158 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2290,26 +2290,33 @@ namespace ts { } } - export function emitComments(text: string, lineMap: number[], writer: EmitTextWriter, comments: CommentRange[], trailingSeparator: boolean, newLine: string, + export function emitComments(text: string, lineMap: number[], writer: EmitTextWriter, comments: CommentRange[], leadingSeparator: boolean, trailingSeparator: boolean, newLine: string, writeComment: (text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) => void) { - let emitLeadingSpace = !trailingSeparator; - forEach(comments, comment => { - if (emitLeadingSpace) { - writer.write(" "); - emitLeadingSpace = false; - } - writeComment(text, lineMap, writer, comment, newLine); - if (comment.hasTrailingNewLine) { - writer.writeLine(); - } - else if (trailingSeparator) { + if (comments && comments.length > 0) { + if (leadingSeparator) { writer.write(" "); } - else { - // Emit leading space to separate comment during next comment emit - emitLeadingSpace = true; + + let emitInterveningSeperator = false; + for (const comment of comments) { + if (emitInterveningSeperator) { + writer.write(" "); + emitInterveningSeperator = false; + } + + writeComment(text, lineMap, writer, comment, newLine); + if (comment.hasTrailingNewLine) { + writer.writeLine(); + } + else { + emitInterveningSeperator = true; + } } - }); + + if (emitInterveningSeperator && trailingSeparator) { + writer.write(" "); + } + } } /** @@ -2366,7 +2373,7 @@ namespace ts { if (nodeLine >= lastCommentLine + 2) { // Valid detachedComments emitNewLineBeforeLeadingComments(lineMap, writer, node, leadingComments); - emitComments(text, lineMap, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(text, lineMap, writer, detachedComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment); currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end }; } } @@ -2705,6 +2712,21 @@ namespace ts { return carriageReturnLineFeed; } + export const enum TextRangeCollapse { + CollapseToStart, + CollapseToEnd, + } + + export function collapseTextRange(range: TextRange, collapse: TextRangeCollapse) { + if (range.pos === range.end) { + return range; + } + + return collapse === TextRangeCollapse.CollapseToStart + ? { pos: range.pos, end: range.end } + : { pos: range.end, end: range.end }; + } + // Node tests // // All node tests in the following list should *not* reference parent pointers so that From f52a30be1bcc5c3cbce6b1f99b9bfefe0ea8b93a Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 16:11:31 -0800 Subject: [PATCH 8/9] Merged some changes from other branches. --- src/compiler/commandLineParser.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index d5bf95a6405..c65466f00de 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -320,6 +320,11 @@ namespace ts { name: "allowSyntheticDefaultImports", type: "boolean", description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking + }, + { + name: "experimentalTransforms", + type: "boolean", + experimental: true } ]; From 64e7aa8345f2fd394cc14df93c493fd6b4b95221 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 22 Feb 2016 16:26:57 -0800 Subject: [PATCH 9/9] PR Feedback. --- src/compiler/printer.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index c6901244186..a1d284dd42b 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -215,7 +215,7 @@ const _super = (function (geti, seti) { onAfterEmitNode = undefined; isUniqueName = undefined; temporaryVariables = undefined; - tempFlags = 0; + tempFlags = TempFlags.Auto; currentSourceFile = undefined; currentText = undefined; extendsEmitted = false; @@ -1837,7 +1837,11 @@ const _super = (function (geti, seti) { } } - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean) { + /** + * Emits any prologue directives at the start of a Statement list, returning the + * number of prologue directives written to the output. + */ + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean): number { for (let i = 0; i < statements.length; i++) { if (isPrologueDirective(statements[i])) { if (startWithNewLine || i > 0) {