diff --git a/scripts/perf-result-post.js b/scripts/perf-result-post.js index 79d90d40c5aab..337729128d261 100644 --- a/scripts/perf-result-post.js +++ b/scripts/perf-result-post.js @@ -3,45 +3,86 @@ // Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally const { Octokit } = require("@octokit/rest"); const fs = require("fs"); +const ado = require("azure-devops-node-api"); +const { default: fetch } = require("node-fetch"); -const requester = process.env.requesting_user; -const source = process.env.source_issue; -const postedComment = process.env.status_comment; -console.log(`Loading fragment from ${process.argv[3]}...`); -const outputTableText = fs.readFileSync(process.argv[3], { encoding: "utf8" }); -console.log(`Fragment contents: -${outputTableText}`); -const gh = new Octokit({ - auth: process.argv[2] -}); -gh.issues.createComment({ - issue_number: +source, - owner: "Microsoft", - repo: "TypeScript", - body: `@${requester} -The results of the perf run you requested are in! -
Here they are:

-${outputTableText} -

` -}).then(async data => { - console.log(`Results posted!`); - const newCommentUrl = data.data.html_url; - const comment = await gh.issues.getComment({ - owner: "Microsoft", - repo: "TypeScript", - comment_id: +postedComment - }); - const newBody = `${comment.data.body} - -Update: [The results are in!](${newCommentUrl})`; - return await gh.issues.updateComment({ - owner: "Microsoft", - repo: "TypeScript", - comment_id: +postedComment, - body: newBody - }); -}).catch(e => { +async function main() { + const source = process.env.SOURCE_ISSUE; + if (!source) throw new Error("SOURCE_ISSUE environment variable not set."); + + const requester = process.env.REQUESTING_USER; + if (!requester) throw new Error("REQUESTING_USER environment variable not set."); + + const buildId = process.env.BUILD_BUILDID; + if (!requester) throw new Error("BUILD_BUILDID environment variable not set."); + + const postedComment = process.env.STATUS_COMMENT; + if (!postedComment) throw new Error("STATUS_COMMENT environment variable not set."); + + const [auth, fragment, includeArtifact] = process.argv.slice(2); + if (!auth) throw new Error("First argument must be a GitHub auth token."); + if (!fragment) throw new Error("Second argument must be a path to an HTML fragment."); + + const gh = new Octokit({ auth }); + try { + console.log(`Loading fragment from ${fragment}...`); + const outputTableText = fs.readFileSync(fragment, { encoding: "utf8" }); + console.log(`Fragment contents:\n${outputTableText}`); + + let benchmarkText = ""; + if (includeArtifact === "--include-artifact") { + // post a link to the benchmark file + const cli = new ado.WebApi("https://typescript.visualstudio.com/defaultcollection", ado.getHandlerFromToken("")); // Empty token, anon auth + const build = await cli.getBuildApi(); + const artifact = await build.getArtifact("typescript", +buildId, "benchmark"); + const updatedUrl = new URL(artifact.resource.url); + updatedUrl.search = `artifactName=benchmark&fileId=${artifact.resource.data}&fileName=manifest`; + const resp = await (await fetch(`${updatedUrl}`)).json(); + for (const file of resp.items) { + if (/[\\/]linux\.benchmark$/.test(file.path)) { + const benchmarkUrl = new URL(artifact.resource.url); + benchmarkUrl.search = `artifactName=benchmark&fileId=${file.blob.id}&fileName=linux.benchmark`; + benchmarkText = `\n
Developer Information:

Download Benchmark

\n`; + break; + } + } + } + + const data = await gh.issues.createComment({ + issue_number: +source, + owner: "Microsoft", + repo: "TypeScript", + body: `@${requester}\nThe results of the perf run you requested are in!\n
Here they are:

\n${outputTableText}\n

${benchmarkText}
` + }); + + console.log(`Results posted!`); + const newCommentUrl = data.data.html_url; + const comment = await gh.issues.getComment({ + owner: "Microsoft", + repo: "TypeScript", + comment_id: +postedComment + }); + const newBody = `${comment.data.body}\n\nUpdate: [The results are in!](${newCommentUrl})`; + await gh.issues.updateComment({ + owner: "Microsoft", + repo: "TypeScript", + comment_id: +postedComment, + body: newBody + }); + } + catch (e) { + const gh = new Octokit({ auth }); + await gh.issues.createComment({ + issue_number: +source, + owner: "Microsoft", + repo: "TypeScript", + body: `Hey @${requester}, something went wrong when publishing results. ([You can check the log here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${buildId}&_a=summary)).` + }); + } +} + +main().catch(e => { console.error(e); process.exit(1); }); diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 365a3d36515af..9323ee4d07ba1 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -227,6 +227,7 @@ namespace ts { const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; + const bindBinaryExpressionFlow = createBindBinaryExpressionFlow(); /** * Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file) @@ -1500,132 +1501,110 @@ namespace ts { bindAssignmentTargetFlow(node.left); } - const enum BindBinaryExpressionFlowState { - BindThenBindChildren, - MaybeBindLeft, - BindToken, - BindRight, - FinishBind - } - - function bindBinaryExpressionFlow(node: BinaryExpression) { - const workStacks: { - expr: BinaryExpression[], - state: BindBinaryExpressionFlowState[], - inStrictMode: (boolean | undefined)[], - parent: (Node | undefined)[], - } = { - expr: [node], - state: [BindBinaryExpressionFlowState.MaybeBindLeft], - inStrictMode: [undefined], - parent: [undefined], - }; - let stackIndex = 0; - while (stackIndex >= 0) { - node = workStacks.expr[stackIndex]; - switch (workStacks.state[stackIndex]) { - case BindBinaryExpressionFlowState.BindThenBindChildren: { - // This state is used only when recuring, to emulate the work that `bind` does before - // reaching `bindChildren`. A normal call to `bindBinaryExpressionFlow` will already have done this work. - setParent(node, parent); - const saveInStrictMode = inStrictMode; - bindWorker(node); - const saveParent = parent; - parent = node; - - advanceState(BindBinaryExpressionFlowState.MaybeBindLeft, saveInStrictMode, saveParent); - break; - } - case BindBinaryExpressionFlowState.MaybeBindLeft: { - const operator = node.operatorToken.kind; - // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions - // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too - // For now, though, since the common cases are chained `+`, leaving it recursive is fine - if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken || - isLogicalOrCoalescingAssignmentOperator(operator)) { - if (isTopLevelLogicalExpression(node)) { - const postExpressionLabel = createBranchLabel(); - bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel); - currentFlow = finishFlowLabel(postExpressionLabel); - } - else { - bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!); - } - completeNode(); - } - else { - advanceState(BindBinaryExpressionFlowState.BindToken); - maybeBind(node.left); - } - break; - } - case BindBinaryExpressionFlowState.BindToken: { - if (node.operatorToken.kind === SyntaxKind.CommaToken) { - maybeBindExpressionFlowIfCall(node.left); - } - advanceState(BindBinaryExpressionFlowState.BindRight); - maybeBind(node.operatorToken); - break; - } - case BindBinaryExpressionFlowState.BindRight: { - advanceState(BindBinaryExpressionFlowState.FinishBind); - maybeBind(node.right); - break; + function createBindBinaryExpressionFlow() { + interface WorkArea { + stackIndex: number; + skip: boolean; + inStrictModeStack: (boolean | undefined)[]; + parentStack: (Node | undefined)[]; + } + + return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined); + + function onEnter(node: BinaryExpression, state: WorkArea | undefined) { + if (state) { + state.stackIndex++; + // Emulate the work that `bind` does before reaching `bindChildren`. A normal call to + // `bindBinaryExpressionFlow` will already have done this work. + setParent(node, parent); + const saveInStrictMode = inStrictMode; + bindWorker(node); + const saveParent = parent; + parent = node; + state.skip = false; + state.inStrictModeStack[state.stackIndex] = saveInStrictMode; + state.parentStack[state.stackIndex] = saveParent; + } + else { + state = { + stackIndex: 0, + skip: false, + inStrictModeStack: [undefined], + parentStack: [undefined] + }; + } + // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions + // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too + // For now, though, since the common cases are chained `+`, leaving it recursive is fine + const operator = node.operatorToken.kind; + if (operator === SyntaxKind.AmpersandAmpersandToken || + operator === SyntaxKind.BarBarToken || + operator === SyntaxKind.QuestionQuestionToken || + isLogicalOrCoalescingAssignmentOperator(operator)) { + if (isTopLevelLogicalExpression(node)) { + const postExpressionLabel = createBranchLabel(); + bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel); + currentFlow = finishFlowLabel(postExpressionLabel); } - case BindBinaryExpressionFlowState.FinishBind: { - const operator = node.operatorToken.kind; - if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) { - bindAssignmentTargetFlow(node.left); - if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) { - const elementAccess = node.left; - if (isNarrowableOperand(elementAccess.expression)) { - currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node); - } - } - } - completeNode(); - break; + else { + bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!); } - default: return Debug.fail(`Invalid state ${workStacks.state[stackIndex]} for bindBinaryExpressionFlow`); + state.skip = true; } + return state; } - /** - * Note that `advanceState` sets the _current_ head state, and that `maybeBind` potentially pushes on a new - * head state; so `advanceState` must be called before any `maybeBind` during a state's execution. - */ - function advanceState(state: BindBinaryExpressionFlowState, isInStrictMode?: boolean, parent?: Node) { - workStacks.state[stackIndex] = state; - if (isInStrictMode !== undefined) { - workStacks.inStrictMode[stackIndex] = isInStrictMode; + function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) { + if (!state.skip) { + return maybeBind(left); } - if (parent !== undefined) { - workStacks.parent[stackIndex] = parent; + } + + function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, node: BinaryExpression) { + if (!state.skip) { + if (operatorToken.kind === SyntaxKind.CommaToken) { + maybeBindExpressionFlowIfCall(node.left); + } + bind(operatorToken); } } - function completeNode() { - if (workStacks.inStrictMode[stackIndex] !== undefined) { - inStrictMode = workStacks.inStrictMode[stackIndex]!; - parent = workStacks.parent[stackIndex]!; + function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) { + if (!state.skip) { + return maybeBind(right); } - stackIndex--; } - /** - * If `node` is a BinaryExpression, adds it to the local work stack, otherwise recursively binds it - */ + function onExit(node: BinaryExpression, state: WorkArea) { + if (!state.skip) { + const operator = node.operatorToken.kind; + if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) { + bindAssignmentTargetFlow(node.left); + if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) { + const elementAccess = node.left; + if (isNarrowableOperand(elementAccess.expression)) { + currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node); + } + } + } + } + const savedInStrictMode = state.inStrictModeStack[state.stackIndex]; + const savedParent = state.parentStack[state.stackIndex]; + if (savedInStrictMode !== undefined) { + inStrictMode = savedInStrictMode; + } + if (savedParent !== undefined) { + parent = savedParent; + } + state.skip = false; + state.stackIndex--; + } + function maybeBind(node: Node) { if (node && isBinaryExpression(node) && !isDestructuringAssignment(node)) { - stackIndex++; - workStacks.expr[stackIndex] = node; - workStacks.state[stackIndex] = BindBinaryExpressionFlowState.BindThenBindChildren; - workStacks.inStrictMode[stackIndex] = undefined; - workStacks.parent[stackIndex] = undefined; - } - else { - bind(node); + return node; } + bind(node); } } diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 8d14c9c1ee5d7..adf968fe2c7bd 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -275,6 +275,14 @@ namespace ts { } } + /** + * Asserts a value has the specified type in typespace only (does not perform a runtime assertion). + * This is useful in cases where we switch on `node.kind` and can be reasonably sure the type is accurate, and + * as a result can reduce the number of unnecessary casts. + */ + export function type(value: unknown): asserts value is T; + export function type(_value: unknown) { } + export function getFunctionName(func: AnyFunction) { if (typeof func !== "function") { return ""; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 24174483fdc6d..d7086a4193343 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1,6 +1,5 @@ namespace ts { const brackets = createBracketsMap(); - const syntheticParent: TextRange = { pos: -1, end: -1 }; /*@internal*/ export function isBuildInfoFile(file: string) { @@ -861,20 +860,11 @@ namespace ts { return outputFiles; } - const enum PipelinePhase { - Notification, - Substitution, - Comments, - SourceMaps, - Emit - } - export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { const { hasGlobalName, - onEmitNode = noEmitNotification, - isEmitNotificationEnabled, - substituteNode = noEmitSubstitution, + onBeforeEmitNode, + onAfterEmitNode, onBeforeEmitNodeArray, onAfterEmitNodeArray, onBeforeEmitToken, @@ -923,9 +913,9 @@ namespace ts { let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[] | undefined; let hasWrittenComment = false; let commentsDisabled = !!printerOptions.removeComments; - let lastNode: Node | undefined; - let lastSubstitution: Node | undefined; const { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); + const preprint = createPreprinter(handlers); + const emitBinaryExpression = createEmitBinaryExpression(); reset(); return { @@ -1003,7 +993,7 @@ namespace ts { if (sourceFile) { setSourceFile(sourceFile); } - emitList(syntheticParent, nodes, format); + emitList(/*parentNode*/ undefined, nodes, format); reset(); writer = previousWriter; } @@ -1147,7 +1137,7 @@ namespace ts { setSourceFile(sourceFile); } - pipelineEmit(hint, node); + emit(preprint(hint, node)); } function setSourceFile(sourceFile: SourceFile | undefined) { @@ -1179,8 +1169,6 @@ namespace ts { currentSourceFile = undefined!; currentLineMap = undefined!; detachedCommentsInfo = undefined; - lastNode = undefined; - lastSubstitution = undefined; setWriter(/*output*/ undefined, /*_sourceMapGenerator*/ undefined); } @@ -1188,678 +1176,681 @@ namespace ts { return currentLineMap || (currentLineMap = getLineStarts(currentSourceFile!)); } - function emit(node: Node): Node; - function emit(node: Node | undefined): Node | undefined; + function emit(node: Node): void; + function emit(node: Node | undefined): void; function emit(node: Node | undefined) { if (node === undefined) return; - const prevSourceFileTextKind = recordBundleFileInternalSectionStart(node); - const substitute = pipelineEmit(EmitHint.Unspecified, node); + + emitWithContext(node, emitWorker); + recordBundleFileInternalSectionEnd(prevSourceFileTextKind); - return substitute; } - function emitIdentifierName(node: Identifier): Node; - function emitIdentifierName(node: Identifier | undefined): Node | undefined; - function emitIdentifierName(node: Identifier | undefined): Node | undefined { - if (node === undefined) return; - return pipelineEmit(EmitHint.IdentifierName, node); + function shouldEmitComments(node: Node) { + return !commentsDisabled && !isSourceFile(node); } - function emitExpression(node: Expression): Node; - function emitExpression(node: Expression | undefined): Node | undefined; - function emitExpression(node: Expression | undefined): Node | undefined { - if (node === undefined) return; - return pipelineEmit(EmitHint.Expression, node); + function shouldEmitSourceMaps(node: Node) { + return !sourceMapsDisabled && + !isSourceFile(node) && + !isInJsonFile(node) && + !isUnparsedSource(node) && + !isUnparsedPrepend(node); } - function emitJsxAttributeValue(node: StringLiteral | JsxExpression): Node { - return pipelineEmit(isStringLiteral(node) ? EmitHint.JsxAttributeValue : EmitHint.Unspecified, node); - } + function beforeEmitWithContext(node: Node, shouldEmitComments: boolean, shouldEmitSourceMaps: boolean) { + onBeforeEmitNode?.(node); - function pipelineEmit(emitHint: EmitHint, node: Node) { - const savedLastNode = lastNode; - const savedLastSubstitution = lastSubstitution; - const savedPreserveSourceNewlines = preserveSourceNewlines; - lastNode = node; - lastSubstitution = undefined; - if (preserveSourceNewlines && !!(getEmitFlags(node) & EmitFlags.IgnoreSourceNewlines)) { + const emitFlags = getEmitFlags(node); + const commentRange = shouldEmitComments ? getCommentRange(node) : undefined; + const sourceMapRange = shouldEmitSourceMaps ? getSourceMapRange(node) : undefined; + + if (preserveSourceNewlines && (emitFlags & EmitFlags.IgnoreSourceNewlines)) { preserveSourceNewlines = false; } - const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, emitHint, node); - pipelinePhase(emitHint, node); + // Emit leading comments + if (commentRange) { + emitLeadingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end); + if (emitFlags & EmitFlags.NoNestedComments) { + commentsDisabled = true; + } + } + + // Emit leading sourcemap + if (sourceMapRange) { + if (isUnparsedNode(node)) { + Debug.assertIsDefined(node.parent, "UnparsedNodes must have parent pointers"); + const parsed = getParsedSourceMap(node.parent); + if (parsed && sourceMapGenerator) { + sourceMapGenerator.appendSourceMap( + writer.getLine(), + writer.getColumn(), + parsed, + node.parent.sourceMapPath!, + node.parent.getLineAndCharacterOfPosition(node.pos), + node.parent.getLineAndCharacterOfPosition(node.end) + ); + } + } + else { + const source = sourceMapRange.source || sourceMapSource; + if (node.kind !== SyntaxKind.NotEmittedStatement + && (emitFlags & EmitFlags.NoLeadingSourceMap) === 0 + && sourceMapRange.pos >= 0) { + emitSourcePos(sourceMapRange.source || sourceMapSource, skipSourceTrivia(source, sourceMapRange.pos)); + } + if (emitFlags & EmitFlags.NoNestedSourceMaps) { + sourceMapsDisabled = true; + } + } + } + } + + function afterEmitWithContext(node: Node, shouldEmitComments: boolean, shouldEmitSourceMaps: boolean, savedContainerPos: number, savedContainerEnd: number, savedDeclarationListContainerEnd: number, savedPreserveSourceNewlines: boolean | undefined) { + const emitFlags = getEmitFlags(node); + const commentRange = shouldEmitComments ? getCommentRange(node) : undefined; + const sourceMapRange = shouldEmitSourceMaps ? getSourceMapRange(node) : undefined; + + // Emit trailing sourcemap + if (sourceMapRange) { + if (!isUnparsedNode(node)) { + if (emitFlags & EmitFlags.NoNestedSourceMaps) { + sourceMapsDisabled = false; + } + if (node.kind !== SyntaxKind.NotEmittedStatement + && (emitFlags & EmitFlags.NoTrailingSourceMap) === 0 + && sourceMapRange.end >= 0) { + emitSourcePos(sourceMapRange.source || sourceMapSource, sourceMapRange.end); + } + } + } - Debug.assert(lastNode === node); + // Emit trailing comments + if (commentRange) { + if (emitFlags & EmitFlags.NoNestedComments) { + commentsDisabled = false; + } + emitTrailingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd); + } - const substitute = lastSubstitution; - lastNode = savedLastNode; - lastSubstitution = savedLastSubstitution; preserveSourceNewlines = savedPreserveSourceNewlines; + onAfterEmitNode?.(node); + } - return substitute || node; + function emitWithContext(node: T, emitCallback: (node: T) => void) { + const savedPreserveSourceNewlines = preserveSourceNewlines; + const savedContainerPos = containerPos; + const savedContainerEnd = containerEnd; + const savedDeclarationListContainerEnd = declarationListContainerEnd; + const emitComments = shouldEmitComments(node); + const emitSourceMaps = shouldEmitSourceMaps(node); + beforeEmitWithContext(node, emitComments, emitSourceMaps); + emitCallback(node); + afterEmitWithContext(node, emitComments, emitSourceMaps, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd, savedPreserveSourceNewlines); } - function getPipelinePhase(phase: PipelinePhase, emitHint: EmitHint, node: Node) { - switch (phase) { - case PipelinePhase.Notification: - if (onEmitNode !== noEmitNotification && (!isEmitNotificationEnabled || isEmitNotificationEnabled(node))) { - return pipelineEmitWithNotification; - } - // falls through + function emitWorker(node: Node): void { + switch (node.kind) { + // Literals + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + return emitNumericOrBigIntLiteral(node); + + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return emitLiteral(node, /*jsxAttributeEscape*/ false); + + case SyntaxKind.JsxText: + return emitJsxText(node); + + // Pseudo-literals + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return emitLiteral(node, /*jsxAttributeEscape*/ false); + + // Identifiers and PrivateIdentifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); + case SyntaxKind.PrivateIdentifier: + return emitPrivateIdentifier(node as PrivateIdentifier); - case PipelinePhase.Substitution: - if (substituteNode !== noEmitSubstitution && (lastSubstitution = substituteNode(emitHint, node)) !== node) { - return pipelineEmitWithSubstitution; - } - // falls through + // Names + case SyntaxKind.QualifiedName: + return emitQualifiedName(node); + case SyntaxKind.ComputedPropertyName: + return emitComputedPropertyName(node); - case PipelinePhase.Comments: - if (!commentsDisabled && node.kind !== SyntaxKind.SourceFile) { - return pipelineEmitWithComments; - } - // falls through + // Signature elements + case SyntaxKind.TypeParameter: + return emitTypeParameter(node); + case SyntaxKind.Parameter: + return emitParameter(node); + case SyntaxKind.Decorator: + return emitDecorator(node); - case PipelinePhase.SourceMaps: - if (!sourceMapsDisabled && node.kind !== SyntaxKind.SourceFile && !isInJsonFile(node)) { - return pipelineEmitWithSourceMap; - } - // falls through + // Type members + case SyntaxKind.PropertySignature: + return emitPropertySignature(node); + case SyntaxKind.PropertyDeclaration: + return emitPropertyDeclaration(node); + case SyntaxKind.MethodSignature: + return emitMethodSignature(node); + case SyntaxKind.MethodDeclaration: + return emitMethodDeclaration(node); + case SyntaxKind.Constructor: + return emitConstructor(node); + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return emitAccessorDeclaration(node); + case SyntaxKind.CallSignature: + return emitCallSignature(node); + case SyntaxKind.ConstructSignature: + return emitConstructSignature(node); + case SyntaxKind.IndexSignature: + return emitIndexSignature(node); + + // Types + case SyntaxKind.TypePredicate: + return emitTypePredicate(node); + case SyntaxKind.TypeReference: + return emitTypeReference(node); + case SyntaxKind.FunctionType: + return emitFunctionType(node); + case SyntaxKind.ConstructorType: + return emitConstructorType(node); + case SyntaxKind.TypeQuery: + return emitTypeQuery(node); + case SyntaxKind.TypeLiteral: + return emitTypeLiteral(node); + case SyntaxKind.ArrayType: + return emitArrayType(node); + case SyntaxKind.TupleType: + return emitTupleType(node); + case SyntaxKind.OptionalType: + return emitOptionalType(node); + // SyntaxKind.RestType is handled below + case SyntaxKind.UnionType: + return emitUnionType(node); + case SyntaxKind.IntersectionType: + return emitIntersectionType(node); + case SyntaxKind.ConditionalType: + return emitConditionalType(node); + case SyntaxKind.InferType: + return emitInferType(node); + case SyntaxKind.ParenthesizedType: + return emitParenthesizedType(node); + case SyntaxKind.ThisType: + return emitThisType(); + case SyntaxKind.TypeOperator: + return emitTypeOperator(node); + case SyntaxKind.IndexedAccessType: + return emitIndexedAccessType(node); + case SyntaxKind.MappedType: + return emitMappedType(node); + case SyntaxKind.LiteralType: + return emitLiteralType(node); + case SyntaxKind.NamedTupleMember: + return emitNamedTupleMember(node as NamedTupleMember); + case SyntaxKind.TemplateLiteralType: + return emitTemplateType(node); + case SyntaxKind.TemplateLiteralTypeSpan: + return emitTemplateTypeSpan(node); + case SyntaxKind.ImportType: + return emitImportTypeNode(node); + + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + return emitObjectBindingPattern(node); + case SyntaxKind.ArrayBindingPattern: + return emitArrayBindingPattern(node); + case SyntaxKind.BindingElement: + return emitBindingElement(node); + + // Expressions + case SyntaxKind.ArrayLiteralExpression: + return emitArrayLiteralExpression(node); + case SyntaxKind.ObjectLiteralExpression: + return emitObjectLiteralExpression(node); + case SyntaxKind.PropertyAccessExpression: + return emitPropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return emitElementAccessExpression(node); + case SyntaxKind.CallExpression: + return emitCallExpression(node); + case SyntaxKind.NewExpression: + return emitNewExpression(node); + case SyntaxKind.TaggedTemplateExpression: + return emitTaggedTemplateExpression(node); + case SyntaxKind.TypeAssertionExpression: + return emitTypeAssertionExpression(node); + case SyntaxKind.ParenthesizedExpression: + return emitParenthesizedExpression(node); + case SyntaxKind.FunctionExpression: + return emitFunctionExpression(node); + case SyntaxKind.ArrowFunction: + return emitArrowFunction(node); + case SyntaxKind.DeleteExpression: + return emitDeleteExpression(node); + case SyntaxKind.TypeOfExpression: + return emitTypeOfExpression(node); + case SyntaxKind.VoidExpression: + return emitVoidExpression(node); + case SyntaxKind.AwaitExpression: + return emitAwaitExpression(node); + case SyntaxKind.PrefixUnaryExpression: + return emitPrefixUnaryExpression(node); + case SyntaxKind.PostfixUnaryExpression: + return emitPostfixUnaryExpression(node); + case SyntaxKind.BinaryExpression: + return emitBinaryExpression(node); + case SyntaxKind.ConditionalExpression: + return emitConditionalExpression(node); + case SyntaxKind.TemplateExpression: + return emitTemplateExpression(node); + case SyntaxKind.YieldExpression: + return emitYieldExpression(node); + case SyntaxKind.SpreadElement: + return emitSpreadExpression(node); + case SyntaxKind.ClassExpression: + return emitClassExpression(node); + case SyntaxKind.OmittedExpression: + return; + case SyntaxKind.ExpressionWithTypeArguments: + return emitExpressionWithTypeArguments(node); + case SyntaxKind.AsExpression: + return emitAsExpression(node); + case SyntaxKind.NonNullExpression: + return emitNonNullExpression(node); + case SyntaxKind.MetaProperty: + return emitMetaProperty(node); + case SyntaxKind.SyntheticExpression: + Debug.fail("SyntheticExpression should never be printed."); + break; - case PipelinePhase.Emit: - return pipelineEmitWithHint; + // Misc + case SyntaxKind.TemplateSpan: + return emitTemplateSpan(node); + case SyntaxKind.SemicolonClassElement: + return emitSemicolonClassElement(); - default: - return Debug.assertNever(phase); + // Element + case SyntaxKind.Block: + return emitBlock(node); + case SyntaxKind.EmptyStatement: + return emitEmptyStatement(/*isEmbeddedStatement*/ false); + case SyntaxKind.VariableStatement: + return emitVariableStatement(node); + case SyntaxKind.ExpressionStatement: + return emitExpressionStatement(node); + case SyntaxKind.IfStatement: + return emitIfStatement(node); + case SyntaxKind.DoStatement: + return emitDoStatement(node); + case SyntaxKind.WhileStatement: + return emitWhileStatement(node); + case SyntaxKind.ForStatement: + return emitForStatement(node); + case SyntaxKind.ForInStatement: + return emitForInStatement(node); + case SyntaxKind.ForOfStatement: + return emitForOfStatement(node); + case SyntaxKind.ContinueStatement: + return emitContinueStatement(node); + case SyntaxKind.BreakStatement: + return emitBreakStatement(node); + case SyntaxKind.ReturnStatement: + return emitReturnStatement(node); + case SyntaxKind.WithStatement: + return emitWithStatement(node); + case SyntaxKind.SwitchStatement: + return emitSwitchStatement(node); + case SyntaxKind.LabeledStatement: + return emitLabeledStatement(node); + case SyntaxKind.ThrowStatement: + return emitThrowStatement(node); + case SyntaxKind.TryStatement: + return emitTryStatement(node); + case SyntaxKind.DebuggerStatement: + return emitDebuggerStatement(node); + + // Declarations + case SyntaxKind.VariableDeclaration: + return emitVariableDeclaration(node); + case SyntaxKind.VariableDeclarationList: + return emitVariableDeclarationList(node); + case SyntaxKind.FunctionDeclaration: + return emitFunctionDeclaration(node); + case SyntaxKind.ClassDeclaration: + return emitClassDeclaration(node); + case SyntaxKind.InterfaceDeclaration: + return emitInterfaceDeclaration(node); + case SyntaxKind.TypeAliasDeclaration: + return emitTypeAliasDeclaration(node); + case SyntaxKind.EnumDeclaration: + return emitEnumDeclaration(node); + case SyntaxKind.ModuleDeclaration: + return emitModuleDeclaration(node); + case SyntaxKind.ModuleBlock: + return emitModuleBlock(node); + case SyntaxKind.CaseBlock: + return emitCaseBlock(node); + case SyntaxKind.NamespaceExportDeclaration: + return emitNamespaceExportDeclaration(node); + case SyntaxKind.ImportEqualsDeclaration: + return emitImportEqualsDeclaration(node); + case SyntaxKind.ImportDeclaration: + return emitImportDeclaration(node); + case SyntaxKind.ImportClause: + return emitImportClause(node); + case SyntaxKind.NamespaceImport: + return emitNamespaceImport(node); + case SyntaxKind.NamedImports: + return emitNamedImports(node); + case SyntaxKind.ImportSpecifier: + return emitImportSpecifier(node); + case SyntaxKind.ExportAssignment: + return emitExportAssignment(node); + case SyntaxKind.ExportDeclaration: + return emitExportDeclaration(node); + case SyntaxKind.NamedExports: + return emitNamedExports(node); + case SyntaxKind.NamespaceExport: + return emitNamespaceExport(node); + case SyntaxKind.ExportSpecifier: + return emitExportSpecifier(node); + case SyntaxKind.MissingDeclaration: + return; + + // Module references + case SyntaxKind.ExternalModuleReference: + return emitExternalModuleReference(node); + + // JSX + case SyntaxKind.JsxElement: + return emitJsxElement(node); + case SyntaxKind.JsxSelfClosingElement: + return emitJsxSelfClosingElement(node); + case SyntaxKind.JsxFragment: + return emitJsxFragment(node); + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxOpeningFragment: + return emitJsxOpeningElementOrFragment(node); + case SyntaxKind.JsxClosingElement: + case SyntaxKind.JsxClosingFragment: + return emitJsxClosingElementOrFragment(node); + case SyntaxKind.JsxAttribute: + return emitJsxAttribute(node); + case SyntaxKind.JsxAttributes: + return emitJsxAttributes(node); + case SyntaxKind.JsxSpreadAttribute: + return emitJsxSpreadAttribute(node); + case SyntaxKind.JsxExpression: + return emitJsxExpression(node); + + // Clauses + case SyntaxKind.CaseClause: + return emitCaseClause(node); + case SyntaxKind.DefaultClause: + return emitDefaultClause(node); + case SyntaxKind.HeritageClause: + return emitHeritageClause(node); + case SyntaxKind.CatchClause: + return emitCatchClause(node); + + // Property assignments + case SyntaxKind.PropertyAssignment: + return emitPropertyAssignment(node); + case SyntaxKind.ShorthandPropertyAssignment: + return emitShorthandPropertyAssignment(node); + case SyntaxKind.SpreadAssignment: + return emitSpreadAssignment(node as SpreadAssignment); + + // Enum + case SyntaxKind.EnumMember: + return emitEnumMember(node); + + // Unparsed + case SyntaxKind.UnparsedPrologue: + return writeUnparsedNode(node); + case SyntaxKind.UnparsedSource: + case SyntaxKind.UnparsedPrepend: + return emitUnparsedSourceOrPrepend(node); + case SyntaxKind.UnparsedText: + case SyntaxKind.UnparsedInternalText: + return emitUnparsedTextLike(node); + case SyntaxKind.UnparsedSyntheticReference: + return emitUnparsedSyntheticReference(node); + + // Top-level nodes + case SyntaxKind.SourceFile: + return emitSourceFile(node); + case SyntaxKind.Bundle: + Debug.fail("Bundles should be printed using printBundle"); + break; + // SyntaxKind.UnparsedSource (handled above) + case SyntaxKind.InputFiles: + Debug.fail("InputFiles should not be printed"); + break; + + // JSDoc nodes (only used in codefixes currently) + case SyntaxKind.JSDocTypeExpression: + return emitJSDocTypeExpression(node as JSDocTypeExpression); + case SyntaxKind.JSDocNameReference: + return emitJSDocNameReference(node as JSDocNameReference); + case SyntaxKind.JSDocAllType: + return writePunctuation("*"); + case SyntaxKind.JSDocUnknownType: + return writePunctuation("?"); + case SyntaxKind.JSDocNullableType: + return emitJSDocNullableType(node as JSDocNullableType); + case SyntaxKind.JSDocNonNullableType: + return emitJSDocNonNullableType(node as JSDocNonNullableType); + case SyntaxKind.JSDocOptionalType: + return emitJSDocOptionalType(node as JSDocOptionalType); + case SyntaxKind.JSDocFunctionType: + return emitJSDocFunctionType(node as JSDocFunctionType); + case SyntaxKind.RestType: + case SyntaxKind.JSDocVariadicType: + return emitRestOrJSDocVariadicType(node as RestTypeNode | JSDocVariadicType); + // SyntaxKind.JSDocNamepathType (missing) + case SyntaxKind.JSDocComment: + return emitJSDoc(node as JSDoc); + case SyntaxKind.JSDocTypeLiteral: + return emitJSDocTypeLiteral(node as JSDocTypeLiteral); + case SyntaxKind.JSDocSignature: + return emitJSDocSignature(node as JSDocSignature); + case SyntaxKind.JSDocTag: + case SyntaxKind.JSDocClassTag: + return emitJSDocSimpleTag(node as JSDocTag); + case SyntaxKind.JSDocAugmentsTag: + case SyntaxKind.JSDocImplementsTag: + return emitJSDocHeritageTag(node as JSDocImplementsTag | JSDocAugmentsTag); + // SyntaxKind.JSDocAuthorTag (missing) + // SyntaxKind.JSDocDeprecatedTag (missing) + // SyntaxKind.JSDocClassTag (see JSDocTag, above) + // SyntaxKind.JSDocPublicTag (missing) + // SyntaxKind.JSDocPrivateTag (missing) + // SyntaxKind.JSDocProtectedTag (missing) + // SyntaxKind.JSDocReadonlyTag (missing) + case SyntaxKind.JSDocCallbackTag: + return emitJSDocCallbackTag(node as JSDocCallbackTag); + // SyntaxKind.JSDocEnumTag (see below) + case SyntaxKind.JSDocParameterTag: + case SyntaxKind.JSDocPropertyTag: + return emitJSDocPropertyLikeTag(node as JSDocPropertyLikeTag); + case SyntaxKind.JSDocEnumTag: + case SyntaxKind.JSDocReturnTag: + case SyntaxKind.JSDocThisTag: + case SyntaxKind.JSDocTypeTag: + return emitJSDocSimpleTypedTag(node as JSDocTypeTag); + case SyntaxKind.JSDocTemplateTag: + return emitJSDocTemplateTag(node as JSDocTemplateTag); + case SyntaxKind.JSDocTypedefTag: + return emitJSDocTypedefTag(node as JSDocTypedefTag); + case SyntaxKind.JSDocSeeTag: + return emitJSDocSeeTag(node as JSDocSeeTag); + // SyntaxKind.JSDocPropertyTag (see JSDocParameterTag, above) + + // Synthesized list + case SyntaxKind.SyntaxList: + Debug.fail("SyntaxList should not be printed"); + break; + + // Transformation nodes + case SyntaxKind.NotEmittedStatement: + break; + case SyntaxKind.PartiallyEmittedExpression: + return emitPartiallyEmittedExpression(node); + case SyntaxKind.CommaListExpression: + return emitCommaList(node); + case SyntaxKind.MergeDeclarationMarker: + case SyntaxKind.EndOfDeclarationMarker: + break; + case SyntaxKind.SyntheticReferenceExpression: + Debug.fail("SyntheticReferenceExpression should not be printed"); } + if (isKeyword(node.kind)) return writeTokenNode(node, writeKeyword); + if (isTokenKind(node.kind)) return writeTokenNode(node, writePunctuation); } - function getNextPipelinePhase(currentPhase: PipelinePhase, emitHint: EmitHint, node: Node) { - return getPipelinePhase(currentPhase + 1, emitHint, node); + function emitMappedTypeParameter(node: TypeParameterDeclaration): void { + emit(node.name); + writeSpace(); + writeKeyword("in"); + writeSpace(); + emit(node.constraint); } - function pipelineEmitWithNotification(hint: EmitHint, node: Node) { - Debug.assert(lastNode === node); - const pipelinePhase = getNextPipelinePhase(PipelinePhase.Notification, hint, node); - onEmitNode(hint, node, pipelinePhase); - Debug.assert(lastNode === node); + function getHelpersFromBundledSourceFiles(bundle: Bundle): string[] | undefined { + let result: string[] | undefined; + if (moduleKind === ModuleKind.None || printerOptions.noEmitHelpers) { + return undefined; + } + const bundledHelpers = new Map(); + for (const sourceFile of bundle.sourceFiles) { + const shouldSkip = getExternalHelpersModuleName(sourceFile) !== undefined; + const helpers = getSortedEmitHelpers(sourceFile); + if (!helpers) continue; + for (const helper of helpers) { + if (!helper.scoped && !shouldSkip && !bundledHelpers.get(helper.name)) { + bundledHelpers.set(helper.name, true); + (result || (result = [])).push(helper.name); + } + } + } + + return result; } - function pipelineEmitWithHint(hint: EmitHint, node: Node): void { - Debug.assert(lastNode === node || lastSubstitution === node); - if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile)); - if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier)); - if (hint === EmitHint.JsxAttributeValue) return emitLiteral(cast(node, isStringLiteral), /*jsxAttributeEscape*/ true); - if (hint === EmitHint.MappedTypeParameter) return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); - if (hint === EmitHint.EmbeddedStatement) { - Debug.assertNode(node, isEmptyStatement); - return emitEmptyStatement(/*isEmbeddedStatement*/ true); + function emitHelpers(node: Node) { + let helpersEmitted = false; + const bundle = node.kind === SyntaxKind.Bundle ? node : undefined; + if (bundle && moduleKind === ModuleKind.None) { + return; } - if (hint === EmitHint.Unspecified) { - if (isKeyword(node.kind)) return writeTokenNode(node, writeKeyword); - - switch (node.kind) { - // Pseudo-literals - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - return emitLiteral(node, /*jsxAttributeEscape*/ false); + const numPrepends = bundle ? bundle.prepends.length : 0; + const numNodes = bundle ? bundle.sourceFiles.length + numPrepends : 1; + for (let i = 0; i < numNodes; i++) { + const currentNode = bundle ? i < numPrepends ? bundle.prepends[i] : bundle.sourceFiles[i - numPrepends] : node; + const sourceFile = isSourceFile(currentNode) ? currentNode : isUnparsedSource(currentNode) ? undefined : currentSourceFile!; + const shouldSkip = printerOptions.noEmitHelpers || (!!sourceFile && hasRecordedExternalHelpers(sourceFile)); + const shouldBundle = (isSourceFile(currentNode) || isUnparsedSource(currentNode)) && !isOwnFileEmit; + const helpers = isUnparsedSource(currentNode) ? currentNode.helpers : getSortedEmitHelpers(currentNode); + if (helpers) { + for (const helper of helpers) { + if (!helper.scoped) { + // Skip the helper if it can be skipped and the noEmitHelpers compiler + // option is set, or if it can be imported and the importHelpers compiler + // option is set. + if (shouldSkip) continue; - case SyntaxKind.UnparsedSource: - case SyntaxKind.UnparsedPrepend: - return emitUnparsedSourceOrPrepend(node); + // Skip the helper if it can be bundled but hasn't already been emitted and we + // are emitting a bundled module. + if (shouldBundle) { + if (bundledHelpers.get(helper.name)) { + continue; + } - case SyntaxKind.UnparsedPrologue: - return writeUnparsedNode(node); + bundledHelpers.set(helper.name, true); + } + } + else if (bundle) { + // Skip the helper if it is scoped and we are emitting bundled helpers + continue; + } + const pos = getTextPosWithWriteLine(); + if (typeof helper.text === "string") { + writeLines(helper.text); + } + else { + writeLines(helper.text(makeFileLevelOptimisticUniqueName)); + } + if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.EmitHelpers, data: helper.name }); + helpersEmitted = true; + } + } + } - case SyntaxKind.UnparsedText: - case SyntaxKind.UnparsedInternalText: - return emitUnparsedTextLike(node); + return helpersEmitted; + } - case SyntaxKind.UnparsedSyntheticReference: - return emitUnparsedSyntheticReference(node); + function getSortedEmitHelpers(node: Node) { + const helpers = getEmitHelpers(node); + return helpers && stableSort(helpers, compareEmitHelpers); + } + // + // Literals/Pseudo-literals + // - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); + // SyntaxKind.NumericLiteral + // SyntaxKind.BigIntLiteral + function emitNumericOrBigIntLiteral(node: NumericLiteral | BigIntLiteral) { + emitLiteral(node, /*jsxAttributeEscape*/ false); + } - // PrivateIdentifiers - case SyntaxKind.PrivateIdentifier: - return emitPrivateIdentifier(node as PrivateIdentifier); + // SyntaxKind.StringLiteral + // SyntaxKind.RegularExpressionLiteral + // SyntaxKind.NoSubstitutionTemplateLiteral + // SyntaxKind.TemplateHead + // SyntaxKind.TemplateMiddle + // SyntaxKind.TemplateTail + function emitLiteral(node: LiteralLikeNode, jsxAttributeEscape: boolean) { + const text = getLiteralTextOfNode(node, printerOptions.neverAsciiEscape, jsxAttributeEscape); + if ((printerOptions.sourceMap || printerOptions.inlineSourceMap) + && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { + writeLiteral(text); + } + else { + // Quick info expects all literals to be called with writeStringLiteral, as there's no specific type for numberLiterals + writeStringLiteral(text); + } + } - // Parse tree nodes - // Names - case SyntaxKind.QualifiedName: - return emitQualifiedName(node); - case SyntaxKind.ComputedPropertyName: - return emitComputedPropertyName(node); + function emitStringLiteralWithJsxAttributeEscape(node: StringLiteral) { + emitLiteral(node, /*jsxAttributeEscape*/ true); + } - // Signature elements - case SyntaxKind.TypeParameter: - return emitTypeParameter(node); - case SyntaxKind.Parameter: - return emitParameter(node); - case SyntaxKind.Decorator: - return emitDecorator(node); + // SyntaxKind.UnparsedSource + // SyntaxKind.UnparsedPrepend + function emitUnparsedSourceOrPrepend(unparsed: UnparsedSource | UnparsedPrepend) { + for (const text of unparsed.texts) { + writeLine(); + emit(text); + } + } - // Type members - case SyntaxKind.PropertySignature: - return emitPropertySignature(node); - case SyntaxKind.PropertyDeclaration: - return emitPropertyDeclaration(node); - case SyntaxKind.MethodSignature: - return emitMethodSignature(node); - case SyntaxKind.MethodDeclaration: - return emitMethodDeclaration(node); - case SyntaxKind.Constructor: - return emitConstructor(node); - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return emitAccessorDeclaration(node); - case SyntaxKind.CallSignature: - return emitCallSignature(node); - case SyntaxKind.ConstructSignature: - return emitConstructSignature(node); - case SyntaxKind.IndexSignature: - return emitIndexSignature(node); - case SyntaxKind.TemplateLiteralTypeSpan: - return emitTemplateTypeSpan(node); - - // Types - case SyntaxKind.TypePredicate: - return emitTypePredicate(node); - case SyntaxKind.TypeReference: - return emitTypeReference(node); - case SyntaxKind.FunctionType: - return emitFunctionType(node); - case SyntaxKind.JSDocFunctionType: - return emitJSDocFunctionType(node as JSDocFunctionType); - case SyntaxKind.ConstructorType: - return emitConstructorType(node); - case SyntaxKind.TypeQuery: - return emitTypeQuery(node); - case SyntaxKind.TypeLiteral: - return emitTypeLiteral(node); - case SyntaxKind.ArrayType: - return emitArrayType(node); - case SyntaxKind.TupleType: - return emitTupleType(node); - case SyntaxKind.OptionalType: - return emitOptionalType(node); - case SyntaxKind.UnionType: - return emitUnionType(node); - case SyntaxKind.IntersectionType: - return emitIntersectionType(node); - case SyntaxKind.ConditionalType: - return emitConditionalType(node); - case SyntaxKind.InferType: - return emitInferType(node); - case SyntaxKind.ParenthesizedType: - return emitParenthesizedType(node); - case SyntaxKind.ExpressionWithTypeArguments: - return emitExpressionWithTypeArguments(node); - case SyntaxKind.ThisType: - return emitThisType(); - case SyntaxKind.TypeOperator: - return emitTypeOperator(node); - case SyntaxKind.IndexedAccessType: - return emitIndexedAccessType(node); - case SyntaxKind.MappedType: - return emitMappedType(node); - case SyntaxKind.LiteralType: - return emitLiteralType(node); - case SyntaxKind.TemplateLiteralType: - return emitTemplateType(node); - case SyntaxKind.ImportType: - return emitImportTypeNode(node); - case SyntaxKind.JSDocAllType: - writePunctuation("*"); - return; - case SyntaxKind.JSDocUnknownType: - writePunctuation("?"); - return; - case SyntaxKind.JSDocNullableType: - return emitJSDocNullableType(node as JSDocNullableType); - case SyntaxKind.JSDocNonNullableType: - return emitJSDocNonNullableType(node as JSDocNonNullableType); - case SyntaxKind.JSDocOptionalType: - return emitJSDocOptionalType(node as JSDocOptionalType); - case SyntaxKind.RestType: - case SyntaxKind.JSDocVariadicType: - return emitRestOrJSDocVariadicType(node as RestTypeNode | JSDocVariadicType); - case SyntaxKind.NamedTupleMember: - return emitNamedTupleMember(node as NamedTupleMember); - - // Binding patterns - case SyntaxKind.ObjectBindingPattern: - return emitObjectBindingPattern(node); - case SyntaxKind.ArrayBindingPattern: - return emitArrayBindingPattern(node); - case SyntaxKind.BindingElement: - return emitBindingElement(node); - - // Misc - case SyntaxKind.TemplateSpan: - return emitTemplateSpan(node); - case SyntaxKind.SemicolonClassElement: - return emitSemicolonClassElement(); - - // Statements - case SyntaxKind.Block: - return emitBlock(node); - case SyntaxKind.VariableStatement: - return emitVariableStatement(node); - case SyntaxKind.EmptyStatement: - return emitEmptyStatement(/*isEmbeddedStatement*/ false); - case SyntaxKind.ExpressionStatement: - return emitExpressionStatement(node); - case SyntaxKind.IfStatement: - return emitIfStatement(node); - case SyntaxKind.DoStatement: - return emitDoStatement(node); - case SyntaxKind.WhileStatement: - return emitWhileStatement(node); - case SyntaxKind.ForStatement: - return emitForStatement(node); - case SyntaxKind.ForInStatement: - return emitForInStatement(node); - case SyntaxKind.ForOfStatement: - return emitForOfStatement(node); - case SyntaxKind.ContinueStatement: - return emitContinueStatement(node); - case SyntaxKind.BreakStatement: - return emitBreakStatement(node); - case SyntaxKind.ReturnStatement: - return emitReturnStatement(node); - case SyntaxKind.WithStatement: - return emitWithStatement(node); - case SyntaxKind.SwitchStatement: - return emitSwitchStatement(node); - case SyntaxKind.LabeledStatement: - return emitLabeledStatement(node); - case SyntaxKind.ThrowStatement: - return emitThrowStatement(node); - case SyntaxKind.TryStatement: - return emitTryStatement(node); - case SyntaxKind.DebuggerStatement: - return emitDebuggerStatement(node); - - // Declarations - case SyntaxKind.VariableDeclaration: - return emitVariableDeclaration(node); - case SyntaxKind.VariableDeclarationList: - return emitVariableDeclarationList(node); - case SyntaxKind.FunctionDeclaration: - return emitFunctionDeclaration(node); - case SyntaxKind.ClassDeclaration: - return emitClassDeclaration(node); - case SyntaxKind.InterfaceDeclaration: - return emitInterfaceDeclaration(node); - case SyntaxKind.TypeAliasDeclaration: - return emitTypeAliasDeclaration(node); - case SyntaxKind.EnumDeclaration: - return emitEnumDeclaration(node); - case SyntaxKind.ModuleDeclaration: - return emitModuleDeclaration(node); - case SyntaxKind.ModuleBlock: - return emitModuleBlock(node); - case SyntaxKind.CaseBlock: - return emitCaseBlock(node); - case SyntaxKind.NamespaceExportDeclaration: - return emitNamespaceExportDeclaration(node); - case SyntaxKind.ImportEqualsDeclaration: - return emitImportEqualsDeclaration(node); - case SyntaxKind.ImportDeclaration: - return emitImportDeclaration(node); - case SyntaxKind.ImportClause: - return emitImportClause(node); - case SyntaxKind.NamespaceImport: - return emitNamespaceImport(node); - case SyntaxKind.NamespaceExport: - return emitNamespaceExport(node); - case SyntaxKind.NamedImports: - return emitNamedImports(node); - case SyntaxKind.ImportSpecifier: - return emitImportSpecifier(node); - case SyntaxKind.ExportAssignment: - return emitExportAssignment(node); - case SyntaxKind.ExportDeclaration: - return emitExportDeclaration(node); - case SyntaxKind.NamedExports: - return emitNamedExports(node); - case SyntaxKind.ExportSpecifier: - return emitExportSpecifier(node); - case SyntaxKind.MissingDeclaration: - return; - - // Module references - case SyntaxKind.ExternalModuleReference: - return emitExternalModuleReference(node); - - // JSX (non-expression) - case SyntaxKind.JsxText: - return emitJsxText(node); - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxOpeningFragment: - return emitJsxOpeningElementOrFragment(node); - case SyntaxKind.JsxClosingElement: - case SyntaxKind.JsxClosingFragment: - return emitJsxClosingElementOrFragment(node); - case SyntaxKind.JsxAttribute: - return emitJsxAttribute(node); - case SyntaxKind.JsxAttributes: - return emitJsxAttributes(node); - case SyntaxKind.JsxSpreadAttribute: - return emitJsxSpreadAttribute(node); - case SyntaxKind.JsxExpression: - return emitJsxExpression(node); - - // Clauses - case SyntaxKind.CaseClause: - return emitCaseClause(node); - case SyntaxKind.DefaultClause: - return emitDefaultClause(node); - case SyntaxKind.HeritageClause: - return emitHeritageClause(node); - case SyntaxKind.CatchClause: - return emitCatchClause(node); - - // Property assignments - case SyntaxKind.PropertyAssignment: - return emitPropertyAssignment(node); - case SyntaxKind.ShorthandPropertyAssignment: - return emitShorthandPropertyAssignment(node); - case SyntaxKind.SpreadAssignment: - return emitSpreadAssignment(node as SpreadAssignment); - - // Enum - case SyntaxKind.EnumMember: - return emitEnumMember(node); - - // JSDoc nodes (only used in codefixes currently) - case SyntaxKind.JSDocParameterTag: - case SyntaxKind.JSDocPropertyTag: - return emitJSDocPropertyLikeTag(node as JSDocPropertyLikeTag); - case SyntaxKind.JSDocReturnTag: - case SyntaxKind.JSDocTypeTag: - case SyntaxKind.JSDocThisTag: - case SyntaxKind.JSDocEnumTag: - return emitJSDocSimpleTypedTag(node as JSDocTypeTag); - case SyntaxKind.JSDocImplementsTag: - case SyntaxKind.JSDocAugmentsTag: - return emitJSDocHeritageTag(node as JSDocImplementsTag | JSDocAugmentsTag); - case SyntaxKind.JSDocTemplateTag: - return emitJSDocTemplateTag(node as JSDocTemplateTag); - case SyntaxKind.JSDocTypedefTag: - return emitJSDocTypedefTag(node as JSDocTypedefTag); - case SyntaxKind.JSDocCallbackTag: - return emitJSDocCallbackTag(node as JSDocCallbackTag); - case SyntaxKind.JSDocSignature: - return emitJSDocSignature(node as JSDocSignature); - case SyntaxKind.JSDocTypeLiteral: - return emitJSDocTypeLiteral(node as JSDocTypeLiteral); - case SyntaxKind.JSDocClassTag: - case SyntaxKind.JSDocTag: - return emitJSDocSimpleTag(node as JSDocTag); - case SyntaxKind.JSDocSeeTag: - return emitJSDocSeeTag(node as JSDocSeeTag); - case SyntaxKind.JSDocNameReference: - return emitJSDocNameReference(node as JSDocNameReference); - - case SyntaxKind.JSDocComment: - return emitJSDoc(node as JSDoc); - - // Transformation nodes (ignored) - } - - if (isExpression(node)) { - hint = EmitHint.Expression; - if (substituteNode !== noEmitSubstitution) { - lastSubstitution = node = substituteNode(hint, node); - } - } - else if (isToken(node)) { - return writeTokenNode(node, writePunctuation); - } - } - if (hint === EmitHint.Expression) { - switch (node.kind) { - // Literals - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - return emitNumericOrBigIntLiteral(node); - - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return emitLiteral(node, /*jsxAttributeEscape*/ false); - - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); - - // Reserved words - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.ThisKeyword: - case SyntaxKind.ImportKeyword: - writeTokenNode(node, writeKeyword); - return; - - // Expressions - case SyntaxKind.ArrayLiteralExpression: - return emitArrayLiteralExpression(node); - case SyntaxKind.ObjectLiteralExpression: - return emitObjectLiteralExpression(node); - case SyntaxKind.PropertyAccessExpression: - return emitPropertyAccessExpression(node); - case SyntaxKind.ElementAccessExpression: - return emitElementAccessExpression(node); - case SyntaxKind.CallExpression: - return emitCallExpression(node); - case SyntaxKind.NewExpression: - return emitNewExpression(node); - case SyntaxKind.TaggedTemplateExpression: - return emitTaggedTemplateExpression(node); - case SyntaxKind.TypeAssertionExpression: - return emitTypeAssertionExpression(node); - case SyntaxKind.ParenthesizedExpression: - return emitParenthesizedExpression(node); - case SyntaxKind.FunctionExpression: - return emitFunctionExpression(node); - case SyntaxKind.ArrowFunction: - return emitArrowFunction(node); - case SyntaxKind.DeleteExpression: - return emitDeleteExpression(node); - case SyntaxKind.TypeOfExpression: - return emitTypeOfExpression(node); - case SyntaxKind.VoidExpression: - return emitVoidExpression(node); - case SyntaxKind.AwaitExpression: - return emitAwaitExpression(node); - case SyntaxKind.PrefixUnaryExpression: - return emitPrefixUnaryExpression(node); - case SyntaxKind.PostfixUnaryExpression: - return emitPostfixUnaryExpression(node); - case SyntaxKind.BinaryExpression: - return emitBinaryExpression(node); - case SyntaxKind.ConditionalExpression: - return emitConditionalExpression(node); - case SyntaxKind.TemplateExpression: - return emitTemplateExpression(node); - case SyntaxKind.YieldExpression: - return emitYieldExpression(node); - case SyntaxKind.SpreadElement: - return emitSpreadExpression(node); - case SyntaxKind.ClassExpression: - return emitClassExpression(node); - case SyntaxKind.OmittedExpression: - return; - case SyntaxKind.AsExpression: - return emitAsExpression(node); - case SyntaxKind.NonNullExpression: - return emitNonNullExpression(node); - case SyntaxKind.MetaProperty: - return emitMetaProperty(node); - - // JSX - case SyntaxKind.JsxElement: - return emitJsxElement(node); - case SyntaxKind.JsxSelfClosingElement: - return emitJsxSelfClosingElement(node); - case SyntaxKind.JsxFragment: - return emitJsxFragment(node); - - // Transformation nodes - case SyntaxKind.PartiallyEmittedExpression: - return emitPartiallyEmittedExpression(node); - - case SyntaxKind.CommaListExpression: - return emitCommaList(node); - } - } - } - - function emitMappedTypeParameter(node: TypeParameterDeclaration): void { - emit(node.name); - writeSpace(); - writeKeyword("in"); - writeSpace(); - emit(node.constraint); - } - - function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) { - Debug.assert(lastNode === node || lastSubstitution === node); - const pipelinePhase = getNextPipelinePhase(PipelinePhase.Substitution, hint, node); - pipelinePhase(hint, lastSubstitution!); - Debug.assert(lastNode === node || lastSubstitution === node); - } - - function getHelpersFromBundledSourceFiles(bundle: Bundle): string[] | undefined { - let result: string[] | undefined; - if (moduleKind === ModuleKind.None || printerOptions.noEmitHelpers) { - return undefined; - } - const bundledHelpers = new Map(); - for (const sourceFile of bundle.sourceFiles) { - const shouldSkip = getExternalHelpersModuleName(sourceFile) !== undefined; - const helpers = getSortedEmitHelpers(sourceFile); - if (!helpers) continue; - for (const helper of helpers) { - if (!helper.scoped && !shouldSkip && !bundledHelpers.get(helper.name)) { - bundledHelpers.set(helper.name, true); - (result || (result = [])).push(helper.name); - } - } - } - - return result; - } - - function emitHelpers(node: Node) { - let helpersEmitted = false; - const bundle = node.kind === SyntaxKind.Bundle ? node : undefined; - if (bundle && moduleKind === ModuleKind.None) { - return; - } - const numPrepends = bundle ? bundle.prepends.length : 0; - const numNodes = bundle ? bundle.sourceFiles.length + numPrepends : 1; - for (let i = 0; i < numNodes; i++) { - const currentNode = bundle ? i < numPrepends ? bundle.prepends[i] : bundle.sourceFiles[i - numPrepends] : node; - const sourceFile = isSourceFile(currentNode) ? currentNode : isUnparsedSource(currentNode) ? undefined : currentSourceFile!; - const shouldSkip = printerOptions.noEmitHelpers || (!!sourceFile && hasRecordedExternalHelpers(sourceFile)); - const shouldBundle = (isSourceFile(currentNode) || isUnparsedSource(currentNode)) && !isOwnFileEmit; - const helpers = isUnparsedSource(currentNode) ? currentNode.helpers : getSortedEmitHelpers(currentNode); - if (helpers) { - for (const helper of helpers) { - if (!helper.scoped) { - // Skip the helper if it can be skipped and the noEmitHelpers compiler - // option is set, or if it can be imported and the importHelpers compiler - // option is set. - if (shouldSkip) continue; - - // Skip the helper if it can be bundled but hasn't already been emitted and we - // are emitting a bundled module. - if (shouldBundle) { - if (bundledHelpers.get(helper.name)) { - continue; - } - - bundledHelpers.set(helper.name, true); - } - } - else if (bundle) { - // Skip the helper if it is scoped and we are emitting bundled helpers - continue; - } - const pos = getTextPosWithWriteLine(); - if (typeof helper.text === "string") { - writeLines(helper.text); - } - else { - writeLines(helper.text(makeFileLevelOptimisticUniqueName)); - } - if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.EmitHelpers, data: helper.name }); - helpersEmitted = true; - } - } - } - - return helpersEmitted; - } - - function getSortedEmitHelpers(node: Node) { - const helpers = getEmitHelpers(node); - return helpers && stableSort(helpers, compareEmitHelpers); - } - - // - // Literals/Pseudo-literals - // - - // SyntaxKind.NumericLiteral - // SyntaxKind.BigIntLiteral - function emitNumericOrBigIntLiteral(node: NumericLiteral | BigIntLiteral) { - emitLiteral(node, /*jsxAttributeEscape*/ false); - } - - // SyntaxKind.StringLiteral - // SyntaxKind.RegularExpressionLiteral - // SyntaxKind.NoSubstitutionTemplateLiteral - // SyntaxKind.TemplateHead - // SyntaxKind.TemplateMiddle - // SyntaxKind.TemplateTail - function emitLiteral(node: LiteralLikeNode, jsxAttributeEscape: boolean) { - const text = getLiteralTextOfNode(node, printerOptions.neverAsciiEscape, jsxAttributeEscape); - if ((printerOptions.sourceMap || printerOptions.inlineSourceMap) - && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { - writeLiteral(text); - } - else { - // Quick info expects all literals to be called with writeStringLiteral, as there's no specific type for numberLiterals - writeStringLiteral(text); - } - } - - // SyntaxKind.UnparsedSource - // SyntaxKind.UnparsedPrepend - function emitUnparsedSourceOrPrepend(unparsed: UnparsedSource | UnparsedPrepend) { - for (const text of unparsed.texts) { - writeLine(); - emit(text); - } - } - - // SyntaxKind.UnparsedPrologue - // SyntaxKind.UnparsedText - // SyntaxKind.UnparsedInternal - // SyntaxKind.UnparsedSyntheticReference - function writeUnparsedNode(unparsed: UnparsedNode) { - writer.rawWrite(unparsed.parent.text.substring(unparsed.pos, unparsed.end)); - } + // SyntaxKind.UnparsedPrologue + // SyntaxKind.UnparsedText + // SyntaxKind.UnparsedInternal + // SyntaxKind.UnparsedSyntheticReference + function writeUnparsedNode(unparsed: UnparsedNode) { + writer.rawWrite(unparsed.parent.text.substring(unparsed.pos, unparsed.end)); + } // SyntaxKind.UnparsedText // SyntaxKind.UnparsedInternal @@ -1917,7 +1908,7 @@ namespace ts { function emitEntityName(node: EntityName) { if (node.kind === SyntaxKind.Identifier) { - emitExpression(node); + emit(node); } else { emit(node); @@ -1926,7 +1917,7 @@ namespace ts { function emitComputedPropertyName(node: ComputedPropertyName) { writePunctuation("["); - emitExpression(node.expression); + emit(node.expression); writePunctuation("]"); } @@ -1968,7 +1959,7 @@ namespace ts { function emitDecorator(decorator: Decorator) { writePunctuation("@"); - emitExpression(decorator.expression); + emit(decorator.expression); } // @@ -2261,7 +2252,7 @@ namespace ts { } writePunctuation("["); - pipelineEmit(EmitHint.MappedTypeParameter, node.typeParameter); + emitWithContext(node.typeParameter, emitMappedTypeParameter); if (node.nameType) { writeSpace(); writeKeyword("as"); @@ -2287,3286 +2278,4455 @@ namespace ts { writeLine(); decreaseIndent(); } - writePunctuation("}"); + writePunctuation("}"); + } + + function emitLiteralType(node: LiteralTypeNode) { + emit(node.literal); + } + + function emitTemplateType(node: TemplateLiteralTypeNode) { + emit(node.head); + emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans); + } + + function emitImportTypeNode(node: ImportTypeNode) { + if (node.isTypeOf) { + writeKeyword("typeof"); + writeSpace(); + } + writeKeyword("import"); + writePunctuation("("); + emit(node.argument); + writePunctuation(")"); + if (node.qualifier) { + writePunctuation("."); + emit(node.qualifier); + } + emitTypeArguments(node, node.typeArguments); + } + + // + // Binding patterns + // + + function emitObjectBindingPattern(node: ObjectBindingPattern) { + writePunctuation("{"); + emitList(node, node.elements, ListFormat.ObjectBindingPatternElements); + writePunctuation("}"); + } + + function emitArrayBindingPattern(node: ArrayBindingPattern) { + writePunctuation("["); + emitList(node, node.elements, ListFormat.ArrayBindingPatternElements); + writePunctuation("]"); + } + + function emitBindingElement(node: BindingElement) { + emit(node.dotDotDotToken); + if (node.propertyName) { + emit(node.propertyName); + writePunctuation(":"); + writeSpace(); + } + emit(node.name); + emitInitializer(node.initializer, node.name.end, node); + } + + // + // Expressions + // + + function emitArrayLiteralExpression(node: ArrayLiteralExpression) { + const elements = node.elements; + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; + emitList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); + } + + function emitObjectLiteralExpression(node: ObjectLiteralExpression) { + forEach(node.properties, generateMemberNames); + + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } + + const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; + const allowTrailingComma = currentSourceFile!.languageVersion >= ScriptTarget.ES5 && !isJsonSourceFile(currentSourceFile!) ? ListFormat.AllowTrailingComma : ListFormat.None; + emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); + + if (indentedFlag) { + decreaseIndent(); + } + } + + function emitPropertyAccessExpression(node: PropertyAccessExpression) { + emit(node.expression); + const token = node.questionDotToken || setTextRangePosEnd(factory.createToken(SyntaxKind.DotToken) as DotToken, node.expression.end, node.name.pos); + const linesBeforeDot = getLinesBetweenNodes(node, node.expression, token); + const linesAfterDot = getLinesBetweenNodes(node, token, node.name); + + writeLinesAndIndent(linesBeforeDot, /*writeSpaceIfNotIndenting*/ false); + + const shouldEmitDotDot = + token.kind !== SyntaxKind.QuestionDotToken && + mayNeedDotDotForPropertyAccess(node.expression) && + !writer.hasTrailingComment() && + !writer.hasTrailingWhitespace(); + + if (shouldEmitDotDot) { + writePunctuation("."); + } + + if (node.questionDotToken) { + emit(token); + } + else { + emitTokenWithComment(token.kind, node.expression.end, writePunctuation, node); + } + writeLinesAndIndent(linesAfterDot, /*writeSpaceIfNotIndenting*/ false); + emit(node.name); + decreaseIndentIf(linesBeforeDot, linesAfterDot); + } + + // 1..toString is a valid property access, emit a dot after the literal + // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal + function mayNeedDotDotForPropertyAccess(expression: Expression) { + expression = skipPartiallyEmittedExpressions(expression); + if (isNumericLiteral(expression)) { + // check if numeric literal is a decimal literal that was originally written with a dot + const text = getLiteralTextOfNode(expression, /*neverAsciiEscape*/ true, /*jsxAttributeEscape*/ false); + // If he number will be printed verbatim and it doesn't already contain a dot, add one + // if the expression doesn't have any comments that will be emitted. + return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!); + } + else if (isAccessExpression(expression)) { + // check if constant enum value is integer + const constantValue = getConstantValue(expression); + // isFinite handles cases when constantValue is undefined + return typeof constantValue === "number" && isFinite(constantValue) + && Math.floor(constantValue) === constantValue; + } + } + + function emitElementAccessExpression(node: ElementAccessExpression) { + emit(node.expression); + emit(node.questionDotToken); + emitTokenWithComment(SyntaxKind.OpenBracketToken, node.expression.end, writePunctuation, node); + emit(node.argumentExpression); + emitTokenWithComment(SyntaxKind.CloseBracketToken, node.argumentExpression.end, writePunctuation, node); + } + + function emitCallExpression(node: CallExpression) { + emit(node.expression); + emit(node.questionDotToken); + emitTypeArguments(node, node.typeArguments); + emitList(node, node.arguments, ListFormat.CallExpressionArguments); + } + + function emitNewExpression(node: NewExpression) { + emitTokenWithComment(SyntaxKind.NewKeyword, node.pos, writeKeyword, node); + writeSpace(); + emit(node.expression); + emitTypeArguments(node, node.typeArguments); + emitList(node, node.arguments, ListFormat.NewExpressionArguments); + } + + function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { + emit(node.tag); + emitTypeArguments(node, node.typeArguments); + writeSpace(); + emit(node.template); + } + + function emitTypeAssertionExpression(node: TypeAssertion) { + writePunctuation("<"); + emit(node.type); + writePunctuation(">"); + emit(node.expression); + } + + function emitParenthesizedExpression(node: ParenthesizedExpression) { + const openParenPos = emitTokenWithComment(SyntaxKind.OpenParenToken, node.pos, writePunctuation, node); + const indented = writeLineSeparatorsAndIndentBefore(node.expression, node); + emit(node.expression); + writeLineSeparatorsAfter(node.expression, node); + decreaseIndentIf(indented); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression ? node.expression.end : openParenPos, writePunctuation, node); + } + + function emitFunctionExpression(node: FunctionExpression) { + generateNameIfNeeded(node.name); + emitFunctionDeclarationOrExpression(node); + } + + function emitArrowFunction(node: ArrowFunction) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + emitSignatureAndBody(node, emitArrowFunctionHead); + } + + function emitArrowFunctionHead(node: ArrowFunction) { + emitTypeParameters(node, node.typeParameters); + emitParametersForArrow(node, node.parameters); + emitTypeAnnotation(node.type); + writeSpace(); + emit(node.equalsGreaterThanToken); + } + + function emitDeleteExpression(node: DeleteExpression) { + emitTokenWithComment(SyntaxKind.DeleteKeyword, node.pos, writeKeyword, node); + writeSpace(); + emit(node.expression); + } + + function emitTypeOfExpression(node: TypeOfExpression) { + emitTokenWithComment(SyntaxKind.TypeOfKeyword, node.pos, writeKeyword, node); + writeSpace(); + emit(node.expression); + } + + function emitVoidExpression(node: VoidExpression) { + emitTokenWithComment(SyntaxKind.VoidKeyword, node.pos, writeKeyword, node); + writeSpace(); + emit(node.expression); + } + + function emitAwaitExpression(node: AwaitExpression) { + emitTokenWithComment(SyntaxKind.AwaitKeyword, node.pos, writeKeyword, node); + writeSpace(); + emit(node.expression); + } + + function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { + writeTokenText(node.operator, writeOperator); + if (shouldEmitWhitespaceBeforeOperand(node)) { + writeSpace(); + } + emit(node.operand); + } + + function shouldEmitWhitespaceBeforeOperand(node: PrefixUnaryExpression) { + // In some cases, we need to emit a space between the operator and the operand. One obvious case + // is when the operator is an identifier, like delete or typeof. We also need to do this for plus + // and minus expressions in certain cases. Specifically, consider the following two cases (parens + // are just for clarity of exposition, and not part of the source code): + // + // (+(+1)) + // (+(++1)) + // + // We need to emit a space in both cases. In the first case, the absence of a space will make + // the resulting expression a prefix increment operation. And in the second, it will make the resulting + // expression a prefix increment whose operand is a plus expression - (++(+x)) + // The same is true of minus of course. + const operand = node.operand; + return operand.kind === SyntaxKind.PrefixUnaryExpression + && ((node.operator === SyntaxKind.PlusToken && ((operand).operator === SyntaxKind.PlusToken || (operand).operator === SyntaxKind.PlusPlusToken)) + || (node.operator === SyntaxKind.MinusToken && ((operand).operator === SyntaxKind.MinusToken || (operand).operator === SyntaxKind.MinusMinusToken))); + } + + function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { + emit(node.operand); + writeTokenText(node.operator, writeOperator); + } + + function createEmitBinaryExpression() { + interface WorkArea { + stackIndex: number; + preserveSourceNewlinesStack: (boolean | undefined)[]; + containerPosStack: number[]; + containerEndStack: number[]; + declarationListContainerEndStack: number[]; + shouldEmitCommentsStack: boolean[]; + shouldEmitSourceMapsStack: boolean[]; + } + + return createBinaryExpressionTrampoline(onEnter, maybeEmitExpression, onOperator, maybeEmitExpression, onExit, /*foldState*/ undefined); + + function onEnter(node: BinaryExpression, state: WorkArea | undefined) { + if (state) { + state.stackIndex++; + state.preserveSourceNewlinesStack[state.stackIndex] = preserveSourceNewlines; + state.containerPosStack[state.stackIndex] = containerPos; + state.containerEndStack[state.stackIndex] = containerEnd; + state.declarationListContainerEndStack[state.stackIndex] = declarationListContainerEnd; + const emitComments = state.shouldEmitCommentsStack[state.stackIndex] = shouldEmitComments(node); + const emitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex] = shouldEmitSourceMaps(node); + beforeEmitWithContext(node, emitComments, emitSourceMaps); + } + else { + state = { + stackIndex: 0, + preserveSourceNewlinesStack: [undefined], + containerPosStack: [-1], + containerEndStack: [-1], + declarationListContainerEndStack: [-1], + shouldEmitCommentsStack: [false], + shouldEmitSourceMapsStack: [false], + }; + } + return state; + } + + function onOperator(operatorToken: BinaryOperatorToken, _state: WorkArea, node: BinaryExpression) { + const isCommaOperator = operatorToken.kind !== SyntaxKind.CommaToken; + const linesBeforeOperator = getLinesBetweenNodes(node, node.left, operatorToken); + const linesAfterOperator = getLinesBetweenNodes(node, operatorToken, node.right); + writeLinesAndIndent(linesBeforeOperator, isCommaOperator); + emitLeadingCommentsOfPosition(operatorToken.pos); + writeTokenNode(operatorToken, operatorToken.kind === SyntaxKind.InKeyword ? writeKeyword : writeOperator); + emitTrailingCommentsOfPosition(operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts + writeLinesAndIndent(linesAfterOperator, /*writeSpaceIfNotIndenting*/ true); + } + + function onExit(node: BinaryExpression, state: WorkArea) { + const linesBeforeOperator = getLinesBetweenNodes(node, node.left, node.operatorToken); + const linesAfterOperator = getLinesBetweenNodes(node, node.operatorToken, node.right); + decreaseIndentIf(linesBeforeOperator, linesAfterOperator); + if (state.stackIndex > 0) { + const savedPreserveSourceNewlines = state.preserveSourceNewlinesStack[state.stackIndex]; + const savedContainerPos = state.containerPosStack[state.stackIndex]; + const savedContainerEnd = state.containerEndStack[state.stackIndex]; + const savedDeclarationListContainerEnd = state.declarationListContainerEndStack[state.stackIndex]; + const shouldEmitComments = state.shouldEmitCommentsStack[state.stackIndex]; + const shouldEmitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex]; + afterEmitWithContext(node, shouldEmitComments, shouldEmitSourceMaps, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd, savedPreserveSourceNewlines); + state.stackIndex--; + } + } + + function maybeEmitExpression(next: Expression) { + // Push a new frame for binary expressions, otherwise emit all other expressions. + if (isBinaryExpression(next)) { + return next; + } + + emit(next); + } } - function emitLiteralType(node: LiteralTypeNode) { - emitExpression(node.literal); + function emitConditionalExpression(node: ConditionalExpression) { + const linesBeforeQuestion = getLinesBetweenNodes(node, node.condition, node.questionToken); + const linesAfterQuestion = getLinesBetweenNodes(node, node.questionToken, node.whenTrue); + const linesBeforeColon = getLinesBetweenNodes(node, node.whenTrue, node.colonToken); + const linesAfterColon = getLinesBetweenNodes(node, node.colonToken, node.whenFalse); + + emit(node.condition); + writeLinesAndIndent(linesBeforeQuestion, /*writeSpaceIfNotIndenting*/ true); + emit(node.questionToken); + writeLinesAndIndent(linesAfterQuestion, /*writeSpaceIfNotIndenting*/ true); + emit(node.whenTrue); + decreaseIndentIf(linesBeforeQuestion, linesAfterQuestion); + + writeLinesAndIndent(linesBeforeColon, /*writeSpaceIfNotIndenting*/ true); + emit(node.colonToken); + writeLinesAndIndent(linesAfterColon, /*writeSpaceIfNotIndenting*/ true); + emit(node.whenFalse); + decreaseIndentIf(linesBeforeColon, linesAfterColon); } - function emitTemplateType(node: TemplateLiteralTypeNode) { + function emitTemplateExpression(node: TemplateExpression) { emit(node.head); emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans); } - function emitImportTypeNode(node: ImportTypeNode) { - if (node.isTypeOf) { - writeKeyword("typeof"); - writeSpace(); - } - writeKeyword("import"); - writePunctuation("("); - emit(node.argument); - writePunctuation(")"); - if (node.qualifier) { - writePunctuation("."); - emit(node.qualifier); - } - emitTypeArguments(node, node.typeArguments); + function emitYieldExpression(node: YieldExpression) { + emitTokenWithComment(SyntaxKind.YieldKeyword, node.pos, writeKeyword, node); + emit(node.asteriskToken); + emitExpressionWithLeadingSpace(node.expression); } - // - // Binding patterns - // + function emitSpreadExpression(node: SpreadElement) { + emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node); + emit(node.expression); + } - function emitObjectBindingPattern(node: ObjectBindingPattern) { - writePunctuation("{"); - emitList(node, node.elements, ListFormat.ObjectBindingPatternElements); - writePunctuation("}"); + function emitClassExpression(node: ClassExpression) { + generateNameIfNeeded(node.name); + emitClassDeclarationOrExpression(node); } - function emitArrayBindingPattern(node: ArrayBindingPattern) { - writePunctuation("["); - emitList(node, node.elements, ListFormat.ArrayBindingPatternElements); - writePunctuation("]"); + function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { + emit(node.expression); + emitTypeArguments(node, node.typeArguments); } - function emitBindingElement(node: BindingElement) { - emit(node.dotDotDotToken); - if (node.propertyName) { - emit(node.propertyName); - writePunctuation(":"); + function emitAsExpression(node: AsExpression) { + emit(node.expression); + if (node.type) { + writeSpace(); + writeKeyword("as"); writeSpace(); + emit(node.type); } + } + + function emitNonNullExpression(node: NonNullExpression) { + emit(node.expression); + writeOperator("!"); + } + + function emitMetaProperty(node: MetaProperty) { + writeToken(node.keywordToken, node.pos, writePunctuation); + writePunctuation("."); emit(node.name); - emitInitializer(node.initializer, node.name.end, node); } // - // Expressions + // Misc // - function emitArrayLiteralExpression(node: ArrayLiteralExpression) { - const elements = node.elements; - const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; - emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); + function emitTemplateSpan(node: TemplateSpan) { + emit(node.expression); + emit(node.literal); } - function emitObjectLiteralExpression(node: ObjectLiteralExpression) { - forEach(node.properties, generateMemberNames); + // + // Statements + // - const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } + function emitBlock(node: Block) { + emitBlockStatements(node, /*forceSingleLine*/ !node.multiLine && isEmptyBlock(node)); + } - const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; - const allowTrailingComma = currentSourceFile!.languageVersion >= ScriptTarget.ES5 && !isJsonSourceFile(currentSourceFile!) ? ListFormat.AllowTrailingComma : ListFormat.None; - emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); + function emitBlockStatements(node: BlockLike, forceSingleLine: boolean) { + emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, /*contextNode*/ node); + const format = forceSingleLine || getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineBlockStatements : ListFormat.MultiLineBlockStatements; + emitList(node, node.statements, format); + emitTokenWithComment(SyntaxKind.CloseBraceToken, node.statements.end, writePunctuation, /*contextNode*/ node, /*indentLeading*/ !!(format & ListFormat.MultiLine)); + } - if (indentedFlag) { - decreaseIndent(); - } + function emitVariableStatement(node: VariableStatement) { + emitModifiers(node, node.modifiers); + emit(node.declarationList); + writeTrailingSemicolon(); } - function emitPropertyAccessExpression(node: PropertyAccessExpression) { - const expression = cast(emitExpression(node.expression), isExpression); - const token = node.questionDotToken || setTextRangePosEnd(factory.createToken(SyntaxKind.DotToken) as DotToken, node.expression.end, node.name.pos); - const linesBeforeDot = getLinesBetweenNodes(node, node.expression, token); - const linesAfterDot = getLinesBetweenNodes(node, token, node.name); + function emitEmptyStatement(isEmbeddedStatement: boolean) { + // While most trailing semicolons are possibly insignificant, an embedded "empty" + // statement is significant and cannot be elided by a trailing-semicolon-omitting writer. + if (isEmbeddedStatement) { + writePunctuation(";"); + } + else { + writeTrailingSemicolon(); + } + } - writeLinesAndIndent(linesBeforeDot, /*writeSpaceIfNotIndenting*/ false); + function emitEmbeddedEmptyStatement(_node: EmptyStatement) { + emitEmptyStatement(/*isEmbeddedStatement*/ true); + } - const shouldEmitDotDot = - token.kind !== SyntaxKind.QuestionDotToken && - mayNeedDotDotForPropertyAccess(expression) && - !writer.hasTrailingComment() && - !writer.hasTrailingWhitespace(); + function emitExpressionStatement(node: ExpressionStatement) { + emit(node.expression); + // Emit semicolon in non json files + // or if json file that created synthesized expression(eg.define expression statement when --out and amd code generation) + if (!isJsonSourceFile(currentSourceFile!) || nodeIsSynthesized(node.expression)) { + writeTrailingSemicolon(); + } + } - if (shouldEmitDotDot) { - writePunctuation("."); + function emitIfStatement(node: IfStatement) { + const openParenPos = emitTokenWithComment(SyntaxKind.IfKeyword, node.pos, writeKeyword, node); + writeSpace(); + emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); + emit(node.expression); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); + emitEmbeddedStatement(node, node.thenStatement); + if (node.elseStatement) { + writeLineOrSpace(node, node.thenStatement, node.elseStatement); + emitTokenWithComment(SyntaxKind.ElseKeyword, node.thenStatement.end, writeKeyword, node); + if (node.elseStatement.kind === SyntaxKind.IfStatement) { + writeSpace(); + emit(node.elseStatement); + } + else { + emitEmbeddedStatement(node, node.elseStatement); + } } + } - if (node.questionDotToken) { - emit(token); + function emitWhileClause(node: WhileStatement | DoStatement, startPos: number) { + const openParenPos = emitTokenWithComment(SyntaxKind.WhileKeyword, startPos, writeKeyword, node); + writeSpace(); + emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); + emit(node.expression); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); + } + + function emitDoStatement(node: DoStatement) { + emitTokenWithComment(SyntaxKind.DoKeyword, node.pos, writeKeyword, node); + emitEmbeddedStatement(node, node.statement); + if (isBlock(node.statement) && !preserveSourceNewlines) { + writeSpace(); } else { - emitTokenWithComment(token.kind, node.expression.end, writePunctuation, node); + writeLineOrSpace(node, node.statement, node.expression); } - writeLinesAndIndent(linesAfterDot, /*writeSpaceIfNotIndenting*/ false); - emit(node.name); - decreaseIndentIf(linesBeforeDot, linesAfterDot); + + emitWhileClause(node, node.statement.end); + writeTrailingSemicolon(); } - // 1..toString is a valid property access, emit a dot after the literal - // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal - function mayNeedDotDotForPropertyAccess(expression: Expression) { - expression = skipPartiallyEmittedExpressions(expression); - if (isNumericLiteral(expression)) { - // check if numeric literal is a decimal literal that was originally written with a dot - const text = getLiteralTextOfNode(expression, /*neverAsciiEscape*/ true, /*jsxAttributeEscape*/ false); - // If he number will be printed verbatim and it doesn't already contain a dot, add one - // if the expression doesn't have any comments that will be emitted. - return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!); + function emitWhileStatement(node: WhileStatement) { + emitWhileClause(node, node.pos); + emitEmbeddedStatement(node, node.statement); + } + + function emitForStatement(node: ForStatement) { + const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node); + writeSpace(); + let pos = emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, /*contextNode*/ node); + emitForBinding(node.initializer); + pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.initializer ? node.initializer.end : pos, writePunctuation, node); + emitExpressionWithLeadingSpace(node.condition); + pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.condition ? node.condition.end : pos, writePunctuation, node); + emitExpressionWithLeadingSpace(node.incrementor); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.incrementor ? node.incrementor.end : pos, writePunctuation, node); + emitEmbeddedStatement(node, node.statement); + } + + function emitForInStatement(node: ForInStatement) { + const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node); + writeSpace(); + emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); + emitForBinding(node.initializer); + writeSpace(); + emitTokenWithComment(SyntaxKind.InKeyword, node.initializer.end, writeKeyword, node); + writeSpace(); + emit(node.expression); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); + emitEmbeddedStatement(node, node.statement); + } + + function emitForOfStatement(node: ForOfStatement) { + const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node); + writeSpace(); + emitWithTrailingSpace(node.awaitModifier); + emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); + emitForBinding(node.initializer); + writeSpace(); + emitTokenWithComment(SyntaxKind.OfKeyword, node.initializer.end, writeKeyword, node); + writeSpace(); + emit(node.expression); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); + emitEmbeddedStatement(node, node.statement); + } + + function emitForBinding(node: VariableDeclarationList | Expression | undefined) { + if (node !== undefined) { + if (node.kind === SyntaxKind.VariableDeclarationList) { + emit(node); + } + else { + emit(node); + } + } + } + + function emitContinueStatement(node: ContinueStatement) { + emitTokenWithComment(SyntaxKind.ContinueKeyword, node.pos, writeKeyword, node); + emitWithLeadingSpace(node.label); + writeTrailingSemicolon(); + } + + function emitBreakStatement(node: BreakStatement) { + emitTokenWithComment(SyntaxKind.BreakKeyword, node.pos, writeKeyword, node); + emitWithLeadingSpace(node.label); + writeTrailingSemicolon(); + } + + function emitTokenWithComment(token: SyntaxKind, pos: number, writer: (s: string) => void, contextNode: Node, indentLeading?: boolean) { + const node = getParseTreeNode(contextNode); + const isSimilarNode = node && node.kind === contextNode.kind; + const startPos = pos; + if (isSimilarNode && currentSourceFile) { + pos = skipTrivia(currentSourceFile.text, pos); } - else if (isAccessExpression(expression)) { - // check if constant enum value is integer - const constantValue = getConstantValue(expression); - // isFinite handles cases when constantValue is undefined - return typeof constantValue === "number" && isFinite(constantValue) - && Math.floor(constantValue) === constantValue; + if (isSimilarNode && contextNode.pos !== startPos) { + const needsIndent = indentLeading && currentSourceFile && !positionsAreOnSameLine(startPos, pos, currentSourceFile); + if (needsIndent) { + increaseIndent(); + } + emitLeadingCommentsOfPosition(startPos); + if (needsIndent) { + decreaseIndent(); + } + } + pos = writeTokenText(token, writer, pos); + if (isSimilarNode && contextNode.end !== pos) { + const isJsxExprContext = contextNode.kind === SyntaxKind.JsxExpression; + emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ !isJsxExprContext, /*forceNoNewline*/ isJsxExprContext); } + return pos; } - function emitElementAccessExpression(node: ElementAccessExpression) { - emitExpression(node.expression); - emit(node.questionDotToken); - emitTokenWithComment(SyntaxKind.OpenBracketToken, node.expression.end, writePunctuation, node); - emitExpression(node.argumentExpression); - emitTokenWithComment(SyntaxKind.CloseBracketToken, node.argumentExpression.end, writePunctuation, node); + function emitReturnStatement(node: ReturnStatement) { + emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, writeKeyword, /*contextNode*/ node); + emitExpressionWithLeadingSpace(node.expression); + writeTrailingSemicolon(); } - function emitCallExpression(node: CallExpression) { - emitExpression(node.expression); - emit(node.questionDotToken); - emitTypeArguments(node, node.typeArguments); - emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); + function emitWithStatement(node: WithStatement) { + const openParenPos = emitTokenWithComment(SyntaxKind.WithKeyword, node.pos, writeKeyword, node); + writeSpace(); + emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); + emit(node.expression); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); + emitEmbeddedStatement(node, node.statement); } - function emitNewExpression(node: NewExpression) { - emitTokenWithComment(SyntaxKind.NewKeyword, node.pos, writeKeyword, node); + function emitSwitchStatement(node: SwitchStatement) { + const openParenPos = emitTokenWithComment(SyntaxKind.SwitchKeyword, node.pos, writeKeyword, node); writeSpace(); - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); + emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); + emit(node.expression); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); + writeSpace(); + emit(node.caseBlock); } - function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { - emitExpression(node.tag); - emitTypeArguments(node, node.typeArguments); + function emitLabeledStatement(node: LabeledStatement) { + emit(node.label); + emitTokenWithComment(SyntaxKind.ColonToken, node.label.end, writePunctuation, node); writeSpace(); - emitExpression(node.template); + emit(node.statement); } - function emitTypeAssertionExpression(node: TypeAssertion) { - writePunctuation("<"); - emit(node.type); - writePunctuation(">"); - emitExpression(node.expression); + function emitThrowStatement(node: ThrowStatement) { + emitTokenWithComment(SyntaxKind.ThrowKeyword, node.pos, writeKeyword, node); + emitExpressionWithLeadingSpace(node.expression); + writeTrailingSemicolon(); } - function emitParenthesizedExpression(node: ParenthesizedExpression) { - const openParenPos = emitTokenWithComment(SyntaxKind.OpenParenToken, node.pos, writePunctuation, node); - const indented = writeLineSeparatorsAndIndentBefore(node.expression, node); - emitExpression(node.expression); - writeLineSeparatorsAfter(node.expression, node); - decreaseIndentIf(indented); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression ? node.expression.end : openParenPos, writePunctuation, node); + function emitTryStatement(node: TryStatement) { + emitTokenWithComment(SyntaxKind.TryKeyword, node.pos, writeKeyword, node); + writeSpace(); + emit(node.tryBlock); + if (node.catchClause) { + writeLineOrSpace(node, node.tryBlock, node.catchClause); + emit(node.catchClause); + } + if (node.finallyBlock) { + writeLineOrSpace(node, node.catchClause || node.tryBlock, node.finallyBlock); + emitTokenWithComment(SyntaxKind.FinallyKeyword, (node.catchClause || node.tryBlock).end, writeKeyword, node); + writeSpace(); + emit(node.finallyBlock); + } } - function emitFunctionExpression(node: FunctionExpression) { - generateNameIfNeeded(node.name); - emitFunctionDeclarationOrExpression(node); + function emitDebuggerStatement(node: DebuggerStatement) { + writeToken(SyntaxKind.DebuggerKeyword, node.pos, writeKeyword); + writeTrailingSemicolon(); } - function emitArrowFunction(node: ArrowFunction) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - emitSignatureAndBody(node, emitArrowFunctionHead); - } + // + // Declarations + // - function emitArrowFunctionHead(node: ArrowFunction) { - emitTypeParameters(node, node.typeParameters); - emitParametersForArrow(node, node.parameters); + function emitVariableDeclaration(node: VariableDeclaration) { + emit(node.name); + emit(node.exclamationToken); emitTypeAnnotation(node.type); - writeSpace(); - emit(node.equalsGreaterThanToken); + emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node); } - function emitDeleteExpression(node: DeleteExpression) { - emitTokenWithComment(SyntaxKind.DeleteKeyword, node.pos, writeKeyword, node); + function emitVariableDeclarationList(node: VariableDeclarationList) { + writeKeyword(isLet(node) ? "let" : isVarConst(node) ? "const" : "var"); writeSpace(); - emitExpression(node.expression); + emitList(node, node.declarations, ListFormat.VariableDeclarationList); } - function emitTypeOfExpression(node: TypeOfExpression) { - emitTokenWithComment(SyntaxKind.TypeOfKeyword, node.pos, writeKeyword, node); - writeSpace(); - emitExpression(node.expression); + function emitFunctionDeclaration(node: FunctionDeclaration) { + emitFunctionDeclarationOrExpression(node); } - function emitVoidExpression(node: VoidExpression) { - emitTokenWithComment(SyntaxKind.VoidKeyword, node.pos, writeKeyword, node); + function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + writeKeyword("function"); + emit(node.asteriskToken); writeSpace(); - emitExpression(node.expression); + emit(node.name); + emitSignatureAndBody(node, emitSignatureHead); } - function emitAwaitExpression(node: AwaitExpression) { - emitTokenWithComment(SyntaxKind.AwaitKeyword, node.pos, writeKeyword, node); - writeSpace(); - emitExpression(node.expression); - } + function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { + const body = node.body; + if (body) { + if (isBlock(body)) { + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } - function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { - writeTokenText(node.operator, writeOperator); - if (shouldEmitWhitespaceBeforeOperand(node)) { - writeSpace(); + pushNameGenerationScope(node); + forEach(node.parameters, generateNames); + generateNames(node.body); + + emitSignatureHead(node); + emitBlockFunctionBody(body); + popNameGenerationScope(node); + + if (indentedFlag) { + decreaseIndent(); + } + } + else { + emitSignatureHead(node); + writeSpace(); + emit(body); + } + } + else { + emitSignatureHead(node); + writeTrailingSemicolon(); } - emitExpression(node.operand); - } - function shouldEmitWhitespaceBeforeOperand(node: PrefixUnaryExpression) { - // In some cases, we need to emit a space between the operator and the operand. One obvious case - // is when the operator is an identifier, like delete or typeof. We also need to do this for plus - // and minus expressions in certain cases. Specifically, consider the following two cases (parens - // are just for clarity of exposition, and not part of the source code): - // - // (+(+1)) - // (+(++1)) - // - // We need to emit a space in both cases. In the first case, the absence of a space will make - // the resulting expression a prefix increment operation. And in the second, it will make the resulting - // expression a prefix increment whose operand is a plus expression - (++(+x)) - // The same is true of minus of course. - const operand = node.operand; - return operand.kind === SyntaxKind.PrefixUnaryExpression - && ((node.operator === SyntaxKind.PlusToken && ((operand).operator === SyntaxKind.PlusToken || (operand).operator === SyntaxKind.PlusPlusToken)) - || (node.operator === SyntaxKind.MinusToken && ((operand).operator === SyntaxKind.MinusToken || (operand).operator === SyntaxKind.MinusMinusToken))); } - function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { - emitExpression(node.operand); - writeTokenText(node.operator, writeOperator); + function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { + emitTypeParameters(node, node.typeParameters); + emitParameters(node, node.parameters); + emitTypeAnnotation(node.type); } - const enum EmitBinaryExpressionState { - EmitLeft, - EmitRight, - FinishEmit - } + function shouldEmitBlockFunctionBodyOnSingleLine(body: Block) { + // We must emit a function body as a single-line body in the following case: + // * The body has NodeEmitFlags.SingleLine specified. - /** - * emitBinaryExpression includes an embedded work stack to attempt to handle as many nested binary expressions - * as possible without creating any additional stack frames. This can only be done when the emit pipeline does - * not require notification/substitution/comment/sourcemap decorations. - */ - function emitBinaryExpression(node: BinaryExpression) { - const nodeStack = [node]; - const stateStack = [EmitBinaryExpressionState.EmitLeft]; - let stackIndex = 0; - while (stackIndex >= 0) { - node = nodeStack[stackIndex]; - switch (stateStack[stackIndex]) { - case EmitBinaryExpressionState.EmitLeft: { - maybePipelineEmitExpression(node.left); - break; - } - case EmitBinaryExpressionState.EmitRight: { - const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; - const linesBeforeOperator = getLinesBetweenNodes(node, node.left, node.operatorToken); - const linesAfterOperator = getLinesBetweenNodes(node, node.operatorToken, node.right); - writeLinesAndIndent(linesBeforeOperator, isCommaOperator); - emitLeadingCommentsOfPosition(node.operatorToken.pos); - writeTokenNode(node.operatorToken, node.operatorToken.kind === SyntaxKind.InKeyword ? writeKeyword : writeOperator); - emitTrailingCommentsOfPosition(node.operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts - writeLinesAndIndent(linesAfterOperator, /*writeSpaceIfNotIndenting*/ true); - maybePipelineEmitExpression(node.right); - break; - } - case EmitBinaryExpressionState.FinishEmit: { - const linesBeforeOperator = getLinesBetweenNodes(node, node.left, node.operatorToken); - const linesAfterOperator = getLinesBetweenNodes(node, node.operatorToken, node.right); - decreaseIndentIf(linesBeforeOperator, linesAfterOperator); - stackIndex--; - break; - } - default: return Debug.fail(`Invalid state ${stateStack[stackIndex]} for emitBinaryExpressionWorker`); - } - } + // We must emit a function body as a multi-line body in the following cases: + // * The body is explicitly marked as multi-line. + // * A non-synthesized body's start and end position are on different lines. + // * Any statement in the body starts on a new line. - function maybePipelineEmitExpression(next: Expression) { - // Advance the state of this unit of work, - stateStack[stackIndex]++; + if (getEmitFlags(body) & EmitFlags.SingleLine) { + return true; + } - // Then actually do the work of emitting the node `next` returned by the prior state + if (body.multiLine) { + return false; + } - // The following section should be identical to `pipelineEmit` save it assumes EmitHint.Expression and offloads - // binary expression handling, where possible, to the contained work queue + if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile!)) { + return false; + } - // #region trampolinePipelineEmit - const savedLastNode = lastNode; - const savedLastSubstitution = lastSubstitution; - lastNode = next; - lastSubstitution = undefined; + if (getLeadingLineTerminatorCount(body, body.statements, ListFormat.PreserveLines) + || getClosingLineTerminatorCount(body, body.statements, ListFormat.PreserveLines)) { + return false; + } - const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, EmitHint.Expression, next); - if (pipelinePhase === pipelineEmitWithHint && isBinaryExpression(next)) { - // If the target pipeline phase is emit directly, and the next node's also a binary expression, - // skip all the intermediate indirection and push the expression directly onto the work stack - stackIndex++; - stateStack[stackIndex] = EmitBinaryExpressionState.EmitLeft; - nodeStack[stackIndex] = next; - } - else { - pipelinePhase(EmitHint.Expression, next); + let previousStatement: Statement | undefined; + for (const statement of body.statements) { + if (getSeparatingLineTerminatorCount(previousStatement, statement, ListFormat.PreserveLines) > 0) { + return false; } - Debug.assert(lastNode === next); - - lastNode = savedLastNode; - lastSubstitution = savedLastSubstitution; - // #endregion trampolinePipelineEmit + previousStatement = statement; } + + return true; } - function emitConditionalExpression(node: ConditionalExpression) { - const linesBeforeQuestion = getLinesBetweenNodes(node, node.condition, node.questionToken); - const linesAfterQuestion = getLinesBetweenNodes(node, node.questionToken, node.whenTrue); - const linesBeforeColon = getLinesBetweenNodes(node, node.whenTrue, node.colonToken); - const linesAfterColon = getLinesBetweenNodes(node, node.colonToken, node.whenFalse); + function emitBlockFunctionBody(body: Block) { + onBeforeEmitNode?.(body); + writeSpace(); + writePunctuation("{"); + increaseIndent(); - emitExpression(node.condition); - writeLinesAndIndent(linesBeforeQuestion, /*writeSpaceIfNotIndenting*/ true); - emit(node.questionToken); - writeLinesAndIndent(linesAfterQuestion, /*writeSpaceIfNotIndenting*/ true); - emitExpression(node.whenTrue); - decreaseIndentIf(linesBeforeQuestion, linesAfterQuestion); + const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body) + ? emitBlockFunctionBodyOnSingleLine + : emitBlockFunctionBodyWorker; - writeLinesAndIndent(linesBeforeColon, /*writeSpaceIfNotIndenting*/ true); - emit(node.colonToken); - writeLinesAndIndent(linesAfterColon, /*writeSpaceIfNotIndenting*/ true); - emitExpression(node.whenFalse); - decreaseIndentIf(linesBeforeColon, linesAfterColon); - } + if (emitBodyWithDetachedComments) { + emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody); + } + else { + emitBlockFunctionBody(body); + } - function emitTemplateExpression(node: TemplateExpression) { - emit(node.head); - emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans); + decreaseIndent(); + writeToken(SyntaxKind.CloseBraceToken, body.statements.end, writePunctuation, body); + onAfterEmitNode?.(body); } - function emitYieldExpression(node: YieldExpression) { - emitTokenWithComment(SyntaxKind.YieldKeyword, node.pos, writeKeyword, node); - emit(node.asteriskToken); - emitExpressionWithLeadingSpace(node.expression); + function emitBlockFunctionBodyOnSingleLine(body: Block) { + emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true); } - function emitSpreadExpression(node: SpreadElement) { - emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node); - emitExpression(node.expression); + function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) { + // Emit all the prologue directives (like "use strict"). + const statementOffset = emitPrologueDirectives(body.statements); + const pos = writer.getTextPos(); + emitHelpers(body); + if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) { + decreaseIndent(); + emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); + increaseIndent(); + } + else { + emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + } } - function emitClassExpression(node: ClassExpression) { - generateNameIfNeeded(node.name); + function emitClassDeclaration(node: ClassDeclaration) { emitClassDeclarationOrExpression(node); } - function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - emitExpression(node.expression); - emitTypeArguments(node, node.typeArguments); - } + function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { + forEach(node.members, generateMemberNames); - function emitAsExpression(node: AsExpression) { - emitExpression(node.expression); - if (node.type) { - writeSpace(); - writeKeyword("as"); + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + writeKeyword("class"); + if (node.name) { writeSpace(); - emit(node.type); + emit(node.name); } - } - - function emitNonNullExpression(node: NonNullExpression) { - emitExpression(node.expression); - writeOperator("!"); - } - - function emitMetaProperty(node: MetaProperty) { - writeToken(node.keywordToken, node.pos, writePunctuation); - writePunctuation("."); - emit(node.name); - } - // - // Misc - // + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + if (indentedFlag) { + increaseIndent(); + } - function emitTemplateSpan(node: TemplateSpan) { - emitExpression(node.expression); - emit(node.literal); - } + emitTypeParameters(node, node.typeParameters); + emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); - // - // Statements - // + writeSpace(); + writePunctuation("{"); + emitList(node, node.members, ListFormat.ClassMembers); + writePunctuation("}"); - function emitBlock(node: Block) { - emitBlockStatements(node, /*forceSingleLine*/ !node.multiLine && isEmptyBlock(node)); + if (indentedFlag) { + decreaseIndent(); + } } - function emitBlockStatements(node: BlockLike, forceSingleLine: boolean) { - emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, /*contextNode*/ node); - const format = forceSingleLine || getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineBlockStatements : ListFormat.MultiLineBlockStatements; - emitList(node, node.statements, format); - emitTokenWithComment(SyntaxKind.CloseBraceToken, node.statements.end, writePunctuation, /*contextNode*/ node, /*indentLeading*/ !!(format & ListFormat.MultiLine)); + function emitInterfaceDeclaration(node: InterfaceDeclaration) { + emitDecorators(node, node.decorators); + emitModifiers(node, node.modifiers); + writeKeyword("interface"); + writeSpace(); + emit(node.name); + emitTypeParameters(node, node.typeParameters); + emitList(node, node.heritageClauses, ListFormat.HeritageClauses); + writeSpace(); + writePunctuation("{"); + emitList(node, node.members, ListFormat.InterfaceMembers); + writePunctuation("}"); } - function emitVariableStatement(node: VariableStatement) { + function emitTypeAliasDeclaration(node: TypeAliasDeclaration) { + emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); - emit(node.declarationList); + writeKeyword("type"); + writeSpace(); + emit(node.name); + emitTypeParameters(node, node.typeParameters); + writeSpace(); + writePunctuation("="); + writeSpace(); + emit(node.type); writeTrailingSemicolon(); } - function emitEmptyStatement(isEmbeddedStatement: boolean) { - // While most trailing semicolons are possibly insignificant, an embedded "empty" - // statement is significant and cannot be elided by a trailing-semicolon-omitting writer. - if (isEmbeddedStatement) { - writePunctuation(";"); - } - else { - writeTrailingSemicolon(); - } - } - - - function emitExpressionStatement(node: ExpressionStatement) { - emitExpression(node.expression); - // Emit semicolon in non json files - // or if json file that created synthesized expression(eg.define expression statement when --out and amd code generation) - if (!isJsonSourceFile(currentSourceFile!) || nodeIsSynthesized(node.expression)) { - writeTrailingSemicolon(); - } - } - - function emitIfStatement(node: IfStatement) { - const openParenPos = emitTokenWithComment(SyntaxKind.IfKeyword, node.pos, writeKeyword, node); + function emitEnumDeclaration(node: EnumDeclaration) { + emitModifiers(node, node.modifiers); + writeKeyword("enum"); writeSpace(); - emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emitExpression(node.expression); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); - emitEmbeddedStatement(node, node.thenStatement); - if (node.elseStatement) { - writeLineOrSpace(node, node.thenStatement, node.elseStatement); - emitTokenWithComment(SyntaxKind.ElseKeyword, node.thenStatement.end, writeKeyword, node); - if (node.elseStatement.kind === SyntaxKind.IfStatement) { - writeSpace(); - emit(node.elseStatement); - } - else { - emitEmbeddedStatement(node, node.elseStatement); - } - } - } + emit(node.name); - function emitWhileClause(node: WhileStatement | DoStatement, startPos: number) { - const openParenPos = emitTokenWithComment(SyntaxKind.WhileKeyword, startPos, writeKeyword, node); writeSpace(); - emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emitExpression(node.expression); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); + writePunctuation("{"); + emitList(node, node.members, ListFormat.EnumMembers); + writePunctuation("}"); } - function emitDoStatement(node: DoStatement) { - emitTokenWithComment(SyntaxKind.DoKeyword, node.pos, writeKeyword, node); - emitEmbeddedStatement(node, node.statement); - if (isBlock(node.statement) && !preserveSourceNewlines) { + function emitModuleDeclaration(node: ModuleDeclaration) { + emitModifiers(node, node.modifiers); + if (~node.flags & NodeFlags.GlobalAugmentation) { + writeKeyword(node.flags & NodeFlags.Namespace ? "namespace" : "module"); writeSpace(); } - else { - writeLineOrSpace(node, node.statement, node.expression); + emit(node.name); + + let body = node.body; + if (!body) return writeTrailingSemicolon(); + while (body.kind === SyntaxKind.ModuleDeclaration) { + writePunctuation("."); + emit((body).name); + body = (body).body!; } - emitWhileClause(node, node.statement.end); - writeTrailingSemicolon(); + writeSpace(); + emit(body); } - function emitWhileStatement(node: WhileStatement) { - emitWhileClause(node, node.pos); - emitEmbeddedStatement(node, node.statement); + function emitModuleBlock(node: ModuleBlock) { + pushNameGenerationScope(node); + forEach(node.statements, generateNames); + emitBlockStatements(node, /*forceSingleLine*/ isEmptyBlock(node)); + popNameGenerationScope(node); } - function emitForStatement(node: ForStatement) { - const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node); - writeSpace(); - let pos = emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, /*contextNode*/ node); - emitForBinding(node.initializer); - pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.initializer ? node.initializer.end : pos, writePunctuation, node); - emitExpressionWithLeadingSpace(node.condition); - pos = emitTokenWithComment(SyntaxKind.SemicolonToken, node.condition ? node.condition.end : pos, writePunctuation, node); - emitExpressionWithLeadingSpace(node.incrementor); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.incrementor ? node.incrementor.end : pos, writePunctuation, node); - emitEmbeddedStatement(node, node.statement); + function emitCaseBlock(node: CaseBlock) { + emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node); + emitList(node, node.clauses, ListFormat.CaseBlockClauses); + emitTokenWithComment(SyntaxKind.CloseBraceToken, node.clauses.end, writePunctuation, node, /*indentLeading*/ true); } - function emitForInStatement(node: ForInStatement) { - const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node); + function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { + emitModifiers(node, node.modifiers); + emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node); writeSpace(); - emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emitForBinding(node.initializer); + if (node.isTypeOnly) { + emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node); + writeSpace(); + } + emit(node.name); writeSpace(); - emitTokenWithComment(SyntaxKind.InKeyword, node.initializer.end, writeKeyword, node); + emitTokenWithComment(SyntaxKind.EqualsToken, node.name.end, writePunctuation, node); writeSpace(); - emitExpression(node.expression); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); - emitEmbeddedStatement(node, node.statement); + emitModuleReference(node.moduleReference); + writeTrailingSemicolon(); } - function emitForOfStatement(node: ForOfStatement) { - const openParenPos = emitTokenWithComment(SyntaxKind.ForKeyword, node.pos, writeKeyword, node); + function emitModuleReference(node: ModuleReference) { + if (node.kind === SyntaxKind.Identifier) { + emit(node); + } + else { + emit(node); + } + } + + function emitImportDeclaration(node: ImportDeclaration) { + emitModifiers(node, node.modifiers); + emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node); writeSpace(); - emitWithTrailingSpace(node.awaitModifier); - emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emitForBinding(node.initializer); + if (node.importClause) { + emit(node.importClause); + writeSpace(); + emitTokenWithComment(SyntaxKind.FromKeyword, node.importClause.end, writeKeyword, node); + writeSpace(); + } + emit(node.moduleSpecifier); + writeTrailingSemicolon(); + } + + function emitImportClause(node: ImportClause) { + if (node.isTypeOnly) { + emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node); + writeSpace(); + } + emit(node.name); + if (node.name && node.namedBindings) { + emitTokenWithComment(SyntaxKind.CommaToken, node.name.end, writePunctuation, node); + writeSpace(); + } + emit(node.namedBindings); + } + + function emitNamespaceImport(node: NamespaceImport) { + const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node); writeSpace(); - emitTokenWithComment(SyntaxKind.OfKeyword, node.initializer.end, writeKeyword, node); + emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node); writeSpace(); - emitExpression(node.expression); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); - emitEmbeddedStatement(node, node.statement); + emit(node.name); } - function emitForBinding(node: VariableDeclarationList | Expression | undefined) { - if (node !== undefined) { - if (node.kind === SyntaxKind.VariableDeclarationList) { - emit(node); - } - else { - emitExpression(node); - } - } + function emitNamedImports(node: NamedImports) { + emitNamedImportsOrExports(node); } - function emitContinueStatement(node: ContinueStatement) { - emitTokenWithComment(SyntaxKind.ContinueKeyword, node.pos, writeKeyword, node); - emitWithLeadingSpace(node.label); - writeTrailingSemicolon(); + function emitImportSpecifier(node: ImportSpecifier) { + emitImportOrExportSpecifier(node); } - function emitBreakStatement(node: BreakStatement) { - emitTokenWithComment(SyntaxKind.BreakKeyword, node.pos, writeKeyword, node); - emitWithLeadingSpace(node.label); + function emitExportAssignment(node: ExportAssignment) { + const nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node); + writeSpace(); + if (node.isExportEquals) { + emitTokenWithComment(SyntaxKind.EqualsToken, nextPos, writeOperator, node); + } + else { + emitTokenWithComment(SyntaxKind.DefaultKeyword, nextPos, writeKeyword, node); + } + writeSpace(); + emit(node.expression); writeTrailingSemicolon(); } - function emitTokenWithComment(token: SyntaxKind, pos: number, writer: (s: string) => void, contextNode: Node, indentLeading?: boolean) { - const node = getParseTreeNode(contextNode); - const isSimilarNode = node && node.kind === contextNode.kind; - const startPos = pos; - if (isSimilarNode && currentSourceFile) { - pos = skipTrivia(currentSourceFile.text, pos); + function emitExportDeclaration(node: ExportDeclaration) { + let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node); + writeSpace(); + if (node.isTypeOnly) { + nextPos = emitTokenWithComment(SyntaxKind.TypeKeyword, nextPos, writeKeyword, node); + writeSpace(); } - if (isSimilarNode && contextNode.pos !== startPos) { - const needsIndent = indentLeading && currentSourceFile && !positionsAreOnSameLine(startPos, pos, currentSourceFile); - if (needsIndent) { - increaseIndent(); - } - emitLeadingCommentsOfPosition(startPos); - if (needsIndent) { - decreaseIndent(); - } + if (node.exportClause) { + emit(node.exportClause); } - pos = writeTokenText(token, writer, pos); - if (isSimilarNode && contextNode.end !== pos) { - const isJsxExprContext = contextNode.kind === SyntaxKind.JsxExpression; - emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ !isJsxExprContext, /*forceNoNewline*/ isJsxExprContext); + else { + nextPos = emitTokenWithComment(SyntaxKind.AsteriskToken, nextPos, writePunctuation, node); + } + if (node.moduleSpecifier) { + writeSpace(); + const fromPos = node.exportClause ? node.exportClause.end : nextPos; + emitTokenWithComment(SyntaxKind.FromKeyword, fromPos, writeKeyword, node); + writeSpace(); + emit(node.moduleSpecifier); } - return pos; - } - - function emitReturnStatement(node: ReturnStatement) { - emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, writeKeyword, /*contextNode*/ node); - emitExpressionWithLeadingSpace(node.expression); writeTrailingSemicolon(); } - function emitWithStatement(node: WithStatement) { - const openParenPos = emitTokenWithComment(SyntaxKind.WithKeyword, node.pos, writeKeyword, node); + function emitNamespaceExportDeclaration(node: NamespaceExportDeclaration) { + let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node); writeSpace(); - emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emitExpression(node.expression); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); - emitEmbeddedStatement(node, node.statement); + nextPos = emitTokenWithComment(SyntaxKind.AsKeyword, nextPos, writeKeyword, node); + writeSpace(); + nextPos = emitTokenWithComment(SyntaxKind.NamespaceKeyword, nextPos, writeKeyword, node); + writeSpace(); + emit(node.name); + writeTrailingSemicolon(); } - function emitSwitchStatement(node: SwitchStatement) { - const openParenPos = emitTokenWithComment(SyntaxKind.SwitchKeyword, node.pos, writeKeyword, node); + function emitNamespaceExport(node: NamespaceExport) { + const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node); writeSpace(); - emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emitExpression(node.expression); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); + emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node); writeSpace(); - emit(node.caseBlock); + emit(node.name); } - function emitLabeledStatement(node: LabeledStatement) { - emit(node.label); - emitTokenWithComment(SyntaxKind.ColonToken, node.label.end, writePunctuation, node); - writeSpace(); - emit(node.statement); + function emitNamedExports(node: NamedExports) { + emitNamedImportsOrExports(node); } - function emitThrowStatement(node: ThrowStatement) { - emitTokenWithComment(SyntaxKind.ThrowKeyword, node.pos, writeKeyword, node); - emitExpressionWithLeadingSpace(node.expression); - writeTrailingSemicolon(); + function emitExportSpecifier(node: ExportSpecifier) { + emitImportOrExportSpecifier(node); } - function emitTryStatement(node: TryStatement) { - emitTokenWithComment(SyntaxKind.TryKeyword, node.pos, writeKeyword, node); - writeSpace(); - emit(node.tryBlock); - if (node.catchClause) { - writeLineOrSpace(node, node.tryBlock, node.catchClause); - emit(node.catchClause); - } - if (node.finallyBlock) { - writeLineOrSpace(node, node.catchClause || node.tryBlock, node.finallyBlock); - emitTokenWithComment(SyntaxKind.FinallyKeyword, (node.catchClause || node.tryBlock).end, writeKeyword, node); + function emitNamedImportsOrExports(node: NamedImportsOrExports) { + writePunctuation("{"); + emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements); + writePunctuation("}"); + } + + function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { + if (node.propertyName) { + emit(node.propertyName); + writeSpace(); + emitTokenWithComment(SyntaxKind.AsKeyword, node.propertyName.end, writeKeyword, node); writeSpace(); - emit(node.finallyBlock); } - } - function emitDebuggerStatement(node: DebuggerStatement) { - writeToken(SyntaxKind.DebuggerKeyword, node.pos, writeKeyword); - writeTrailingSemicolon(); + emit(node.name); } // - // Declarations + // Module references // - function emitVariableDeclaration(node: VariableDeclaration) { - emit(node.name); - emit(node.exclamationToken); - emitTypeAnnotation(node.type); - emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node); + function emitExternalModuleReference(node: ExternalModuleReference) { + writeKeyword("require"); + writePunctuation("("); + emit(node.expression); + writePunctuation(")"); } - function emitVariableDeclarationList(node: VariableDeclarationList) { - writeKeyword(isLet(node) ? "let" : isVarConst(node) ? "const" : "var"); - writeSpace(); - emitList(node, node.declarations, ListFormat.VariableDeclarationList); - } + // + // JSX + // - function emitFunctionDeclaration(node: FunctionDeclaration) { - emitFunctionDeclarationOrExpression(node); + function emitJsxElement(node: JsxElement) { + emit(node.openingElement); + emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); + emit(node.closingElement); } - function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - writeKeyword("function"); - emit(node.asteriskToken); + function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { + writePunctuation("<"); + emitJsxTagName(node.tagName); + emitTypeArguments(node, node.typeArguments); writeSpace(); - emitIdentifierName(node.name); - emitSignatureAndBody(node, emitSignatureHead); + emit(node.attributes); + writePunctuation("/>"); } - function emitBlockCallback(_hint: EmitHint, body: Node): void { - emitBlockFunctionBody(body); + function emitJsxFragment(node: JsxFragment) { + emit(node.openingFragment); + emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); + emit(node.closingFragment); } - function emitSignatureAndBody(node: FunctionLikeDeclaration, emitSignatureHead: (node: SignatureDeclaration) => void) { - const body = node.body; - if (body) { - if (isBlock(body)) { - const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } - - pushNameGenerationScope(node); - forEach(node.parameters, generateNames); - generateNames(node.body); - - emitSignatureHead(node); - if (onEmitNode) { - onEmitNode(EmitHint.Unspecified, body, emitBlockCallback); - } - else { - emitBlockFunctionBody(body); - } - popNameGenerationScope(node); + function emitJsxOpeningElementOrFragment(node: JsxOpeningElement | JsxOpeningFragment) { + writePunctuation("<"); - if (indentedFlag) { - decreaseIndent(); - } - } - else { - emitSignatureHead(node); + if (isJsxOpeningElement(node)) { + const indented = writeLineSeparatorsAndIndentBefore(node.tagName, node); + emitJsxTagName(node.tagName); + emitTypeArguments(node, node.typeArguments); + if (node.attributes.properties && node.attributes.properties.length > 0) { writeSpace(); - emitExpression(body); } - } - else { - emitSignatureHead(node); - writeTrailingSemicolon(); + emit(node.attributes); + writeLineSeparatorsAfter(node.attributes, node); + decreaseIndentIf(indented); } + writePunctuation(">"); } - function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { - emitTypeParameters(node, node.typeParameters); - emitParameters(node, node.parameters); - emitTypeAnnotation(node.type); + function emitJsxText(node: JsxText) { + writer.writeLiteral(node.text); } - function shouldEmitBlockFunctionBodyOnSingleLine(body: Block) { - // We must emit a function body as a single-line body in the following case: - // * The body has NodeEmitFlags.SingleLine specified. - - // We must emit a function body as a multi-line body in the following cases: - // * The body is explicitly marked as multi-line. - // * A non-synthesized body's start and end position are on different lines. - // * Any statement in the body starts on a new line. - - if (getEmitFlags(body) & EmitFlags.SingleLine) { - return true; - } - - if (body.multiLine) { - return false; - } - - if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile!)) { - return false; - } - - if (getLeadingLineTerminatorCount(body, body.statements, ListFormat.PreserveLines) - || getClosingLineTerminatorCount(body, body.statements, ListFormat.PreserveLines)) { - return false; + function emitJsxClosingElementOrFragment(node: JsxClosingElement | JsxClosingFragment) { + writePunctuation(""); + } - let previousStatement: Statement | undefined; - for (const statement of body.statements) { - if (getSeparatingLineTerminatorCount(previousStatement, statement, ListFormat.PreserveLines) > 0) { - return false; - } + function emitJsxAttributes(node: JsxAttributes) { + emitList(node, node.properties, ListFormat.JsxElementAttributes); + } - previousStatement = statement; - } + function emitJsxAttributeValue(node: StringLiteral | JsxExpression): void { + const emitCallback = isStringLiteral(node) ? emitStringLiteralWithJsxAttributeEscape : emitWorker; + emitWithContext(node, emitCallback); + } - return true; + function emitJsxAttribute(node: JsxAttribute) { + emit(node.name); + emitNodeWithPrefix("=", writePunctuation, node.initializer, emitJsxAttributeValue); } - function emitBlockFunctionBody(body: Block) { - writeSpace(); - writePunctuation("{"); - increaseIndent(); + function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { + writePunctuation("{..."); + emit(node.expression); + writePunctuation("}"); + } - const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body) - ? emitBlockFunctionBodyOnSingleLine - : emitBlockFunctionBodyWorker; + function hasTrailingCommentsAtPosition(pos: number) { + let result = false; + forEachTrailingCommentRange(currentSourceFile?.text || "", pos + 1, () => result = true); + return result; + } - if (emitBodyWithDetachedComments) { - emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody); - } - else { - emitBlockFunctionBody(body); - } + function hasLeadingCommentsAtPosition(pos: number) { + let result = false; + forEachLeadingCommentRange(currentSourceFile?.text || "", pos + 1, () => result = true); + return result; + } - decreaseIndent(); - writeToken(SyntaxKind.CloseBraceToken, body.statements.end, writePunctuation, body); + function hasCommentsAtPosition(pos: number) { + return hasTrailingCommentsAtPosition(pos) || hasLeadingCommentsAtPosition(pos); } - function emitBlockFunctionBodyOnSingleLine(body: Block) { - emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true); + function emitJsxExpression(node: JsxExpression) { + if (node.expression || (!commentsDisabled && !nodeIsSynthesized(node) && hasCommentsAtPosition(node.pos))) { // preserve empty expressions if they contain comments! + const isMultiline = currentSourceFile && !nodeIsSynthesized(node) && getLineAndCharacterOfPosition(currentSourceFile, node.pos).line !== getLineAndCharacterOfPosition(currentSourceFile, node.end).line; + if (isMultiline) { + writer.increaseIndent(); + } + const end = emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node); + emit(node.dotDotDotToken); + emit(node.expression); + emitTokenWithComment(SyntaxKind.CloseBraceToken, node.expression?.end || end, writePunctuation, node); + if (isMultiline) { + writer.decreaseIndent(); + } + } } - function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) { - // Emit all the prologue directives (like "use strict"). - const statementOffset = emitPrologueDirectives(body.statements); - const pos = writer.getTextPos(); - emitHelpers(body); - if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) { - decreaseIndent(); - emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); - increaseIndent(); + function emitJsxTagName(node: JsxTagNameExpression) { + if (node.kind === SyntaxKind.Identifier) { + emit(node); } else { - emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + emit(node); } } - function emitClassDeclaration(node: ClassDeclaration) { - emitClassDeclarationOrExpression(node); - } - - function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { - forEach(node.members, generateMemberNames); + // + // Clauses + // - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - writeKeyword("class"); - if (node.name) { - writeSpace(); - emitIdentifierName(node.name); - } + function emitCaseClause(node: CaseClause) { + emitTokenWithComment(SyntaxKind.CaseKeyword, node.pos, writeKeyword, node); + writeSpace(); + emit(node.expression); - const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - if (indentedFlag) { - increaseIndent(); - } + emitCaseOrDefaultClauseRest(node, node.statements, node.expression.end); + } - emitTypeParameters(node, node.typeParameters); - emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); + function emitDefaultClause(node: DefaultClause) { + const pos = emitTokenWithComment(SyntaxKind.DefaultKeyword, node.pos, writeKeyword, node); + emitCaseOrDefaultClauseRest(node, node.statements, pos); + } - writeSpace(); - writePunctuation("{"); - emitList(node, node.members, ListFormat.ClassMembers); - writePunctuation("}"); + function emitCaseOrDefaultClauseRest(parentNode: Node, statements: NodeArray, colonPos: number) { + const emitAsSingleStatement = + statements.length === 1 && + ( + // treat synthesized nodes as located on the same line for emit purposes + nodeIsSynthesized(parentNode) || + nodeIsSynthesized(statements[0]) || + rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile!) + ); - if (indentedFlag) { - decreaseIndent(); + let format = ListFormat.CaseOrDefaultClauseStatements; + if (emitAsSingleStatement) { + writeToken(SyntaxKind.ColonToken, colonPos, writePunctuation, parentNode); + writeSpace(); + format &= ~(ListFormat.MultiLine | ListFormat.Indented); } + else { + emitTokenWithComment(SyntaxKind.ColonToken, colonPos, writePunctuation, parentNode); + } + emitList(parentNode, statements, format); } - function emitInterfaceDeclaration(node: InterfaceDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - writeKeyword("interface"); + function emitHeritageClause(node: HeritageClause) { writeSpace(); - emit(node.name); - emitTypeParameters(node, node.typeParameters); - emitList(node, node.heritageClauses, ListFormat.HeritageClauses); + writeTokenText(node.token, writeKeyword); writeSpace(); - writePunctuation("{"); - emitList(node, node.members, ListFormat.InterfaceMembers); - writePunctuation("}"); + emitList(node, node.types, ListFormat.HeritageClauseTypes); } - function emitTypeAliasDeclaration(node: TypeAliasDeclaration) { - emitDecorators(node, node.decorators); - emitModifiers(node, node.modifiers); - writeKeyword("type"); - writeSpace(); - emit(node.name); - emitTypeParameters(node, node.typeParameters); - writeSpace(); - writePunctuation("="); + function emitCatchClause(node: CatchClause) { + const openParenPos = emitTokenWithComment(SyntaxKind.CatchKeyword, node.pos, writeKeyword, node); writeSpace(); - emit(node.type); - writeTrailingSemicolon(); + if (node.variableDeclaration) { + emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); + emit(node.variableDeclaration); + emitTokenWithComment(SyntaxKind.CloseParenToken, node.variableDeclaration.end, writePunctuation, node); + writeSpace(); + } + emit(node.block); } - function emitEnumDeclaration(node: EnumDeclaration) { - emitModifiers(node, node.modifiers); - writeKeyword("enum"); - writeSpace(); - emit(node.name); + // + // Property assignments + // + function emitPropertyAssignment(node: PropertyAssignment) { + emit(node.name); + writePunctuation(":"); writeSpace(); - writePunctuation("{"); - emitList(node, node.members, ListFormat.EnumMembers); - writePunctuation("}"); + // This is to ensure that we emit comment in the following case: + // For example: + // obj = { + // id: /*comment1*/ ()=>void + // } + // "comment1" is not considered to be leading comment for node.initializer + // but rather a trailing comment on the previous node. + const initializer = node.initializer; + if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { + const commentRange = getCommentRange(initializer); + emitTrailingCommentsOfPosition(commentRange.pos); + } + emit(initializer); } - function emitModuleDeclaration(node: ModuleDeclaration) { - emitModifiers(node, node.modifiers); - if (~node.flags & NodeFlags.GlobalAugmentation) { - writeKeyword(node.flags & NodeFlags.Namespace ? "namespace" : "module"); + function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { + emit(node.name); + if (node.objectAssignmentInitializer) { + writeSpace(); + writePunctuation("="); writeSpace(); + emit(node.objectAssignmentInitializer); } - emit(node.name); + } - let body = node.body; - if (!body) return writeTrailingSemicolon(); - while (body.kind === SyntaxKind.ModuleDeclaration) { - writePunctuation("."); - emit((body).name); - body = (body).body!; + function emitSpreadAssignment(node: SpreadAssignment) { + if (node.expression) { + emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node); + emit(node.expression); } + } + + // + // Enum + // + function emitEnumMember(node: EnumMember) { + emit(node.name); + emitInitializer(node.initializer, node.name.end, node); + } + + // + // JSDoc + // + function emitJSDoc(node: JSDoc) { + write("/**"); + if (node.comment) { + const lines = node.comment.split(/\r\n?|\n/g); + for (const line of lines) { + writeLine(); + writeSpace(); + writePunctuation("*"); + writeSpace(); + write(line); + } + } + if (node.tags) { + if (node.tags.length === 1 && node.tags[0].kind === SyntaxKind.JSDocTypeTag && !node.comment) { + writeSpace(); + emit(node.tags[0]); + } + else { + emitList(node, node.tags, ListFormat.JSDocComment); + } + } writeSpace(); - emit(body); + write("*/"); } - function emitModuleBlock(node: ModuleBlock) { - pushNameGenerationScope(node); - forEach(node.statements, generateNames); - emitBlockStatements(node, /*forceSingleLine*/ isEmptyBlock(node)); - popNameGenerationScope(node); + function emitJSDocSimpleTypedTag(tag: JSDocTypeTag | JSDocThisTag | JSDocEnumTag | JSDocReturnTag) { + emitJSDocTagName(tag.tagName); + emitJSDocTypeExpression(tag.typeExpression); + emitJSDocComment(tag.comment); } - function emitCaseBlock(node: CaseBlock) { - emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node); - emitList(node, node.clauses, ListFormat.CaseBlockClauses); - emitTokenWithComment(SyntaxKind.CloseBraceToken, node.clauses.end, writePunctuation, node, /*indentLeading*/ true); + function emitJSDocSeeTag(tag: JSDocSeeTag) { + emitJSDocTagName(tag.tagName); + emit(tag.name); + emitJSDocComment(tag.comment); } - function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { - emitModifiers(node, node.modifiers); - emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node); + function emitJSDocNameReference(node: JSDocNameReference) { writeSpace(); - if (node.isTypeOnly) { - emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node); - writeSpace(); - } + writePunctuation("{"); emit(node.name); - writeSpace(); - emitTokenWithComment(SyntaxKind.EqualsToken, node.name.end, writePunctuation, node); - writeSpace(); - emitModuleReference(node.moduleReference); - writeTrailingSemicolon(); + writePunctuation("}"); } - function emitModuleReference(node: ModuleReference) { - if (node.kind === SyntaxKind.Identifier) { - emitExpression(node); - } - else { - emit(node); - } + function emitJSDocHeritageTag(tag: JSDocImplementsTag | JSDocAugmentsTag) { + emitJSDocTagName(tag.tagName); + writeSpace(); + writePunctuation("{"); + emit(tag.class); + writePunctuation("}"); + emitJSDocComment(tag.comment); } - function emitImportDeclaration(node: ImportDeclaration) { - emitModifiers(node, node.modifiers); - emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node); + function emitJSDocTemplateTag(tag: JSDocTemplateTag) { + emitJSDocTagName(tag.tagName); + emitJSDocTypeExpression(tag.constraint); writeSpace(); - if (node.importClause) { - emit(node.importClause); - writeSpace(); - emitTokenWithComment(SyntaxKind.FromKeyword, node.importClause.end, writeKeyword, node); - writeSpace(); - } - emitExpression(node.moduleSpecifier); - writeTrailingSemicolon(); + emitList(tag, tag.typeParameters, ListFormat.CommaListElements); + emitJSDocComment(tag.comment); } - function emitImportClause(node: ImportClause) { - if (node.isTypeOnly) { - emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node); - writeSpace(); + function emitJSDocTypedefTag(tag: JSDocTypedefTag) { + emitJSDocTagName(tag.tagName); + if (tag.typeExpression) { + if (tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) { + emitJSDocTypeExpression(tag.typeExpression); + } + else { + writeSpace(); + writePunctuation("{"); + write("Object"); + if (tag.typeExpression.isArrayType) { + writePunctuation("["); + writePunctuation("]"); + } + writePunctuation("}"); + } } - emit(node.name); - if (node.name && node.namedBindings) { - emitTokenWithComment(SyntaxKind.CommaToken, node.name.end, writePunctuation, node); + if (tag.fullName) { writeSpace(); + emit(tag.fullName); + } + emitJSDocComment(tag.comment); + if (tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeLiteral) { + emitJSDocTypeLiteral(tag.typeExpression); } - emit(node.namedBindings); } - function emitNamespaceImport(node: NamespaceImport) { - const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node); - writeSpace(); - emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node); - writeSpace(); - emit(node.name); + function emitJSDocCallbackTag(tag: JSDocCallbackTag) { + emitJSDocTagName(tag.tagName); + if (tag.name) { + writeSpace(); + emit(tag.name); + } + emitJSDocComment(tag.comment); + emitJSDocSignature(tag.typeExpression); } - function emitNamedImports(node: NamedImports) { - emitNamedImportsOrExports(node); + function emitJSDocSimpleTag(tag: JSDocTag) { + emitJSDocTagName(tag.tagName); + emitJSDocComment(tag.comment); } - function emitImportSpecifier(node: ImportSpecifier) { - emitImportOrExportSpecifier(node); + function emitJSDocTypeLiteral(lit: JSDocTypeLiteral) { + emitList(lit, factory.createNodeArray(lit.jsDocPropertyTags), ListFormat.JSDocComment); } - function emitExportAssignment(node: ExportAssignment) { - const nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node); - writeSpace(); - if (node.isExportEquals) { - emitTokenWithComment(SyntaxKind.EqualsToken, nextPos, writeOperator, node); + function emitJSDocSignature(sig: JSDocSignature) { + if (sig.typeParameters) { + emitList(sig, factory.createNodeArray(sig.typeParameters), ListFormat.JSDocComment); } - else { - emitTokenWithComment(SyntaxKind.DefaultKeyword, nextPos, writeKeyword, node); + if (sig.parameters) { + emitList(sig, factory.createNodeArray(sig.parameters), ListFormat.JSDocComment); + } + if (sig.type) { + writeLine(); + writeSpace(); + writePunctuation("*"); + writeSpace(); + emit(sig.type); } - writeSpace(); - emitExpression(node.expression); - writeTrailingSemicolon(); } - function emitExportDeclaration(node: ExportDeclaration) { - let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node); + function emitJSDocPropertyLikeTag(param: JSDocPropertyLikeTag) { + emitJSDocTagName(param.tagName); + emitJSDocTypeExpression(param.typeExpression); writeSpace(); - if (node.isTypeOnly) { - nextPos = emitTokenWithComment(SyntaxKind.TypeKeyword, nextPos, writeKeyword, node); - writeSpace(); - } - if (node.exportClause) { - emit(node.exportClause); + if (param.isBracketed) { + writePunctuation("["); } - else { - nextPos = emitTokenWithComment(SyntaxKind.AsteriskToken, nextPos, writePunctuation, node); + emit(param.name); + if (param.isBracketed) { + writePunctuation("]"); } - if (node.moduleSpecifier) { + emitJSDocComment(param.comment); + } + + function emitJSDocTagName(tagName: Identifier) { + writePunctuation("@"); + emit(tagName); + } + + function emitJSDocComment(comment: string | undefined) { + if (comment) { writeSpace(); - const fromPos = node.exportClause ? node.exportClause.end : nextPos; - emitTokenWithComment(SyntaxKind.FromKeyword, fromPos, writeKeyword, node); + write(comment); + } + } + + function emitJSDocTypeExpression(typeExpression: JSDocTypeExpression | undefined) { + if (typeExpression) { writeSpace(); - emitExpression(node.moduleSpecifier); + writePunctuation("{"); + emit(typeExpression.type); + writePunctuation("}"); } - writeTrailingSemicolon(); } - function emitNamespaceExportDeclaration(node: NamespaceExportDeclaration) { - let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node); - writeSpace(); - nextPos = emitTokenWithComment(SyntaxKind.AsKeyword, nextPos, writeKeyword, node); - writeSpace(); - nextPos = emitTokenWithComment(SyntaxKind.NamespaceKeyword, nextPos, writeKeyword, node); - writeSpace(); - emit(node.name); - writeTrailingSemicolon(); + // + // Top-level nodes + // + + function emitSourceFile(node: SourceFile) { + writeLine(); + const statements = node.statements; + if (emitBodyWithDetachedComments) { + // Emit detached comment if there are no prologue directives or if the first node is synthesized. + // The synthesized node will have no leading comment so some comments may be missed. + const shouldEmitDetachedComment = statements.length === 0 || + !isPrologueDirective(statements[0]) || + nodeIsSynthesized(statements[0]); + if (shouldEmitDetachedComment) { + emitBodyWithDetachedComments(node, statements, emitSourceFileWorker); + return; + } + } + emitSourceFileWorker(node); } - function emitNamespaceExport(node: NamespaceExport) { - const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node); - writeSpace(); - emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node); - writeSpace(); - emit(node.name); + function emitSyntheticTripleSlashReferencesIfNeeded(node: Bundle) { + emitTripleSlashDirectives(!!node.hasNoDefaultLib, node.syntheticFileReferences || [], node.syntheticTypeReferences || [], node.syntheticLibReferences || []); + for (const prepend of node.prepends) { + if (isUnparsedSource(prepend) && prepend.syntheticReferences) { + for (const ref of prepend.syntheticReferences) { + emit(ref); + writeLine(); + } + } + } } - function emitNamedExports(node: NamedExports) { - emitNamedImportsOrExports(node); + function emitTripleSlashDirectivesIfNeeded(node: SourceFile) { + if (node.isDeclarationFile) emitTripleSlashDirectives(node.hasNoDefaultLib, node.referencedFiles, node.typeReferenceDirectives, node.libReferenceDirectives); } - function emitExportSpecifier(node: ExportSpecifier) { - emitImportOrExportSpecifier(node); + function emitTripleSlashDirectives(hasNoDefaultLib: boolean, files: readonly FileReference[], types: readonly FileReference[], libs: readonly FileReference[]) { + if (hasNoDefaultLib) { + const pos = writer.getTextPos(); + writeComment(`/// `); + if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.NoDefaultLib }); + writeLine(); + } + if (currentSourceFile && currentSourceFile.moduleName) { + writeComment(`/// `); + writeLine(); + } + if (currentSourceFile && currentSourceFile.amdDependencies) { + for (const dep of currentSourceFile.amdDependencies) { + if (dep.name) { + writeComment(`/// `); + } + else { + writeComment(`/// `); + } + writeLine(); + } + } + for (const directive of files) { + const pos = writer.getTextPos(); + writeComment(`/// `); + if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Reference, data: directive.fileName }); + writeLine(); + } + for (const directive of types) { + const pos = writer.getTextPos(); + writeComment(`/// `); + if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Type, data: directive.fileName }); + writeLine(); + } + for (const directive of libs) { + const pos = writer.getTextPos(); + writeComment(`/// `); + if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Lib, data: directive.fileName }); + writeLine(); + } } - function emitNamedImportsOrExports(node: NamedImportsOrExports) { - writePunctuation("{"); - emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements); - writePunctuation("}"); + function emitSourceFileWorker(node: SourceFile) { + const statements = node.statements; + pushNameGenerationScope(node); + forEach(node.statements, generateNames); + emitHelpers(node); + const index = findIndex(statements, statement => !isPrologueDirective(statement)); + emitTripleSlashDirectivesIfNeeded(node); + emitList(node, statements, ListFormat.MultiLine, index === -1 ? statements.length : index); + popNameGenerationScope(node); } - function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { - if (node.propertyName) { - emit(node.propertyName); - writeSpace(); - emitTokenWithComment(SyntaxKind.AsKeyword, node.propertyName.end, writeKeyword, node); - writeSpace(); - } + // Transformation nodes - emit(node.name); + function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { + emit(node.expression); } - // - // Module references - // - - function emitExternalModuleReference(node: ExternalModuleReference) { - writeKeyword("require"); - writePunctuation("("); - emitExpression(node.expression); - writePunctuation(")"); + function emitCommaList(node: CommaListExpression) { + emitList(node, node.elements, ListFormat.CommaListElements); } - // - // JSX - // + /** + * Emits any prologue directives at the start of a Statement list, returning the + * number of prologue directives written to the output. + */ + function emitPrologueDirectives(statements: readonly Node[], sourceFile?: SourceFile, seenPrologueDirectives?: Set, recordBundleFileSection?: true): number { + let needsToSetSourceFile = !!sourceFile; + for (let i = 0; i < statements.length; i++) { + const statement = statements[i]; + if (isPrologueDirective(statement)) { + const shouldEmitPrologueDirective = seenPrologueDirectives ? !seenPrologueDirectives.has(statement.expression.text) : true; + if (shouldEmitPrologueDirective) { + if (needsToSetSourceFile) { + needsToSetSourceFile = false; + setSourceFile(sourceFile); + } + writeLine(); + const pos = writer.getTextPos(); + emit(statement); + if (recordBundleFileSection && bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prologue, data: statement.expression.text }); + if (seenPrologueDirectives) { + seenPrologueDirectives.add(statement.expression.text); + } + } + } + else { + // return index of the first non prologue directive + return i; + } + } - function emitJsxElement(node: JsxElement) { - emit(node.openingElement); - emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); - emit(node.closingElement); + return statements.length; } - function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { - writePunctuation("<"); - emitJsxTagName(node.tagName); - emitTypeArguments(node, node.typeArguments); - writeSpace(); - emit(node.attributes); - writePunctuation("/>"); + function emitUnparsedPrologues(prologues: readonly UnparsedPrologue[], seenPrologueDirectives: Set) { + for (const prologue of prologues) { + if (!seenPrologueDirectives.has(prologue.data)) { + writeLine(); + const pos = writer.getTextPos(); + emit(prologue); + if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prologue, data: prologue.data }); + if (seenPrologueDirectives) { + seenPrologueDirectives.add(prologue.data); + } + } + } } - function emitJsxFragment(node: JsxFragment) { - emit(node.openingFragment); - emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); - emit(node.closingFragment); + function emitPrologueDirectivesIfNeeded(sourceFileOrBundle: Bundle | SourceFile) { + if (isSourceFile(sourceFileOrBundle)) { + emitPrologueDirectives(sourceFileOrBundle.statements, sourceFileOrBundle); + } + else { + const seenPrologueDirectives = new Set(); + for (const prepend of sourceFileOrBundle.prepends) { + emitUnparsedPrologues((prepend as UnparsedSource).prologues, seenPrologueDirectives); + } + for (const sourceFile of sourceFileOrBundle.sourceFiles) { + emitPrologueDirectives(sourceFile.statements, sourceFile, seenPrologueDirectives, /*recordBundleFileSection*/ true); + } + setSourceFile(undefined); + } } - function emitJsxOpeningElementOrFragment(node: JsxOpeningElement | JsxOpeningFragment) { - writePunctuation("<"); - - if (isJsxOpeningElement(node)) { - const indented = writeLineSeparatorsAndIndentBefore(node.tagName, node); - emitJsxTagName(node.tagName); - emitTypeArguments(node, node.typeArguments); - if (node.attributes.properties && node.attributes.properties.length > 0) { - writeSpace(); + function getPrologueDirectivesFromBundledSourceFiles(bundle: Bundle): SourceFilePrologueInfo[] | undefined { + const seenPrologueDirectives = new Set(); + let prologues: SourceFilePrologueInfo[] | undefined; + for (let index = 0; index < bundle.sourceFiles.length; index++) { + const sourceFile = bundle.sourceFiles[index]; + let directives: SourceFilePrologueDirective[] | undefined; + let end = 0; + for (const statement of sourceFile.statements) { + if (!isPrologueDirective(statement)) break; + if (seenPrologueDirectives.has(statement.expression.text)) continue; + seenPrologueDirectives.add(statement.expression.text); + (directives || (directives = [])).push({ + pos: statement.pos, + end: statement.end, + expression: { + pos: statement.expression.pos, + end: statement.expression.end, + text: statement.expression.text + } + }); + end = end < statement.end ? statement.end : end; } - emit(node.attributes); - writeLineSeparatorsAfter(node.attributes, node); - decreaseIndentIf(indented); + if (directives) (prologues || (prologues = [])).push({ file: index, text: sourceFile.text.substring(0, end), directives }); } - - writePunctuation(">"); + return prologues; } - function emitJsxText(node: JsxText) { - writer.writeLiteral(node.text); + function emitShebangIfNeeded(sourceFileOrBundle: Bundle | SourceFile | UnparsedSource) { + if (isSourceFile(sourceFileOrBundle) || isUnparsedSource(sourceFileOrBundle)) { + const shebang = getShebang(sourceFileOrBundle.text); + if (shebang) { + writeComment(shebang); + writeLine(); + return true; + } + } + else { + for (const prepend of sourceFileOrBundle.prepends) { + Debug.assertNode(prepend, isUnparsedSource); + if (emitShebangIfNeeded(prepend)) { + return true; + } + } + for (const sourceFile of sourceFileOrBundle.sourceFiles) { + // Emit only the first encountered shebang + if (emitShebangIfNeeded(sourceFile)) { + return true; + } + } + } } - function emitJsxClosingElementOrFragment(node: JsxClosingElement | JsxClosingFragment) { - writePunctuation(""); + // + // Helpers + // + + function emitNodeWithWriter(node: Node | undefined, writer: typeof write) { + if (!node) return; + const savedWrite = write; + write = writer; + emit(node); + write = savedWrite; } - function emitJsxAttributes(node: JsxAttributes) { - emitList(node, node.properties, ListFormat.JsxElementAttributes); + function emitModifiers(node: Node, modifiers: NodeArray | undefined) { + if (modifiers && modifiers.length) { + emitList(node, modifiers, ListFormat.Modifiers); + writeSpace(); + } } - function emitJsxAttribute(node: JsxAttribute) { - emit(node.name); - emitNodeWithPrefix("=", writePunctuation, node.initializer, emitJsxAttributeValue); + function emitTypeAnnotation(node: TypeNode | undefined) { + if (node) { + writePunctuation(":"); + writeSpace(); + emit(node); + } } - function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { - writePunctuation("{..."); - emitExpression(node.expression); - writePunctuation("}"); + function emitInitializer(node: Expression | undefined, equalCommentStartPos: number, container: Node) { + if (node) { + writeSpace(); + emitTokenWithComment(SyntaxKind.EqualsToken, equalCommentStartPos, writeOperator, container); + writeSpace(); + emit(node); + } } - function hasTrailingCommentsAtPosition(pos: number) { - let result = false; - forEachTrailingCommentRange(currentSourceFile?.text || "", pos + 1, () => result = true); - return result; + function emitNodeWithPrefix(prefix: string, prefixWriter: (s: string) => void, node: T | undefined, emit: (node: T) => void) { + if (node) { + prefixWriter(prefix); + emit(node); + } } - function hasLeadingCommentsAtPosition(pos: number) { - let result = false; - forEachLeadingCommentRange(currentSourceFile?.text || "", pos + 1, () => result = true); - return result; + function emitWithLeadingSpace(node: Node | undefined) { + if (node) { + writeSpace(); + emit(node); + } } - function hasCommentsAtPosition(pos: number) { - return hasTrailingCommentsAtPosition(pos) || hasLeadingCommentsAtPosition(pos); + function emitExpressionWithLeadingSpace(node: Expression | undefined) { + if (node) { + writeSpace(); + emit(node); + } } - function emitJsxExpression(node: JsxExpression) { - if (node.expression || (!commentsDisabled && !nodeIsSynthesized(node) && hasCommentsAtPosition(node.pos))) { // preserve empty expressions if they contain comments! - const isMultiline = currentSourceFile && !nodeIsSynthesized(node) && getLineAndCharacterOfPosition(currentSourceFile, node.pos).line !== getLineAndCharacterOfPosition(currentSourceFile, node.end).line; - if (isMultiline) { - writer.increaseIndent(); - } - const end = emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node); - emit(node.dotDotDotToken); - emitExpression(node.expression); - emitTokenWithComment(SyntaxKind.CloseBraceToken, node.expression?.end || end, writePunctuation, node); - if (isMultiline) { - writer.decreaseIndent(); - } + function emitWithTrailingSpace(node: Node | undefined) { + if (node) { + emit(node); + writeSpace(); } } - function emitJsxTagName(node: JsxTagNameExpression) { - if (node.kind === SyntaxKind.Identifier) { - emitExpression(node); + function emitEmbeddedStatement(parent: Node, node: Statement) { + if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) { + writeSpace(); + emit(node); } else { - emit(node); + writeLine(); + increaseIndent(); + if (isEmptyStatement(node)) { + emitWithContext(node, emitEmbeddedEmptyStatement); + } + else { + emit(node); + } + decreaseIndent(); } } - // - // Clauses - // + function emitDecorators(parentNode: Node, decorators: NodeArray | undefined) { + emitList(parentNode, decorators, ListFormat.Decorators); + } - function emitCaseClause(node: CaseClause) { - emitTokenWithComment(SyntaxKind.CaseKeyword, node.pos, writeKeyword, node); - writeSpace(); - emitExpression(node.expression); + function emitTypeArguments(parentNode: Node, typeArguments: NodeArray | undefined) { + emitList(parentNode, typeArguments, ListFormat.TypeArguments); + } - emitCaseOrDefaultClauseRest(node, node.statements, node.expression.end); + function emitTypeParameters(parentNode: SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration | ClassExpression, typeParameters: NodeArray | undefined) { + if (isFunctionLike(parentNode) && parentNode.typeArguments) { // Quick info uses type arguments in place of type parameters on instantiated signatures + return emitTypeArguments(parentNode, parentNode.typeArguments); + } + emitList(parentNode, typeParameters, ListFormat.TypeParameters); } - function emitDefaultClause(node: DefaultClause) { - const pos = emitTokenWithComment(SyntaxKind.DefaultKeyword, node.pos, writeKeyword, node); - emitCaseOrDefaultClauseRest(node, node.statements, pos); + function emitParameters(parentNode: Node, parameters: NodeArray) { + emitList(parentNode, parameters, ListFormat.Parameters); } - function emitCaseOrDefaultClauseRest(parentNode: Node, statements: NodeArray, colonPos: number) { - const emitAsSingleStatement = - statements.length === 1 && - ( - // treat synthesized nodes as located on the same line for emit purposes - nodeIsSynthesized(parentNode) || - nodeIsSynthesized(statements[0]) || - rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile!) - ); + function canEmitSimpleArrowHead(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) { + const parameter = singleOrUndefined(parameters); + return parameter + && parameter.pos === parentNode.pos // may not have parsed tokens between parent and parameter + && isArrowFunction(parentNode) // only arrow functions may have simple arrow head + && !parentNode.type // arrow function may not have return type annotation + && !some(parentNode.decorators) // parent may not have decorators + && !some(parentNode.modifiers) // parent may not have modifiers + && !some(parentNode.typeParameters) // parent may not have type parameters + && !some(parameter.decorators) // parameter may not have decorators + && !some(parameter.modifiers) // parameter may not have modifiers + && !parameter.dotDotDotToken // parameter may not be rest + && !parameter.questionToken // parameter may not be optional + && !parameter.type // parameter may not have a type annotation + && !parameter.initializer // parameter may not have an initializer + && isIdentifier(parameter.name); // parameter name must be identifier + } - let format = ListFormat.CaseOrDefaultClauseStatements; - if (emitAsSingleStatement) { - writeToken(SyntaxKind.ColonToken, colonPos, writePunctuation, parentNode); - writeSpace(); - format &= ~(ListFormat.MultiLine | ListFormat.Indented); + function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) { + if (canEmitSimpleArrowHead(parentNode, parameters)) { + emitList(parentNode, parameters, ListFormat.Parameters & ~ListFormat.Parenthesis); } else { - emitTokenWithComment(SyntaxKind.ColonToken, colonPos, writePunctuation, parentNode); + emitParameters(parentNode, parameters); } - emitList(parentNode, statements, format); } - function emitHeritageClause(node: HeritageClause) { - writeSpace(); - writeTokenText(node.token, writeKeyword); - writeSpace(); - emitList(node, node.types, ListFormat.HeritageClauseTypes); + function emitParametersForIndexSignature(parentNode: Node, parameters: NodeArray) { + emitList(parentNode, parameters, ListFormat.IndexSignatureParameters); } - function emitCatchClause(node: CatchClause) { - const openParenPos = emitTokenWithComment(SyntaxKind.CatchKeyword, node.pos, writeKeyword, node); - writeSpace(); - if (node.variableDeclaration) { - emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emit(node.variableDeclaration); - emitTokenWithComment(SyntaxKind.CloseParenToken, node.variableDeclaration.end, writePunctuation, node); - writeSpace(); + function writeDelimiter(format: ListFormat) { + switch (format & ListFormat.DelimitersMask) { + case ListFormat.None: + break; + case ListFormat.CommaDelimited: + writePunctuation(","); + break; + case ListFormat.BarDelimited: + writeSpace(); + writePunctuation("|"); + break; + case ListFormat.AsteriskDelimited: + writeSpace(); + writePunctuation("*"); + writeSpace(); + break; + case ListFormat.AmpersandDelimited: + writeSpace(); + writePunctuation("&"); + break; } - emit(node.block); } - // - // Property assignments - // + function emitList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, start = 0, count = children ? children.length - start : 0) { + const isUndefined = children === undefined; + if (isUndefined && format & ListFormat.OptionalIfUndefined) { + return; + } - function emitPropertyAssignment(node: PropertyAssignment) { - emit(node.name); - writePunctuation(":"); - writeSpace(); - // This is to ensure that we emit comment in the following case: - // For example: - // obj = { - // id: /*comment1*/ ()=>void - // } - // "comment1" is not considered to be leading comment for node.initializer - // but rather a trailing comment on the previous node. - const initializer = node.initializer; - if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { - const commentRange = getCommentRange(initializer); - emitTrailingCommentsOfPosition(commentRange.pos); + const isEmpty = children === undefined || start >= children.length || count === 0; + if (isEmpty && format & ListFormat.OptionalIfEmpty) { + if (onBeforeEmitNodeArray) { + onBeforeEmitNodeArray(children); + } + if (onAfterEmitNodeArray) { + onAfterEmitNodeArray(children); + } + return; } - emitExpression(initializer); - } - function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { - emit(node.name); - if (node.objectAssignmentInitializer) { - writeSpace(); - writePunctuation("="); - writeSpace(); - emitExpression(node.objectAssignmentInitializer); + if (format & ListFormat.BracketsMask) { + writePunctuation(getOpeningBracket(format)); + if (isEmpty && children) { + emitTrailingCommentsOfPosition(children.pos, /*prefixSpace*/ true); // Emit comments within empty bracketed lists + } } - } - function emitSpreadAssignment(node: SpreadAssignment) { - if (node.expression) { - emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node); - emitExpression(node.expression); + if (onBeforeEmitNodeArray) { + onBeforeEmitNodeArray(children); } - } - // - // Enum - // + if (isEmpty) { + // Write a line terminator if the parent node was multi-line + if (format & ListFormat.MultiLine && !(preserveSourceNewlines && (!parentNode || rangeIsOnSingleLine(parentNode, currentSourceFile!)))) { + writeLine(); + } + else if (format & ListFormat.SpaceBetweenBraces && !(format & ListFormat.NoSpaceIfEmpty)) { + writeSpace(); + } + } + else { + Debug.type>(children); + // Write the opening line terminator or leading whitespace. + const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; + let shouldEmitInterveningComments = mayEmitInterveningComments; + const leadingLineTerminatorCount = getLeadingLineTerminatorCount(parentNode, children, format); // TODO: GH#18217 + if (leadingLineTerminatorCount) { + writeLine(leadingLineTerminatorCount); + shouldEmitInterveningComments = false; + } + else if (format & ListFormat.SpaceBetweenBraces) { + writeSpace(); + } + + // Increase the indent, if requested. + if (format & ListFormat.Indented) { + increaseIndent(); + } + + // Emit each child. + let previousSibling: Node | undefined; + let previousSourceFileTextKind: ReturnType; + let shouldDecreaseIndentAfterEmit = false; + for (let i = 0; i < count; i++) { + const child = children[start + i]; + + // Write the delimiter if this is not the first node. + if (format & ListFormat.AsteriskDelimited) { + // always write JSDoc in the format "\n *" + writeLine(); + writeDelimiter(format); + } + else if (previousSibling) { + // i.e + // function commentedParameters( + // /* Parameter a */ + // a + // /* End of parameter a */ -> this comment isn't considered to be trailing comment of parameter "a" due to newline + // , + if (format & ListFormat.DelimitersMask && previousSibling.end !== (parentNode ? parentNode.end : -1)) { + emitLeadingCommentsOfPosition(previousSibling.end); + } + writeDelimiter(format); + recordBundleFileInternalSectionEnd(previousSourceFileTextKind); + + // Write either a line terminator or whitespace to separate the elements. + const separatingLineTerminatorCount = getSeparatingLineTerminatorCount(previousSibling, child, format); + if (separatingLineTerminatorCount > 0) { + // If a synthesized node in a single-line list starts on a new + // line, we should increase the indent. + if ((format & (ListFormat.LinesMask | ListFormat.Indented)) === ListFormat.SingleLine) { + increaseIndent(); + shouldDecreaseIndentAfterEmit = true; + } + + writeLine(separatingLineTerminatorCount); + shouldEmitInterveningComments = false; + } + else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { + writeSpace(); + } + } + + // Emit this child. + previousSourceFileTextKind = recordBundleFileInternalSectionStart(child); + if (shouldEmitInterveningComments) { + if (emitTrailingCommentsOfPosition) { + const commentRange = getCommentRange(child); + emitTrailingCommentsOfPosition(commentRange.pos); + } + } + else { + shouldEmitInterveningComments = mayEmitInterveningComments; + } + + nextListElementPos = child.pos; + emit(child); + + if (shouldDecreaseIndentAfterEmit) { + decreaseIndent(); + shouldDecreaseIndentAfterEmit = false; + } + + previousSibling = child; + } + + // Write a trailing comma, if requested. + const emitFlags = previousSibling ? getEmitFlags(previousSibling) : 0; + const skipTrailingComments = commentsDisabled || !!(emitFlags & EmitFlags.NoTrailingComments); + const hasTrailingComma = children?.hasTrailingComma && (format & ListFormat.AllowTrailingComma) && (format & ListFormat.CommaDelimited); + if (hasTrailingComma) { + if (previousSibling && !skipTrailingComments) { + emitTokenWithComment(SyntaxKind.CommaToken, previousSibling.end, writePunctuation, previousSibling); + } + else { + writePunctuation(","); + } + } + + // Emit any trailing comment of the last element in the list + // i.e + // var array = [... + // 2 + // /* end of element 2 */ + // ]; + if (previousSibling && (parentNode ? parentNode.end : -1) !== previousSibling.end && (format & ListFormat.DelimitersMask) && !skipTrailingComments) { + emitLeadingCommentsOfPosition(hasTrailingComma && children?.end ? children.end : previousSibling.end); + } - function emitEnumMember(node: EnumMember) { - emit(node.name); - emitInitializer(node.initializer, node.name.end, node); - } + // Decrease the indent, if requested. + if (format & ListFormat.Indented) { + decreaseIndent(); + } - // - // JSDoc - // - function emitJSDoc(node: JSDoc) { - write("/**"); - if (node.comment) { - const lines = node.comment.split(/\r\n?|\n/g); - for (const line of lines) { - writeLine(); - writeSpace(); - writePunctuation("*"); - writeSpace(); - write(line); + recordBundleFileInternalSectionEnd(previousSourceFileTextKind); + + // Write the closing line terminator or closing whitespace. + const closingLineTerminatorCount = getClosingLineTerminatorCount(parentNode, children, format); + if (closingLineTerminatorCount) { + writeLine(closingLineTerminatorCount); } - } - if (node.tags) { - if (node.tags.length === 1 && node.tags[0].kind === SyntaxKind.JSDocTypeTag && !node.comment) { + else if (format & (ListFormat.SpaceAfterList | ListFormat.SpaceBetweenBraces)) { writeSpace(); - emit(node.tags[0]); } - else { - emitList(node, node.tags, ListFormat.JSDocComment); + } + + if (onAfterEmitNodeArray) { + onAfterEmitNodeArray(children); + } + + if (format & ListFormat.BracketsMask) { + if (isEmpty && children) { + emitLeadingCommentsOfPosition(children.end); // Emit leading comments within empty lists } + writePunctuation(getClosingBracket(format)); } - writeSpace(); - write("*/"); } - function emitJSDocSimpleTypedTag(tag: JSDocTypeTag | JSDocThisTag | JSDocEnumTag | JSDocReturnTag) { - emitJSDocTagName(tag.tagName); - emitJSDocTypeExpression(tag.typeExpression); - emitJSDocComment(tag.comment); + // Writers + + function writeLiteral(s: string) { + writer.writeLiteral(s); } - function emitJSDocSeeTag(tag: JSDocSeeTag) { - emitJSDocTagName(tag.tagName); - emit(tag.name); - emitJSDocComment(tag.comment); + function writeStringLiteral(s: string) { + writer.writeStringLiteral(s); } - function emitJSDocNameReference(node: JSDocNameReference) { - writeSpace(); - writePunctuation("{"); - emit(node.name); - writePunctuation("}"); + function writeBase(s: string) { + writer.write(s); } - function emitJSDocHeritageTag(tag: JSDocImplementsTag | JSDocAugmentsTag) { - emitJSDocTagName(tag.tagName); - writeSpace(); - writePunctuation("{"); - emit(tag.class); - writePunctuation("}"); - emitJSDocComment(tag.comment); + function writeSymbol(s: string, sym: Symbol) { + writer.writeSymbol(s, sym); } - function emitJSDocTemplateTag(tag: JSDocTemplateTag) { - emitJSDocTagName(tag.tagName); - emitJSDocTypeExpression(tag.constraint); - writeSpace(); - emitList(tag, tag.typeParameters, ListFormat.CommaListElements); - emitJSDocComment(tag.comment); + function writePunctuation(s: string) { + writer.writePunctuation(s); } - function emitJSDocTypedefTag(tag: JSDocTypedefTag) { - emitJSDocTagName(tag.tagName); - if (tag.typeExpression) { - if (tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) { - emitJSDocTypeExpression(tag.typeExpression); - } - else { - writeSpace(); - writePunctuation("{"); - write("Object"); - if (tag.typeExpression.isArrayType) { - writePunctuation("["); - writePunctuation("]"); - } - writePunctuation("}"); - } - } - if (tag.fullName) { - writeSpace(); - emit(tag.fullName); - } - emitJSDocComment(tag.comment); - if (tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeLiteral) { - emitJSDocTypeLiteral(tag.typeExpression); - } + function writeTrailingSemicolon() { + writer.writeTrailingSemicolon(";"); } - function emitJSDocCallbackTag(tag: JSDocCallbackTag) { - emitJSDocTagName(tag.tagName); - if (tag.name) { - writeSpace(); - emit(tag.name); - } - emitJSDocComment(tag.comment); - emitJSDocSignature(tag.typeExpression); + function writeKeyword(s: string) { + writer.writeKeyword(s); } - function emitJSDocSimpleTag(tag: JSDocTag) { - emitJSDocTagName(tag.tagName); - emitJSDocComment(tag.comment); + function writeOperator(s: string) { + writer.writeOperator(s); } - function emitJSDocTypeLiteral(lit: JSDocTypeLiteral) { - emitList(lit, factory.createNodeArray(lit.jsDocPropertyTags), ListFormat.JSDocComment); + function writeParameter(s: string) { + writer.writeParameter(s); } - function emitJSDocSignature(sig: JSDocSignature) { - if (sig.typeParameters) { - emitList(sig, factory.createNodeArray(sig.typeParameters), ListFormat.JSDocComment); - } - if (sig.parameters) { - emitList(sig, factory.createNodeArray(sig.parameters), ListFormat.JSDocComment); - } - if (sig.type) { - writeLine(); - writeSpace(); - writePunctuation("*"); - writeSpace(); - emit(sig.type); - } + function writeComment(s: string) { + writer.writeComment(s); } - function emitJSDocPropertyLikeTag(param: JSDocPropertyLikeTag) { - emitJSDocTagName(param.tagName); - emitJSDocTypeExpression(param.typeExpression); - writeSpace(); - if (param.isBracketed) { - writePunctuation("["); - } - emit(param.name); - if (param.isBracketed) { - writePunctuation("]"); - } - emitJSDocComment(param.comment); + function writeSpace() { + writer.writeSpace(" "); } - function emitJSDocTagName(tagName: Identifier) { - writePunctuation("@"); - emit(tagName); + function writeProperty(s: string) { + writer.writeProperty(s); } - function emitJSDocComment(comment: string | undefined) { - if (comment) { - writeSpace(); - write(comment); + function writeLine(count = 1) { + for (let i = 0; i < count; i++) { + writer.writeLine(i > 0); } } - function emitJSDocTypeExpression(typeExpression: JSDocTypeExpression | undefined) { - if (typeExpression) { - writeSpace(); - writePunctuation("{"); - emit(typeExpression.type); - writePunctuation("}"); - } + function increaseIndent() { + writer.increaseIndent(); } - // - // Top-level nodes - // + function decreaseIndent() { + writer.decreaseIndent(); + } - function emitSourceFile(node: SourceFile) { - writeLine(); - const statements = node.statements; - if (emitBodyWithDetachedComments) { - // Emit detached comment if there are no prologue directives or if the first node is synthesized. - // The synthesized node will have no leading comment so some comments may be missed. - const shouldEmitDetachedComment = statements.length === 0 || - !isPrologueDirective(statements[0]) || - nodeIsSynthesized(statements[0]); - if (shouldEmitDetachedComment) { - emitBodyWithDetachedComments(node, statements, emitSourceFileWorker); - return; - } - } - emitSourceFileWorker(node); + function writeToken(token: SyntaxKind, pos: number, writer: (s: string) => void, contextNode?: Node) { + return !sourceMapsDisabled + ? emitTokenWithSourceMap(contextNode, token, writer, pos, writeTokenText) + : writeTokenText(token, writer, pos); } - function emitSyntheticTripleSlashReferencesIfNeeded(node: Bundle) { - emitTripleSlashDirectives(!!node.hasNoDefaultLib, node.syntheticFileReferences || [], node.syntheticTypeReferences || [], node.syntheticLibReferences || []); - for (const prepend of node.prepends) { - if (isUnparsedSource(prepend) && prepend.syntheticReferences) { - for (const ref of prepend.syntheticReferences) { - emit(ref); - writeLine(); - } - } + function writeTokenNode(node: Node, writer: (s: string) => void) { + if (onBeforeEmitToken) { + onBeforeEmitToken(node); + } + writer(tokenToString(node.kind)!); + if (onAfterEmitToken) { + onAfterEmitToken(node); } } - function emitTripleSlashDirectivesIfNeeded(node: SourceFile) { - if (node.isDeclarationFile) emitTripleSlashDirectives(node.hasNoDefaultLib, node.referencedFiles, node.typeReferenceDirectives, node.libReferenceDirectives); + function writeTokenText(token: SyntaxKind, writer: (s: string) => void): void; + function writeTokenText(token: SyntaxKind, writer: (s: string) => void, pos: number): number; + function writeTokenText(token: SyntaxKind, writer: (s: string) => void, pos?: number): number { + const tokenString = tokenToString(token)!; + writer(tokenString); + return pos! < 0 ? pos! : pos! + tokenString.length; } - function emitTripleSlashDirectives(hasNoDefaultLib: boolean, files: readonly FileReference[], types: readonly FileReference[], libs: readonly FileReference[]) { - if (hasNoDefaultLib) { - const pos = writer.getTextPos(); - writeComment(`/// `); - if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.NoDefaultLib }); - writeLine(); + function writeLineOrSpace(parentNode: Node, prevChildNode: Node, nextChildNode: Node) { + if (getEmitFlags(parentNode) & EmitFlags.SingleLine) { + writeSpace(); + } + else if (preserveSourceNewlines) { + const lines = getLinesBetweenNodes(parentNode, prevChildNode, nextChildNode); + if (lines) { + writeLine(lines); + } + else { + writeSpace(); + } } - if (currentSourceFile && currentSourceFile.moduleName) { - writeComment(`/// `); + else { writeLine(); } - if (currentSourceFile && currentSourceFile.amdDependencies) { - for (const dep of currentSourceFile.amdDependencies) { - if (dep.name) { - writeComment(`/// `); - } - else { - writeComment(`/// `); - } + } + + function writeLines(text: string): void { + const lines = text.split(/\r\n?|\n/g); + const indentation = guessIndentation(lines); + for (const lineText of lines) { + const line = indentation ? lineText.slice(indentation) : lineText; + if (line.length) { writeLine(); + write(line); } } - for (const directive of files) { - const pos = writer.getTextPos(); - writeComment(`/// `); - if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Reference, data: directive.fileName }); - writeLine(); - } - for (const directive of types) { - const pos = writer.getTextPos(); - writeComment(`/// `); - if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Type, data: directive.fileName }); - writeLine(); - } - for (const directive of libs) { - const pos = writer.getTextPos(); - writeComment(`/// `); - if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Lib, data: directive.fileName }); - writeLine(); - } } - function emitSourceFileWorker(node: SourceFile) { - const statements = node.statements; - pushNameGenerationScope(node); - forEach(node.statements, generateNames); - emitHelpers(node); - const index = findIndex(statements, statement => !isPrologueDirective(statement)); - emitTripleSlashDirectivesIfNeeded(node); - emitList(node, statements, ListFormat.MultiLine, index === -1 ? statements.length : index); - popNameGenerationScope(node); + function writeLinesAndIndent(lineCount: number, writeSpaceIfNotIndenting: boolean) { + if (lineCount) { + increaseIndent(); + writeLine(lineCount); + } + else if (writeSpaceIfNotIndenting) { + writeSpace(); + } } - // Transformation nodes - - function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { - emitExpression(node.expression); + // Helper function to decrease the indent if we previously indented. Allows multiple + // previous indent values to be considered at a time. This also allows caller to just + // call this once, passing in all their appropriate indent values, instead of needing + // to call this helper function multiple times. + function decreaseIndentIf(value1: boolean | number | undefined, value2?: boolean | number) { + if (value1) { + decreaseIndent(); + } + if (value2) { + decreaseIndent(); + } } - function emitCommaList(node: CommaListExpression) { - emitExpressionList(node, node.elements, ListFormat.CommaListElements); - } + function getLeadingLineTerminatorCount(parentNode: Node | undefined, children: readonly Node[], format: ListFormat): number { + if (format & ListFormat.PreserveLines || preserveSourceNewlines) { + if (format & ListFormat.PreferNewLine) { + return 1; + } - /** - * Emits any prologue directives at the start of a Statement list, returning the - * number of prologue directives written to the output. - */ - function emitPrologueDirectives(statements: readonly Node[], sourceFile?: SourceFile, seenPrologueDirectives?: Set, recordBundleFileSection?: true): number { - let needsToSetSourceFile = !!sourceFile; - for (let i = 0; i < statements.length; i++) { - const statement = statements[i]; - if (isPrologueDirective(statement)) { - const shouldEmitPrologueDirective = seenPrologueDirectives ? !seenPrologueDirectives.has(statement.expression.text) : true; - if (shouldEmitPrologueDirective) { - if (needsToSetSourceFile) { - needsToSetSourceFile = false; - setSourceFile(sourceFile); - } - writeLine(); - const pos = writer.getTextPos(); - emit(statement); - if (recordBundleFileSection && bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prologue, data: statement.expression.text }); - if (seenPrologueDirectives) { - seenPrologueDirectives.add(statement.expression.text); - } + const firstChild = children[0]; + if (firstChild === undefined) { + return !parentNode || rangeIsOnSingleLine(parentNode, currentSourceFile!) ? 0 : 1; + } + if (firstChild.pos === nextListElementPos) { + // If this child starts at the beginning of a list item in a parent list, its leading + // line terminators have already been written as the separating line terminators of the + // parent list. Example: + // + // class Foo { + // constructor() {} + // public foo() {} + // } + // + // The outer list is the list of class members, with one line terminator between the + // constructor and the method. The constructor is written, the separating line terminator + // is written, and then we start emitting the method. Its modifiers ([public]) constitute an inner + // list, so we look for its leading line terminators. If we didn't know that we had already + // written a newline as part of the parent list, it would appear that we need to write a + // leading newline to start the modifiers. + return 0; + } + if (firstChild.kind === SyntaxKind.JsxText) { + // JsxText will be written with its leading whitespace, so don't add more manually. + return 0; + } + if (parentNode && + !positionIsSynthesized(parentNode.pos) && + !nodeIsSynthesized(firstChild) && + (!firstChild.parent || getOriginalNode(firstChild.parent) === getOriginalNode(parentNode)) + ) { + if (preserveSourceNewlines) { + return getEffectiveLines( + includeComments => getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter( + firstChild.pos, + parentNode.pos, + currentSourceFile!, + includeComments)); } + return rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile!) ? 0 : 1; } - else { - // return index of the first non prologue directive - return i; + if (synthesizedNodeStartsOnNewLine(firstChild, format)) { + return 1; } } - - return statements.length; + return format & ListFormat.MultiLine ? 1 : 0; } - function emitUnparsedPrologues(prologues: readonly UnparsedPrologue[], seenPrologueDirectives: Set) { - for (const prologue of prologues) { - if (!seenPrologueDirectives.has(prologue.data)) { - writeLine(); - const pos = writer.getTextPos(); - emit(prologue); - if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prologue, data: prologue.data }); - if (seenPrologueDirectives) { - seenPrologueDirectives.add(prologue.data); + function getSeparatingLineTerminatorCount(previousNode: Node | undefined, nextNode: Node, format: ListFormat): number { + if (format & ListFormat.PreserveLines || preserveSourceNewlines) { + if (previousNode === undefined || nextNode === undefined) { + return 0; + } + if (nextNode.kind === SyntaxKind.JsxText) { + // JsxText will be written with its leading whitespace, so don't add more manually. + return 0; + } + else if (!nodeIsSynthesized(previousNode) && !nodeIsSynthesized(nextNode) && (!previousNode.parent || !nextNode.parent || previousNode.parent === nextNode.parent)) { + if (preserveSourceNewlines && previousNode.parent && nextNode.parent) { + return getEffectiveLines( + includeComments => getLinesBetweenRangeEndAndRangeStart( + previousNode, + nextNode, + currentSourceFile!, + includeComments)); } + return rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile!) ? 0 : 1; + } + else if (synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format)) { + return 1; } } + else if (getStartsOnNewLine(nextNode)) { + return 1; + } + return format & ListFormat.MultiLine ? 1 : 0; } - function emitPrologueDirectivesIfNeeded(sourceFileOrBundle: Bundle | SourceFile) { - if (isSourceFile(sourceFileOrBundle)) { - emitPrologueDirectives(sourceFileOrBundle.statements, sourceFileOrBundle); - } - else { - const seenPrologueDirectives = new Set(); - for (const prepend of sourceFileOrBundle.prepends) { - emitUnparsedPrologues((prepend as UnparsedSource).prologues, seenPrologueDirectives); + function getClosingLineTerminatorCount(parentNode: Node | undefined, children: readonly Node[], format: ListFormat): number { + if (format & ListFormat.PreserveLines || preserveSourceNewlines) { + if (format & ListFormat.PreferNewLine) { + return 1; } - for (const sourceFile of sourceFileOrBundle.sourceFiles) { - emitPrologueDirectives(sourceFile.statements, sourceFile, seenPrologueDirectives, /*recordBundleFileSection*/ true); + + const lastChild = lastOrUndefined(children); + if (lastChild === undefined) { + return !parentNode || rangeIsOnSingleLine(parentNode, currentSourceFile!) ? 0 : 1; + } + if (parentNode && !positionIsSynthesized(parentNode.pos) && !nodeIsSynthesized(lastChild) && (!lastChild.parent || lastChild.parent === parentNode)) { + if (preserveSourceNewlines) { + const end = isNodeArray(children) && !positionIsSynthesized(children.end) ? children.end : lastChild.end; + return getEffectiveLines( + includeComments => getLinesBetweenPositionAndNextNonWhitespaceCharacter( + end, + parentNode.end, + currentSourceFile!, + includeComments)); + } + return rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile!) ? 0 : 1; + } + if (synthesizedNodeStartsOnNewLine(lastChild, format)) { + return 1; } - setSourceFile(undefined); } + if (format & ListFormat.MultiLine && !(format & ListFormat.NoTrailingNewLine)) { + return 1; + } + return 0; } - function getPrologueDirectivesFromBundledSourceFiles(bundle: Bundle): SourceFilePrologueInfo[] | undefined { - const seenPrologueDirectives = new Set(); - let prologues: SourceFilePrologueInfo[] | undefined; - for (let index = 0; index < bundle.sourceFiles.length; index++) { - const sourceFile = bundle.sourceFiles[index]; - let directives: SourceFilePrologueDirective[] | undefined; - let end = 0; - for (const statement of sourceFile.statements) { - if (!isPrologueDirective(statement)) break; - if (seenPrologueDirectives.has(statement.expression.text)) continue; - seenPrologueDirectives.add(statement.expression.text); - (directives || (directives = [])).push({ - pos: statement.pos, - end: statement.end, - expression: { - pos: statement.expression.pos, - end: statement.expression.end, - text: statement.expression.text - } - }); - end = end < statement.end ? statement.end : end; - } - if (directives) (prologues || (prologues = [])).push({ file: index, text: sourceFile.text.substring(0, end), directives }); + function getEffectiveLines(getLineDifference: (includeComments: boolean) => number) { + // If 'preserveSourceNewlines' is disabled, we should never call this function + // because it could be more expensive than alternative approximations. + Debug.assert(!!preserveSourceNewlines); + // We start by measuring the line difference from a position to its adjacent comments, + // so that this is counted as a one-line difference, not two: + // + // node1; + // // NODE2 COMMENT + // node2; + const lines = getLineDifference(/*includeComments*/ true); + if (lines === 0) { + // However, if the line difference considering comments was 0, we might have this: + // + // node1; // NODE2 COMMENT + // node2; + // + // in which case we should be ignoring node2's comment, so this too is counted as + // a one-line difference, not zero. + return getLineDifference(/*includeComments*/ false); + } + return lines; + } + + function writeLineSeparatorsAndIndentBefore(node: Node, parent: Node): boolean { + const leadingNewlines = preserveSourceNewlines && getLeadingLineTerminatorCount(parent, [node], ListFormat.None); + if (leadingNewlines) { + writeLinesAndIndent(leadingNewlines, /*writeLinesIfNotIndenting*/ false); } - return prologues; + return !!leadingNewlines; } - function emitShebangIfNeeded(sourceFileOrBundle: Bundle | SourceFile | UnparsedSource) { - if (isSourceFile(sourceFileOrBundle) || isUnparsedSource(sourceFileOrBundle)) { - const shebang = getShebang(sourceFileOrBundle.text); - if (shebang) { - writeComment(shebang); - writeLine(); - return true; - } + function writeLineSeparatorsAfter(node: Node, parent: Node) { + const trailingNewlines = preserveSourceNewlines && getClosingLineTerminatorCount(parent, [node], ListFormat.None); + if (trailingNewlines) { + writeLine(trailingNewlines); } - else { - for (const prepend of sourceFileOrBundle.prepends) { - Debug.assertNode(prepend, isUnparsedSource); - if (emitShebangIfNeeded(prepend)) { - return true; - } - } - for (const sourceFile of sourceFileOrBundle.sourceFiles) { - // Emit only the first encountered shebang - if (emitShebangIfNeeded(sourceFile)) { - return true; - } + } + + function synthesizedNodeStartsOnNewLine(node: Node, format: ListFormat) { + if (nodeIsSynthesized(node)) { + const startsOnNewLine = getStartsOnNewLine(node); + if (startsOnNewLine === undefined) { + return (format & ListFormat.PreferNewLine) !== 0; } + + return startsOnNewLine; } + + return (format & ListFormat.PreferNewLine) !== 0; } - // - // Helpers - // + function getLinesBetweenNodes(parent: Node, node1: Node, node2: Node): number { + if (getEmitFlags(parent) & EmitFlags.NoIndentation) { + return 0; + } - function emitNodeWithWriter(node: Node | undefined, writer: typeof write) { - if (!node) return; - const savedWrite = write; - write = writer; - emit(node); - write = savedWrite; - } + parent = skipSynthesizedParentheses(parent); + node1 = skipSynthesizedParentheses(node1); + node2 = skipSynthesizedParentheses(node2); - function emitModifiers(node: Node, modifiers: NodeArray | undefined) { - if (modifiers && modifiers.length) { - emitList(node, modifiers, ListFormat.Modifiers); - writeSpace(); + // Always use a newline for synthesized code if the synthesizer desires it. + if (getStartsOnNewLine(node2)) { + return 1; } - } - function emitTypeAnnotation(node: TypeNode | undefined) { - if (node) { - writePunctuation(":"); - writeSpace(); - emit(node); + if (!nodeIsSynthesized(parent) && !nodeIsSynthesized(node1) && !nodeIsSynthesized(node2)) { + if (preserveSourceNewlines) { + return getEffectiveLines( + includeComments => getLinesBetweenRangeEndAndRangeStart( + node1, + node2, + currentSourceFile!, + includeComments)); + } + return rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile!) ? 0 : 1; } + + return 0; } - function emitInitializer(node: Expression | undefined, equalCommentStartPos: number, container: Node) { - if (node) { - writeSpace(); - emitTokenWithComment(SyntaxKind.EqualsToken, equalCommentStartPos, writeOperator, container); - writeSpace(); - emitExpression(node); + function isEmptyBlock(block: BlockLike) { + return block.statements.length === 0 + && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile!); + } + + function skipSynthesizedParentheses(node: Node) { + while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { + node = (node).expression; } + + return node; } - function emitNodeWithPrefix(prefix: string, prefixWriter: (s: string) => void, node: T | undefined, emit: (node: T) => void) { - if (node) { - prefixWriter(prefix); - emit(node); + function getTextOfNode(node: Node, includeTrivia?: boolean): string { + if (isGeneratedIdentifier(node)) { + return generateName(node); + } + else if ((isIdentifier(node) || isPrivateIdentifier(node)) && (nodeIsSynthesized(node) || !node.parent || !currentSourceFile || (node.parent && currentSourceFile && getSourceFileOfNode(node) !== getOriginalNode(currentSourceFile)))) { + return idText(node); + } + else if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { + return getTextOfNode((node).textSourceNode!, includeTrivia); + } + else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { + return node.text; } + + return getSourceTextOfNodeFromSourceFile(currentSourceFile!, node, includeTrivia); } - function emitWithLeadingSpace(node: Node | undefined) { - if (node) { - writeSpace(); - emit(node); + function getLiteralTextOfNode(node: LiteralLikeNode, neverAsciiEscape: boolean | undefined, jsxAttributeEscape: boolean): string { + if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { + const textSourceNode = (node).textSourceNode!; + if (isIdentifier(textSourceNode) || isNumericLiteral(textSourceNode)) { + const text = isNumericLiteral(textSourceNode) ? textSourceNode.text : getTextOfNode(textSourceNode); + return jsxAttributeEscape ? `"${escapeJsxAttributeString(text)}"` : + neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? `"${escapeString(text)}"` : + `"${escapeNonAsciiString(text)}"`; + } + else { + return getLiteralTextOfNode(textSourceNode, neverAsciiEscape, jsxAttributeEscape); + } } + + const flags = (neverAsciiEscape ? GetLiteralTextFlags.NeverAsciiEscape : 0) + | (jsxAttributeEscape ? GetLiteralTextFlags.JsxAttributeEscape : 0) + | (printerOptions.terminateUnterminatedLiterals ? GetLiteralTextFlags.TerminateUnterminatedLiterals : 0) + | (printerOptions.target && printerOptions.target === ScriptTarget.ESNext ? GetLiteralTextFlags.AllowNumericSeparator : 0); + + return getLiteralText(node, currentSourceFile!, flags); } - function emitExpressionWithLeadingSpace(node: Expression | undefined) { - if (node) { - writeSpace(); - emitExpression(node); + /** + * Push a new name generation scope. + */ + function pushNameGenerationScope(node: Node | undefined) { + if (node && getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { + return; } + tempFlagsStack.push(tempFlags); + tempFlags = 0; + reservedNamesStack.push(reservedNames); } - function emitWithTrailingSpace(node: Node | undefined) { - if (node) { - emit(node); - writeSpace(); + /** + * Pop the current name generation scope. + */ + function popNameGenerationScope(node: Node | undefined) { + if (node && getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { + return; } + tempFlags = tempFlagsStack.pop()!; + reservedNames = reservedNamesStack.pop()!; } - function emitEmbeddedStatement(parent: Node, node: Statement) { - if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) { - writeSpace(); - emit(node); + function reserveNameInNestedScopes(name: string) { + if (!reservedNames || reservedNames === lastOrUndefined(reservedNamesStack)) { + reservedNames = new Set(); } - else { - writeLine(); - increaseIndent(); - if (isEmptyStatement(node)) { - pipelineEmit(EmitHint.EmbeddedStatement, node); - } - else { - emit(node); - } - decreaseIndent(); + reservedNames.add(name); + } + + function generateNames(node: Node | undefined) { + if (!node) return; + switch (node.kind) { + case SyntaxKind.Block: + forEach((node).statements, generateNames); + break; + case SyntaxKind.LabeledStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + generateNames((node).statement); + break; + case SyntaxKind.IfStatement: + generateNames((node).thenStatement); + generateNames((node).elseStatement); + break; + case SyntaxKind.ForStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.ForInStatement: + generateNames((node).initializer); + generateNames((node).statement); + break; + case SyntaxKind.SwitchStatement: + generateNames((node).caseBlock); + break; + case SyntaxKind.CaseBlock: + forEach((node).clauses, generateNames); + break; + case SyntaxKind.CaseClause: + case SyntaxKind.DefaultClause: + forEach((node).statements, generateNames); + break; + case SyntaxKind.TryStatement: + generateNames((node).tryBlock); + generateNames((node).catchClause); + generateNames((node).finallyBlock); + break; + case SyntaxKind.CatchClause: + generateNames((node).variableDeclaration); + generateNames((node).block); + break; + case SyntaxKind.VariableStatement: + generateNames((node).declarationList); + break; + case SyntaxKind.VariableDeclarationList: + forEach((node).declarations, generateNames); + break; + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + case SyntaxKind.ClassDeclaration: + generateNameIfNeeded((node).name); + break; + case SyntaxKind.FunctionDeclaration: + generateNameIfNeeded((node).name); + if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { + forEach((node).parameters, generateNames); + generateNames((node).body); + } + break; + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + forEach((node).elements, generateNames); + break; + case SyntaxKind.ImportDeclaration: + generateNames((node).importClause); + break; + case SyntaxKind.ImportClause: + generateNameIfNeeded((node).name); + generateNames((node).namedBindings); + break; + case SyntaxKind.NamespaceImport: + generateNameIfNeeded((node).name); + break; + case SyntaxKind.NamespaceExport: + generateNameIfNeeded((node).name); + break; + case SyntaxKind.NamedImports: + forEach((node).elements, generateNames); + break; + case SyntaxKind.ImportSpecifier: + generateNameIfNeeded((node).propertyName || (node).name); + break; } } - function emitDecorators(parentNode: Node, decorators: NodeArray | undefined) { - emitList(parentNode, decorators, ListFormat.Decorators); - } - - function emitTypeArguments(parentNode: Node, typeArguments: NodeArray | undefined) { - emitList(parentNode, typeArguments, ListFormat.TypeArguments); - } - - function emitTypeParameters(parentNode: SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration | ClassExpression, typeParameters: NodeArray | undefined) { - if (isFunctionLike(parentNode) && parentNode.typeArguments) { // Quick info uses type arguments in place of type parameters on instantiated signatures - return emitTypeArguments(parentNode, parentNode.typeArguments); + function generateMemberNames(node: Node | undefined) { + if (!node) return; + switch (node.kind) { + case SyntaxKind.PropertyAssignment: + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + generateNameIfNeeded((node).name); + break; } - emitList(parentNode, typeParameters, ListFormat.TypeParameters); - } - - function emitParameters(parentNode: Node, parameters: NodeArray) { - emitList(parentNode, parameters, ListFormat.Parameters); } - function canEmitSimpleArrowHead(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) { - const parameter = singleOrUndefined(parameters); - return parameter - && parameter.pos === parentNode.pos // may not have parsed tokens between parent and parameter - && isArrowFunction(parentNode) // only arrow functions may have simple arrow head - && !parentNode.type // arrow function may not have return type annotation - && !some(parentNode.decorators) // parent may not have decorators - && !some(parentNode.modifiers) // parent may not have modifiers - && !some(parentNode.typeParameters) // parent may not have type parameters - && !some(parameter.decorators) // parameter may not have decorators - && !some(parameter.modifiers) // parameter may not have modifiers - && !parameter.dotDotDotToken // parameter may not be rest - && !parameter.questionToken // parameter may not be optional - && !parameter.type // parameter may not have a type annotation - && !parameter.initializer // parameter may not have an initializer - && isIdentifier(parameter.name); // parameter name must be identifier + function generateNameIfNeeded(name: DeclarationName | undefined) { + if (name) { + if (isGeneratedIdentifier(name)) { + generateName(name); + } + else if (isBindingPattern(name)) { + generateNames(name); + } + } } - function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) { - if (canEmitSimpleArrowHead(parentNode, parameters)) { - emitList(parentNode, parameters, ListFormat.Parameters & ~ListFormat.Parenthesis); + /** + * Generate the text for a generated identifier. + */ + function generateName(name: GeneratedIdentifier) { + if ((name.autoGenerateFlags & GeneratedIdentifierFlags.KindMask) === GeneratedIdentifierFlags.Node) { + // Node names generate unique names based on their original node + // and are cached based on that node's id. + return generateNameCached(getNodeForGeneratedName(name), name.autoGenerateFlags); } else { - emitParameters(parentNode, parameters); + // Auto, Loop, and Unique names are cached based on their unique + // autoGenerateId. + const autoGenerateId = name.autoGenerateId!; + return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = makeName(name)); } } - function emitParametersForIndexSignature(parentNode: Node, parameters: NodeArray) { - emitList(parentNode, parameters, ListFormat.IndexSignatureParameters); - } - - function emitList(parentNode: TextRange, children: NodeArray | undefined, format: ListFormat, start?: number, count?: number) { - emitNodeList(emit, parentNode, children, format, start, count); + function generateNameCached(node: Node, flags?: GeneratedIdentifierFlags) { + const nodeId = getNodeId(node); + return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node, flags)); } - function emitExpressionList(parentNode: TextRange, children: NodeArray | undefined, format: ListFormat, start?: number, count?: number) { - emitNodeList(emitExpression as (node: Node) => void, parentNode, children, format, start, count); // TODO: GH#18217 + /** + * Returns a value indicating whether a name is unique globally, within the current file, + * or within the NameGenerator. + */ + function isUniqueName(name: string): boolean { + return isFileLevelUniqueName(name) + && !generatedNames.has(name) + && !(reservedNames && reservedNames.has(name)); } - function writeDelimiter(format: ListFormat) { - switch (format & ListFormat.DelimitersMask) { - case ListFormat.None: - break; - case ListFormat.CommaDelimited: - writePunctuation(","); - break; - case ListFormat.BarDelimited: - writeSpace(); - writePunctuation("|"); - break; - case ListFormat.AsteriskDelimited: - writeSpace(); - writePunctuation("*"); - writeSpace(); - break; - case ListFormat.AmpersandDelimited: - writeSpace(); - writePunctuation("&"); - break; - } + /** + * Returns a value indicating whether a name is unique globally or within the current file. + */ + function isFileLevelUniqueName(name: string) { + return currentSourceFile ? ts.isFileLevelUniqueName(currentSourceFile, name, hasGlobalName) : true; } - function emitNodeList(emit: (node: Node) => void, parentNode: TextRange, children: NodeArray | undefined, format: ListFormat, start = 0, count = children ? children.length - start : 0) { - const isUndefined = children === undefined; - if (isUndefined && format & ListFormat.OptionalIfUndefined) { - return; - } - - const isEmpty = children === undefined || start >= children.length || count === 0; - if (isEmpty && format & ListFormat.OptionalIfEmpty) { - if (onBeforeEmitNodeArray) { - onBeforeEmitNodeArray(children); - } - if (onAfterEmitNodeArray) { - onAfterEmitNodeArray(children); - } - return; - } - - if (format & ListFormat.BracketsMask) { - writePunctuation(getOpeningBracket(format)); - if (isEmpty && !isUndefined) { - // TODO: GH#18217 - emitTrailingCommentsOfPosition(children!.pos, /*prefixSpace*/ true); // Emit comments within empty bracketed lists + /** + * Returns a value indicating whether a name is unique within a container. + */ + function isUniqueLocalName(name: string, container: Node): boolean { + for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer!) { + if (node.locals) { + const local = node.locals.get(escapeLeadingUnderscores(name)); + // We conservatively include alias symbols to cover cases where they're emitted as locals + if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + return false; + } } } + return true; + } - if (onBeforeEmitNodeArray) { - onBeforeEmitNodeArray(children); - } - - if (isEmpty) { - // Write a line terminator if the parent node was multi-line - if (format & ListFormat.MultiLine && !(preserveSourceNewlines && rangeIsOnSingleLine(parentNode, currentSourceFile!))) { - writeLine(); - } - else if (format & ListFormat.SpaceBetweenBraces && !(format & ListFormat.NoSpaceIfEmpty)) { - writeSpace(); + /** + * Return the next available name in the pattern _a ... _z, _0, _1, ... + * TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name. + * Note that names generated by makeTempVariableName and makeUniqueName will never conflict. + */ + function makeTempVariableName(flags: TempFlags, reservedInNestedScopes?: boolean): string { + if (flags && !(tempFlags & flags)) { + const name = flags === TempFlags._i ? "_i" : "_n"; + if (isUniqueName(name)) { + tempFlags |= flags; + if (reservedInNestedScopes) { + reserveNameInNestedScopes(name); + } + return name; } } - else { - // Write the opening line terminator or leading whitespace. - const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; - let shouldEmitInterveningComments = mayEmitInterveningComments; - const leadingLineTerminatorCount = getLeadingLineTerminatorCount(parentNode, children!, format); // TODO: GH#18217 - if (leadingLineTerminatorCount) { - writeLine(leadingLineTerminatorCount); - shouldEmitInterveningComments = false; - } - else if (format & ListFormat.SpaceBetweenBraces) { - writeSpace(); - } - - // Increase the indent, if requested. - if (format & ListFormat.Indented) { - increaseIndent(); - } - - // Emit each child. - let previousSibling: Node | undefined; - let previousSourceFileTextKind: ReturnType; - let shouldDecreaseIndentAfterEmit = false; - for (let i = 0; i < count; i++) { - const child = children![start + i]; - - // Write the delimiter if this is not the first node. - if (format & ListFormat.AsteriskDelimited) { - // always write JSDoc in the format "\n *" - writeLine(); - writeDelimiter(format); - } - else if (previousSibling) { - // i.e - // function commentedParameters( - // /* Parameter a */ - // a - // /* End of parameter a */ -> this comment isn't considered to be trailing comment of parameter "a" due to newline - // , - if (format & ListFormat.DelimitersMask && previousSibling.end !== parentNode.end) { - emitLeadingCommentsOfPosition(previousSibling.end); - } - writeDelimiter(format); - recordBundleFileInternalSectionEnd(previousSourceFileTextKind); - - // Write either a line terminator or whitespace to separate the elements. - const separatingLineTerminatorCount = getSeparatingLineTerminatorCount(previousSibling, child, format); - if (separatingLineTerminatorCount > 0) { - // If a synthesized node in a single-line list starts on a new - // line, we should increase the indent. - if ((format & (ListFormat.LinesMask | ListFormat.Indented)) === ListFormat.SingleLine) { - increaseIndent(); - shouldDecreaseIndentAfterEmit = true; - } - - writeLine(separatingLineTerminatorCount); - shouldEmitInterveningComments = false; - } - else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { - writeSpace(); - } - } - - // Emit this child. - previousSourceFileTextKind = recordBundleFileInternalSectionStart(child); - if (shouldEmitInterveningComments) { - if (emitTrailingCommentsOfPosition) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos); - } - } - else { - shouldEmitInterveningComments = mayEmitInterveningComments; - } - - nextListElementPos = child.pos; - emit(child); - - if (shouldDecreaseIndentAfterEmit) { - decreaseIndent(); - shouldDecreaseIndentAfterEmit = false; + while (true) { + const count = tempFlags & TempFlags.CountMask; + tempFlags++; + // Skip over 'i' and 'n' + if (count !== 8 && count !== 13) { + const name = count < 26 + ? "_" + String.fromCharCode(CharacterCodes.a + count) + : "_" + (count - 26); + if (isUniqueName(name)) { + if (reservedInNestedScopes) { + reserveNameInNestedScopes(name); + } + return name; } - - previousSibling = child; } + } + } - // Write a trailing comma, if requested. - const emitFlags = previousSibling ? getEmitFlags(previousSibling) : 0; - const skipTrailingComments = commentsDisabled || !!(emitFlags & EmitFlags.NoTrailingComments); - const hasTrailingComma = children?.hasTrailingComma && (format & ListFormat.AllowTrailingComma) && (format & ListFormat.CommaDelimited); - if (hasTrailingComma) { - if (previousSibling && !skipTrailingComments) { - emitTokenWithComment(SyntaxKind.CommaToken, previousSibling.end, writePunctuation, previousSibling); + /** + * Generate a name that is unique within the current file and doesn't conflict with any names + * in global scope. The name is formed by adding an '_n' suffix to the specified base name, + * where n is a positive integer. Note that names generated by makeTempVariableName and + * makeUniqueName are guaranteed to never conflict. + * If `optimistic` is set, the first instance will use 'baseName' verbatim instead of 'baseName_1' + */ + function makeUniqueName(baseName: string, checkFn: (name: string) => boolean = isUniqueName, optimistic?: boolean, scoped?: boolean): string { + if (optimistic) { + if (checkFn(baseName)) { + if (scoped) { + reserveNameInNestedScopes(baseName); } else { - writePunctuation(","); + generatedNames.add(baseName); } - } - - // Emit any trailing comment of the last element in the list - // i.e - // var array = [... - // 2 - // /* end of element 2 */ - // ]; - if (previousSibling && parentNode.end !== previousSibling.end && (format & ListFormat.DelimitersMask) && !skipTrailingComments) { - emitLeadingCommentsOfPosition(hasTrailingComma && children?.end ? children.end : previousSibling.end); - } - - // Decrease the indent, if requested. - if (format & ListFormat.Indented) { - decreaseIndent(); - } - - recordBundleFileInternalSectionEnd(previousSourceFileTextKind); - - // Write the closing line terminator or closing whitespace. - const closingLineTerminatorCount = getClosingLineTerminatorCount(parentNode, children!, format); - if (closingLineTerminatorCount) { - writeLine(closingLineTerminatorCount); - } - else if (format & (ListFormat.SpaceAfterList | ListFormat.SpaceBetweenBraces)) { - writeSpace(); + return baseName; } } - - if (onAfterEmitNodeArray) { - onAfterEmitNodeArray(children); + // Find the first unique 'name_n', where n is a positive number + if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { + baseName += "_"; } - - if (format & ListFormat.BracketsMask) { - if (isEmpty && !isUndefined) { - // TODO: GH#18217 - emitLeadingCommentsOfPosition(children!.end); // Emit leading comments within empty lists + let i = 1; + while (true) { + const generatedName = baseName + i; + if (checkFn(generatedName)) { + if (scoped) { + reserveNameInNestedScopes(generatedName); + } + else { + generatedNames.add(generatedName); + } + return generatedName; } - writePunctuation(getClosingBracket(format)); + i++; } } - // Writers - - function writeLiteral(s: string) { - writer.writeLiteral(s); - } - - function writeStringLiteral(s: string) { - writer.writeStringLiteral(s); + function makeFileLevelOptimisticUniqueName(name: string) { + return makeUniqueName(name, isFileLevelUniqueName, /*optimistic*/ true); } - function writeBase(s: string) { - writer.write(s); + /** + * Generates a unique name for a ModuleDeclaration or EnumDeclaration. + */ + function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { + const name = getTextOfNode(node.name); + // Use module/enum name itself if it is unique, otherwise make a unique variation + return isUniqueLocalName(name, node) ? name : makeUniqueName(name); } - function writeSymbol(s: string, sym: Symbol) { - writer.writeSymbol(s, sym); + /** + * Generates a unique name for an ImportDeclaration or ExportDeclaration. + */ + function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { + const expr = getExternalModuleName(node)!; // TODO: GH#18217 + const baseName = isStringLiteral(expr) ? + makeIdentifierFromModuleName(expr.text) : "module"; + return makeUniqueName(baseName); } - function writePunctuation(s: string) { - writer.writePunctuation(s); + /** + * Generates a unique name for a default export. + */ + function generateNameForExportDefault() { + return makeUniqueName("default"); } - function writeTrailingSemicolon() { - writer.writeTrailingSemicolon(";"); + /** + * Generates a unique name for a class expression. + */ + function generateNameForClassExpression() { + return makeUniqueName("class"); } - function writeKeyword(s: string) { - writer.writeKeyword(s); + function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration) { + if (isIdentifier(node.name)) { + return generateNameCached(node.name); + } + return makeTempVariableName(TempFlags.Auto); } - function writeOperator(s: string) { - writer.writeOperator(s); + /** + * Generates a unique name from a node. + */ + function generateNameForNode(node: Node, flags?: GeneratedIdentifierFlags): string { + switch (node.kind) { + case SyntaxKind.Identifier: + return makeUniqueName( + getTextOfNode(node), + isUniqueName, + !!(flags! & GeneratedIdentifierFlags.Optimistic), + !!(flags! & GeneratedIdentifierFlags.ReservedInNestedScopes) + ); + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.EnumDeclaration: + return generateNameForModuleOrEnum(node); + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportDeclaration: + return generateNameForImportOrExportDeclaration(node); + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ExportAssignment: + return generateNameForExportDefault(); + case SyntaxKind.ClassExpression: + return generateNameForClassExpression(); + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return generateNameForMethodOrAccessor(node); + case SyntaxKind.ComputedPropertyName: + return makeTempVariableName(TempFlags.Auto, /*reserveInNestedScopes*/ true); + default: + return makeTempVariableName(TempFlags.Auto); + } } - function writeParameter(s: string) { - writer.writeParameter(s); - } + /** + * Generates a unique identifier for a node. + */ + function makeName(name: GeneratedIdentifier) { + switch (name.autoGenerateFlags & GeneratedIdentifierFlags.KindMask) { + case GeneratedIdentifierFlags.Auto: + return makeTempVariableName(TempFlags.Auto, !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes)); + case GeneratedIdentifierFlags.Loop: + return makeTempVariableName(TempFlags._i, !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes)); + case GeneratedIdentifierFlags.Unique: + return makeUniqueName( + idText(name), + (name.autoGenerateFlags & GeneratedIdentifierFlags.FileLevel) ? isFileLevelUniqueName : isUniqueName, + !!(name.autoGenerateFlags & GeneratedIdentifierFlags.Optimistic), + !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes) + ); + } - function writeComment(s: string) { - writer.writeComment(s); + return Debug.fail("Unsupported GeneratedIdentifierKind."); } - function writeSpace() { - writer.writeSpace(" "); - } + /** + * Gets the node from which a name should be generated. + */ + function getNodeForGeneratedName(name: GeneratedIdentifier) { + const autoGenerateId = name.autoGenerateId; + let node = name as Node; + let original = node.original; + while (original) { + node = original; - function writeProperty(s: string) { - writer.writeProperty(s); - } + // if "node" is a different generated name (having a different + // "autoGenerateId"), use it and stop traversing. + if (isIdentifier(node) + && !!(node.autoGenerateFlags! & GeneratedIdentifierFlags.Node) + && node.autoGenerateId !== autoGenerateId) { + break; + } - function writeLine(count = 1) { - for (let i = 0; i < count; i++) { - writer.writeLine(i > 0); + original = node.original; } - } - function increaseIndent() { - writer.increaseIndent(); + // otherwise, return the original node for the source; + return node; } - function decreaseIndent() { - writer.decreaseIndent(); - } + // Comments - function writeToken(token: SyntaxKind, pos: number, writer: (s: string) => void, contextNode?: Node) { - return !sourceMapsDisabled - ? emitTokenWithSourceMap(contextNode, token, writer, pos, writeTokenText) - : writeTokenText(token, writer, pos); - } + function emitLeadingCommentsOfNode(node: Node, emitFlags: EmitFlags, pos: number, end: number) { + enterComment(); + hasWrittenComment = false; - function writeTokenNode(node: Node, writer: (s: string) => void) { - if (onBeforeEmitToken) { - onBeforeEmitToken(node); - } - writer(tokenToString(node.kind)!); - if (onAfterEmitToken) { - onAfterEmitToken(node); + // We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation. + // It is expensive to walk entire tree just to set one kind of node to have no comments. + const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0 || node.kind === SyntaxKind.JsxText; + const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0 || node.kind === SyntaxKind.JsxText; + + // Save current container state on the stack. + if ((pos > 0 || end > 0) && pos !== end) { + // Emit leading comments if the position is not synthesized and the node + // has not opted out from emitting leading comments. + if (!skipLeadingComments) { + emitLeadingComments(pos, /*isEmittedNode*/ node.kind !== SyntaxKind.NotEmittedStatement); + } + + if (!skipLeadingComments || (pos >= 0 && (emitFlags & EmitFlags.NoLeadingComments) !== 0)) { + // Advance the container position if comments get emitted or if they've been disabled explicitly using NoLeadingComments. + containerPos = pos; + } + + if (!skipTrailingComments || (end >= 0 && (emitFlags & EmitFlags.NoTrailingComments) !== 0)) { + // As above. + containerEnd = end; + + // To avoid invalid comment emit in a down-level binding pattern, we + // keep track of the last declaration list container's end + if (node.kind === SyntaxKind.VariableDeclarationList) { + declarationListContainerEnd = end; + } + } } + forEach(getSyntheticLeadingComments(node), emitLeadingSynthesizedComment); + exitComment(); } - function writeTokenText(token: SyntaxKind, writer: (s: string) => void): void; - function writeTokenText(token: SyntaxKind, writer: (s: string) => void, pos: number): number; - function writeTokenText(token: SyntaxKind, writer: (s: string) => void, pos?: number): number { - const tokenString = tokenToString(token)!; - writer(tokenString); - return pos! < 0 ? pos! : pos! + tokenString.length; + function emitTrailingCommentsOfNode(node: Node, emitFlags: EmitFlags, pos: number, end: number, savedContainerPos: number, savedContainerEnd: number, savedDeclarationListContainerEnd: number) { + enterComment(); + const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0 || node.kind === SyntaxKind.JsxText; + forEach(getSyntheticTrailingComments(node), emitTrailingSynthesizedComment); + if ((pos > 0 || end > 0) && pos !== end) { + // Restore previous container state. + containerPos = savedContainerPos; + containerEnd = savedContainerEnd; + declarationListContainerEnd = savedDeclarationListContainerEnd; + + // Emit trailing comments if the position is not synthesized and the node + // has not opted out from emitting leading comments and is an emitted node. + if (!skipTrailingComments && node.kind !== SyntaxKind.NotEmittedStatement) { + emitTrailingComments(end); + } + } + exitComment(); } - function writeLineOrSpace(parentNode: Node, prevChildNode: Node, nextChildNode: Node) { - if (getEmitFlags(parentNode) & EmitFlags.SingleLine) { - writeSpace(); + function emitLeadingSynthesizedComment(comment: SynthesizedComment) { + if (comment.hasLeadingNewline || comment.kind === SyntaxKind.SingleLineCommentTrivia) { + writer.writeLine(); } - else if (preserveSourceNewlines) { - const lines = getLinesBetweenNodes(parentNode, prevChildNode, nextChildNode); - if (lines) { - writeLine(lines); - } - else { - writeSpace(); - } + writeSynthesizedComment(comment); + if (comment.hasTrailingNewLine || comment.kind === SyntaxKind.SingleLineCommentTrivia) { + writer.writeLine(); } else { - writeLine(); + writer.writeSpace(" "); } } - function writeLines(text: string): void { - const lines = text.split(/\r\n?|\n/g); - const indentation = guessIndentation(lines); - for (const lineText of lines) { - const line = indentation ? lineText.slice(indentation) : lineText; - if (line.length) { - writeLine(); - write(line); - } + function emitTrailingSynthesizedComment(comment: SynthesizedComment) { + if (!writer.isAtStartOfLine()) { + writer.writeSpace(" "); + } + writeSynthesizedComment(comment); + if (comment.hasTrailingNewLine) { + writer.writeLine(); } } - function writeLinesAndIndent(lineCount: number, writeSpaceIfNotIndenting: boolean) { - if (lineCount) { - increaseIndent(); - writeLine(lineCount); - } - else if (writeSpaceIfNotIndenting) { - writeSpace(); - } + function writeSynthesizedComment(comment: SynthesizedComment) { + const text = formatSynthesizedComment(comment); + const lineMap = comment.kind === SyntaxKind.MultiLineCommentTrivia ? computeLineStarts(text) : undefined; + writeCommentRange(text, lineMap!, writer, 0, text.length, newLine); } - // Helper function to decrease the indent if we previously indented. Allows multiple - // previous indent values to be considered at a time. This also allows caller to just - // call this once, passing in all their appropriate indent values, instead of needing - // to call this helper function multiple times. - function decreaseIndentIf(value1: boolean | number | undefined, value2?: boolean | number) { - if (value1) { - decreaseIndent(); - } - if (value2) { - decreaseIndent(); - } + function formatSynthesizedComment(comment: SynthesizedComment) { + return comment.kind === SyntaxKind.MultiLineCommentTrivia + ? `/*${comment.text}*/` + : `//${comment.text}`; } - function getLeadingLineTerminatorCount(parentNode: TextRange, children: readonly Node[], format: ListFormat): number { - if (format & ListFormat.PreserveLines || preserveSourceNewlines) { - if (format & ListFormat.PreferNewLine) { - return 1; - } + function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) { + enterComment(); + const { pos, end } = detachedRange; + const emitFlags = getEmitFlags(node); + const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0; + const skipTrailingComments = commentsDisabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0; + if (!skipLeadingComments) { + emitDetachedCommentsAndUpdateCommentsInfo(detachedRange); + } - const firstChild = children[0]; - if (firstChild === undefined) { - return rangeIsOnSingleLine(parentNode, currentSourceFile!) ? 0 : 1; - } - if (firstChild.pos === nextListElementPos) { - // If this child starts at the beginning of a list item in a parent list, its leading - // line terminators have already been written as the separating line terminators of the - // parent list. Example: - // - // class Foo { - // constructor() {} - // public foo() {} - // } - // - // The outer list is the list of class members, with one line terminator between the - // constructor and the method. The constructor is written, the separating line terminator - // is written, and then we start emitting the method. Its modifiers ([public]) constitute an inner - // list, so we look for its leading line terminators. If we didn't know that we had already - // written a newline as part of the parent list, it would appear that we need to write a - // leading newline to start the modifiers. - return 0; - } - if (firstChild.kind === SyntaxKind.JsxText) { - // JsxText will be written with its leading whitespace, so don't add more manually. - return 0; - } - if (!positionIsSynthesized(parentNode.pos) && - !nodeIsSynthesized(firstChild) && - (!firstChild.parent || getOriginalNode(firstChild.parent) === getOriginalNode(parentNode as Node)) - ) { - if (preserveSourceNewlines) { - return getEffectiveLines( - includeComments => getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter( - firstChild.pos, - parentNode.pos, - currentSourceFile!, - includeComments)); - } - return rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile!) ? 0 : 1; - } - if (synthesizedNodeStartsOnNewLine(firstChild, format)) { - return 1; - } + exitComment(); + if (emitFlags & EmitFlags.NoNestedComments && !commentsDisabled) { + commentsDisabled = true; + emitCallback(node); + commentsDisabled = false; + } + else { + emitCallback(node); } - return format & ListFormat.MultiLine ? 1 : 0; - } - function getSeparatingLineTerminatorCount(previousNode: Node | undefined, nextNode: Node, format: ListFormat): number { - if (format & ListFormat.PreserveLines || preserveSourceNewlines) { - if (previousNode === undefined || nextNode === undefined) { - return 0; - } - if (nextNode.kind === SyntaxKind.JsxText) { - // JsxText will be written with its leading whitespace, so don't add more manually. - return 0; - } - else if (!nodeIsSynthesized(previousNode) && !nodeIsSynthesized(nextNode) && previousNode.parent === nextNode.parent) { - if (preserveSourceNewlines) { - return getEffectiveLines( - includeComments => getLinesBetweenRangeEndAndRangeStart( - previousNode, - nextNode, - currentSourceFile!, - includeComments)); - } - return rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile!) ? 0 : 1; - } - else if (synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format)) { - return 1; + enterComment(); + if (!skipTrailingComments) { + emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true); + if (hasWrittenComment && !writer.isAtStartOfLine()) { + writer.writeLine(); } } - else if (getStartsOnNewLine(nextNode)) { - return 1; - } - return format & ListFormat.MultiLine ? 1 : 0; + exitComment(); + } - function getClosingLineTerminatorCount(parentNode: TextRange, children: readonly Node[], format: ListFormat): number { - if (format & ListFormat.PreserveLines || preserveSourceNewlines) { - if (format & ListFormat.PreferNewLine) { - return 1; - } + function emitLeadingComments(pos: number, isEmittedNode: boolean) { + hasWrittenComment = false; - const lastChild = lastOrUndefined(children); - if (lastChild === undefined) { - return rangeIsOnSingleLine(parentNode, currentSourceFile!) ? 0 : 1; - } - if (!positionIsSynthesized(parentNode.pos) && !nodeIsSynthesized(lastChild) && (!lastChild.parent || lastChild.parent === parentNode)) { - if (preserveSourceNewlines) { - const end = isNodeArray(children) && !positionIsSynthesized(children.end) ? children.end : lastChild.end; - return getEffectiveLines( - includeComments => getLinesBetweenPositionAndNextNonWhitespaceCharacter( - end, - parentNode.end, - currentSourceFile!, - includeComments)); - } - return rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile!) ? 0 : 1; + if (isEmittedNode) { + if (pos === 0 && currentSourceFile?.isDeclarationFile) { + forEachLeadingCommentToEmit(pos, emitNonTripleSlashLeadingComment); } - if (synthesizedNodeStartsOnNewLine(lastChild, format)) { - return 1; + else { + forEachLeadingCommentToEmit(pos, emitLeadingComment); } } - if (format & ListFormat.MultiLine && !(format & ListFormat.NoTrailingNewLine)) { - return 1; - } - return 0; - } - - function getEffectiveLines(getLineDifference: (includeComments: boolean) => number) { - // If 'preserveSourceNewlines' is disabled, we should never call this function - // because it could be more expensive than alternative approximations. - Debug.assert(!!preserveSourceNewlines); - // We start by measuring the line difference from a position to its adjacent comments, - // so that this is counted as a one-line difference, not two: - // - // node1; - // // NODE2 COMMENT - // node2; - const lines = getLineDifference(/*includeComments*/ true); - if (lines === 0) { - // However, if the line difference considering comments was 0, we might have this: - // - // node1; // NODE2 COMMENT - // node2; - // - // in which case we should be ignoring node2's comment, so this too is counted as - // a one-line difference, not zero. - return getLineDifference(/*includeComments*/ false); - } - return lines; - } - - function writeLineSeparatorsAndIndentBefore(node: Node, parent: Node): boolean { - const leadingNewlines = preserveSourceNewlines && getLeadingLineTerminatorCount(parent, [node], ListFormat.None); - if (leadingNewlines) { - writeLinesAndIndent(leadingNewlines, /*writeLinesIfNotIndenting*/ false); + else if (pos === 0) { + // If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node, + // unless it is a triple slash comment at the top of the file. + // For Example: + // /// + // declare var x; + // /// + // interface F {} + // The first /// will NOT be removed while the second one will be removed even though both node will not be emitted + forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment); } - return !!leadingNewlines; } - function writeLineSeparatorsAfter(node: Node, parent: Node) { - const trailingNewlines = preserveSourceNewlines && getClosingLineTerminatorCount(parent, [node], ListFormat.None); - if (trailingNewlines) { - writeLine(trailingNewlines); + function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) { + if (isTripleSlashComment(commentPos, commentEnd)) { + emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); } } - function synthesizedNodeStartsOnNewLine(node: Node, format: ListFormat) { - if (nodeIsSynthesized(node)) { - const startsOnNewLine = getStartsOnNewLine(node); - if (startsOnNewLine === undefined) { - return (format & ListFormat.PreferNewLine) !== 0; - } - - return startsOnNewLine; + function emitNonTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) { + if (!isTripleSlashComment(commentPos, commentEnd)) { + emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); } + } - return (format & ListFormat.PreferNewLine) !== 0; + function shouldWriteComment(text: string, pos: number) { + if (printerOptions.onlyPrintJsDocStyle) { + return (isJSDocLikeText(text, pos) || isPinnedComment(text, pos)); + } + return true; } - function getLinesBetweenNodes(parent: Node, node1: Node, node2: Node): number { - if (getEmitFlags(parent) & EmitFlags.NoIndentation) { - return 0; + function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) { + if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; + if (!hasWrittenComment) { + emitNewLineBeforeLeadingCommentOfPosition(getCurrentLineMap(), writer, rangePos, commentPos); + hasWrittenComment = true; } - parent = skipSynthesizedParentheses(parent); - node1 = skipSynthesizedParentheses(node1); - node2 = skipSynthesizedParentheses(node2); + // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + emitPos(commentPos); + writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + emitPos(commentEnd); - // Always use a newline for synthesized code if the synthesizer desires it. - if (getStartsOnNewLine(node2)) { - return 1; + if (hasTrailingNewLine) { + writer.writeLine(); + } + else if (kind === SyntaxKind.MultiLineCommentTrivia) { + writer.writeSpace(" "); } + } - if (!nodeIsSynthesized(parent) && !nodeIsSynthesized(node1) && !nodeIsSynthesized(node2)) { - if (preserveSourceNewlines) { - return getEffectiveLines( - includeComments => getLinesBetweenRangeEndAndRangeStart( - node1, - node2, - currentSourceFile!, - includeComments)); - } - return rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile!) ? 0 : 1; + function emitLeadingCommentsOfPosition(pos: number) { + if (commentsDisabled || pos === -1) { + return; } - return 0; + emitLeadingComments(pos, /*isEmittedNode*/ true); } - function isEmptyBlock(block: BlockLike) { - return block.statements.length === 0 - && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile!); + function emitTrailingComments(pos: number) { + forEachTrailingCommentToEmit(pos, emitTrailingComment); } - function skipSynthesizedParentheses(node: Node) { - while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { - node = (node).expression; + function emitTrailingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) { + if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; + // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/ + if (!writer.isAtStartOfLine()) { + writer.writeSpace(" "); } - return node; - } + emitPos(commentPos); + writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + emitPos(commentEnd); - function getTextOfNode(node: Node, includeTrivia?: boolean): string { - if (isGeneratedIdentifier(node)) { - return generateName(node); - } - else if ((isIdentifier(node) || isPrivateIdentifier(node)) && (nodeIsSynthesized(node) || !node.parent || !currentSourceFile || (node.parent && currentSourceFile && getSourceFileOfNode(node) !== getOriginalNode(currentSourceFile)))) { - return idText(node); - } - else if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { - return getTextOfNode((node).textSourceNode!, includeTrivia); - } - else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { - return node.text; + if (hasTrailingNewLine) { + writer.writeLine(); } - - return getSourceTextOfNodeFromSourceFile(currentSourceFile!, node, includeTrivia); } - function getLiteralTextOfNode(node: LiteralLikeNode, neverAsciiEscape: boolean | undefined, jsxAttributeEscape: boolean): string { - if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { - const textSourceNode = (node).textSourceNode!; - if (isIdentifier(textSourceNode) || isNumericLiteral(textSourceNode)) { - const text = isNumericLiteral(textSourceNode) ? textSourceNode.text : getTextOfNode(textSourceNode); - return jsxAttributeEscape ? `"${escapeJsxAttributeString(text)}"` : - neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? `"${escapeString(text)}"` : - `"${escapeNonAsciiString(text)}"`; - } - else { - return getLiteralTextOfNode(textSourceNode, neverAsciiEscape, jsxAttributeEscape); - } + function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean, forceNoNewline?: boolean) { + if (commentsDisabled) { + return; } + enterComment(); + forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : forceNoNewline ? emitTrailingCommentOfPositionNoNewline : emitTrailingCommentOfPosition); + exitComment(); + } - const flags = (neverAsciiEscape ? GetLiteralTextFlags.NeverAsciiEscape : 0) - | (jsxAttributeEscape ? GetLiteralTextFlags.JsxAttributeEscape : 0) - | (printerOptions.terminateUnterminatedLiterals ? GetLiteralTextFlags.TerminateUnterminatedLiterals : 0) - | (printerOptions.target && printerOptions.target === ScriptTarget.ESNext ? GetLiteralTextFlags.AllowNumericSeparator : 0); + function emitTrailingCommentOfPositionNoNewline(commentPos: number, commentEnd: number, kind: SyntaxKind) { + // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space - return getLiteralText(node, currentSourceFile!, flags); - } + emitPos(commentPos); + writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + emitPos(commentEnd); - /** - * Push a new name generation scope. - */ - function pushNameGenerationScope(node: Node | undefined) { - if (node && getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { - return; + if (kind === SyntaxKind.SingleLineCommentTrivia) { + writer.writeLine(); // still write a newline for single-line comments, so closing tokens aren't written on the same line } - tempFlagsStack.push(tempFlags); - tempFlags = 0; - reservedNamesStack.push(reservedNames); } - /** - * Pop the current name generation scope. - */ - function popNameGenerationScope(node: Node | undefined) { - if (node && getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { - return; + function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) { + // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space + + emitPos(commentPos); + writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + emitPos(commentEnd); + + if (hasTrailingNewLine) { + writer.writeLine(); + } + else { + writer.writeSpace(" "); } - tempFlags = tempFlagsStack.pop()!; - reservedNames = reservedNamesStack.pop()!; } - function reserveNameInNestedScopes(name: string) { - if (!reservedNames || reservedNames === lastOrUndefined(reservedNamesStack)) { - reservedNames = new Set(); + function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) { + // Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments + if (currentSourceFile && (containerPos === -1 || pos !== containerPos)) { + if (hasDetachedComments(pos)) { + forEachLeadingCommentWithoutDetachedComments(cb); + } + else { + forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos); + } } - reservedNames.add(name); } - function generateNames(node: Node | undefined) { - if (!node) return; - switch (node.kind) { - case SyntaxKind.Block: - forEach((node).statements, generateNames); - break; - case SyntaxKind.LabeledStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - generateNames((node).statement); - break; - case SyntaxKind.IfStatement: - generateNames((node).thenStatement); - generateNames((node).elseStatement); - break; - case SyntaxKind.ForStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.ForInStatement: - generateNames((node).initializer); - generateNames((node).statement); - break; - case SyntaxKind.SwitchStatement: - generateNames((node).caseBlock); - break; - case SyntaxKind.CaseBlock: - forEach((node).clauses, generateNames); - break; - case SyntaxKind.CaseClause: - case SyntaxKind.DefaultClause: - forEach((node).statements, generateNames); - break; - case SyntaxKind.TryStatement: - generateNames((node).tryBlock); - generateNames((node).catchClause); - generateNames((node).finallyBlock); - break; - case SyntaxKind.CatchClause: - generateNames((node).variableDeclaration); - generateNames((node).block); - break; - case SyntaxKind.VariableStatement: - generateNames((node).declarationList); - break; - case SyntaxKind.VariableDeclarationList: - forEach((node).declarations, generateNames); - break; - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.BindingElement: - case SyntaxKind.ClassDeclaration: - generateNameIfNeeded((node).name); - break; - case SyntaxKind.FunctionDeclaration: - generateNameIfNeeded((node).name); - if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { - forEach((node).parameters, generateNames); - generateNames((node).body); - } - break; - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - forEach((node).elements, generateNames); - break; - case SyntaxKind.ImportDeclaration: - generateNames((node).importClause); - break; - case SyntaxKind.ImportClause: - generateNameIfNeeded((node).name); - generateNames((node).namedBindings); - break; - case SyntaxKind.NamespaceImport: - generateNameIfNeeded((node).name); - break; - case SyntaxKind.NamespaceExport: - generateNameIfNeeded((node).name); - break; - case SyntaxKind.NamedImports: - forEach((node).elements, generateNames); - break; - case SyntaxKind.ImportSpecifier: - generateNameIfNeeded((node).propertyName || (node).name); - break; + function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { + // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments + if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { + forEachTrailingCommentRange(currentSourceFile.text, end, cb); } } - function generateMemberNames(node: Node | undefined) { - if (!node) return; - switch (node.kind) { - case SyntaxKind.PropertyAssignment: - case SyntaxKind.ShorthandPropertyAssignment: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - generateNameIfNeeded((node).name); - break; + function hasDetachedComments(pos: number) { + return detachedCommentsInfo !== undefined && last(detachedCommentsInfo).nodePos === pos; + } + + function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) { + // get the leading comments from detachedPos + const pos = last(detachedCommentsInfo!).detachedCommentEndPos; + if (detachedCommentsInfo!.length - 1) { + detachedCommentsInfo!.pop(); + } + else { + detachedCommentsInfo = undefined; } + + forEachLeadingCommentRange(currentSourceFile!.text, pos, cb, /*state*/ pos); } - function generateNameIfNeeded(name: DeclarationName | undefined) { - if (name) { - if (isGeneratedIdentifier(name)) { - generateName(name); + function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) { + const currentDetachedCommentInfo = emitDetachedComments(currentSourceFile!.text, getCurrentLineMap(), writer, emitComment, range, newLine, commentsDisabled); + if (currentDetachedCommentInfo) { + if (detachedCommentsInfo) { + detachedCommentsInfo.push(currentDetachedCommentInfo); } - else if (isBindingPattern(name)) { - generateNames(name); + else { + detachedCommentsInfo = [currentDetachedCommentInfo]; } } } + function emitComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) { + if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; + emitPos(commentPos); + writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine); + emitPos(commentEnd); + } + /** - * Generate the text for a generated identifier. + * Determine if the given comment is a triple-slash + * + * @return true if the comment is a triple-slash comment else false */ - function generateName(name: GeneratedIdentifier) { - if ((name.autoGenerateFlags & GeneratedIdentifierFlags.KindMask) === GeneratedIdentifierFlags.Node) { - // Node names generate unique names based on their original node - // and are cached based on that node's id. - return generateNameCached(getNodeForGeneratedName(name), name.autoGenerateFlags); - } - else { - // Auto, Loop, and Unique names are cached based on their unique - // autoGenerateId. - const autoGenerateId = name.autoGenerateId!; - return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = makeName(name)); - } + function isTripleSlashComment(commentPos: number, commentEnd: number) { + return isRecognizedTripleSlashComment(currentSourceFile!.text, commentPos, commentEnd); } - function generateNameCached(node: Node, flags?: GeneratedIdentifierFlags) { - const nodeId = getNodeId(node); - return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node, flags)); + // Source Maps + + function getParsedSourceMap(node: UnparsedSource) { + if (node.parsedSourceMap === undefined && node.sourceMapText !== undefined) { + node.parsedSourceMap = tryParseRawSourceMap(node.sourceMapText) || false; + } + return node.parsedSourceMap || undefined; } /** - * Returns a value indicating whether a name is unique globally, within the current file, - * or within the NameGenerator. + * Skips trivia such as comments and white-space that can be optionally overridden by the source-map source */ - function isUniqueName(name: string): boolean { - return isFileLevelUniqueName(name) - && !generatedNames.has(name) - && !(reservedNames && reservedNames.has(name)); + function skipSourceTrivia(source: SourceMapSource, pos: number): number { + return source.skipTrivia ? source.skipTrivia(pos) : skipTrivia(source.text, pos); } /** - * Returns a value indicating whether a name is unique globally or within the current file. + * Emits a mapping. + * + * If the position is synthetic (undefined or a negative value), no mapping will be + * created. + * + * @param pos The position. */ - function isFileLevelUniqueName(name: string) { - return currentSourceFile ? ts.isFileLevelUniqueName(currentSourceFile, name, hasGlobalName) : true; + function emitPos(pos: number) { + if (sourceMapsDisabled || positionIsSynthesized(pos) || isJsonSourceMapSource(sourceMapSource)) { + return; + } + + const { line: sourceLine, character: sourceCharacter } = getLineAndCharacterOfPosition(sourceMapSource, pos); + sourceMapGenerator!.addMapping( + writer.getLine(), + writer.getColumn(), + sourceMapSourceIndex, + sourceLine, + sourceCharacter, + /*nameIndex*/ undefined); } - /** - * Returns a value indicating whether a name is unique within a container. - */ - function isUniqueLocalName(name: string, container: Node): boolean { - for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer!) { - if (node.locals) { - const local = node.locals.get(escapeLeadingUnderscores(name)); - // We conservatively include alias symbols to cover cases where they're emitted as locals - if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { - return false; - } - } + function emitSourcePos(source: SourceMapSource, pos: number) { + if (source !== sourceMapSource) { + const savedSourceMapSource = sourceMapSource; + const savedSourceMapSourceIndex = sourceMapSourceIndex; + setSourceMapSource(source); + emitPos(pos); + resetSourceMapSource(savedSourceMapSource, savedSourceMapSourceIndex); + } + else { + emitPos(pos); } - return true; } /** - * Return the next available name in the pattern _a ... _z, _0, _1, ... - * TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name. - * Note that names generated by makeTempVariableName and makeUniqueName will never conflict. + * Emits a token of a node with possible leading and trailing source maps. + * + * @param node The node containing the token. + * @param token The token to emit. + * @param tokenStartPos The start pos of the token. + * @param emitCallback The callback used to emit the token. */ - function makeTempVariableName(flags: TempFlags, reservedInNestedScopes?: boolean): string { - if (flags && !(tempFlags & flags)) { - const name = flags === TempFlags._i ? "_i" : "_n"; - if (isUniqueName(name)) { - tempFlags |= flags; - if (reservedInNestedScopes) { - reserveNameInNestedScopes(name); - } - return name; - } + function emitTokenWithSourceMap(node: Node | undefined, token: SyntaxKind, writer: (s: string) => void, tokenPos: number, emitCallback: (token: SyntaxKind, writer: (s: string) => void, tokenStartPos: number) => number) { + if (sourceMapsDisabled || node && isInJsonFile(node)) { + return emitCallback(token, writer, tokenPos); } - while (true) { - const count = tempFlags & TempFlags.CountMask; - tempFlags++; - // Skip over 'i' and 'n' - if (count !== 8 && count !== 13) { - const name = count < 26 - ? "_" + String.fromCharCode(CharacterCodes.a + count) - : "_" + (count - 26); - if (isUniqueName(name)) { - if (reservedInNestedScopes) { - reserveNameInNestedScopes(name); - } - return name; - } - } + + const emitNode = node && node.emitNode; + const emitFlags = emitNode && emitNode.flags || EmitFlags.None; + const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token]; + const source = range && range.source || sourceMapSource; + + tokenPos = skipSourceTrivia(source, range ? range.pos : tokenPos); + if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) { + emitSourcePos(source, tokenPos); } + + tokenPos = emitCallback(token, writer, tokenPos); + + if (range) tokenPos = range.end; + if ((emitFlags & EmitFlags.NoTokenTrailingSourceMaps) === 0 && tokenPos >= 0) { + emitSourcePos(source, tokenPos); + } + + return tokenPos; } - /** - * Generate a name that is unique within the current file and doesn't conflict with any names - * in global scope. The name is formed by adding an '_n' suffix to the specified base name, - * where n is a positive integer. Note that names generated by makeTempVariableName and - * makeUniqueName are guaranteed to never conflict. - * If `optimistic` is set, the first instance will use 'baseName' verbatim instead of 'baseName_1' - */ - function makeUniqueName(baseName: string, checkFn: (name: string) => boolean = isUniqueName, optimistic?: boolean, scoped?: boolean): string { - if (optimistic) { - if (checkFn(baseName)) { - if (scoped) { - reserveNameInNestedScopes(baseName); - } - else { - generatedNames.add(baseName); - } - return baseName; - } + function setSourceMapSource(source: SourceMapSource) { + if (sourceMapsDisabled) { + return; } - // Find the first unique 'name_n', where n is a positive number - if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { - baseName += "_"; + + sourceMapSource = source; + + if (source === mostRecentlyAddedSourceMapSource) { + // Fast path for when the new source map is the most recently added, in which case + // we use its captured index without going through the source map generator. + sourceMapSourceIndex = mostRecentlyAddedSourceMapSourceIndex; + return; } - let i = 1; - while (true) { - const generatedName = baseName + i; - if (checkFn(generatedName)) { - if (scoped) { - reserveNameInNestedScopes(generatedName); - } - else { - generatedNames.add(generatedName); - } - return generatedName; - } - i++; + + if (isJsonSourceMapSource(source)) { + return; + } + + sourceMapSourceIndex = sourceMapGenerator!.addSource(source.fileName); + if (printerOptions.inlineSources) { + sourceMapGenerator!.setSourceContent(sourceMapSourceIndex, source.text); } + + mostRecentlyAddedSourceMapSource = source; + mostRecentlyAddedSourceMapSourceIndex = sourceMapSourceIndex; + } + + function resetSourceMapSource(source: SourceMapSource, sourceIndex: number) { + sourceMapSource = source; + sourceMapSourceIndex = sourceIndex; + } + + function isJsonSourceMapSource(sourceFile: SourceMapSource) { + return fileExtensionIs(sourceFile.fileName, Extension.Json); } + } + + const enum PreprintPipelinePhase { + Notification, + Substitution, + Visit + } + + function createPreprinter(handlers: PrintHandlers) { + const { + substituteNode = noEmitSubstitution, + onEmitNode = noEmitNotification, + isEmitNotificationEnabled + } = handlers; - function makeFileLevelOptimisticUniqueName(name: string) { - return makeUniqueName(name, isFileLevelUniqueName, /*optimistic*/ true); - } + let pipelineResult: Node | undefined; + + // Outer visitors + // + // These visitors are invoked by inner visitors to re-enter the pipeline + // for notification and substitution. + + const visit = makeVisitor(pipelineVisitorForUnspecified); + const visitSourceFile = makeVisitor(pipelineVisitorForSourceFile, isSourceFile); + const visitIdentifierName = makeVisitor(pipelineVisitorForIdentifierName, isIdentifier); + const visitModuleName = makeVisitor(pipelineVisitorForIdentifierNameOrUnspecified, isModuleName); + const visitPropertyName = makeVisitor(pipelineVisitorForIdentifierNameOrUnspecified, isPropertyName); + const visitMemberName = makeVisitor(pipelineVisitorForIdentifierNameOrUnspecified, isMemberName); + const visitBindingName = makeVisitor(pipelineVisitorForIdentifierNameOrUnspecified, isBindingName); + const visitEntityName = makeVisitor(pipelineVisitorForIdentifierReferenceOrUnspecified, isEntityName); + const visitExpression = makeVisitor(pipelineVisitorForExpression, isExpression); + const visitForInitializer = makeVisitor(pipelineVisitorForForInitializer, isForInitializer); + const visitTypeNode = makeVisitor(pipelineVisitorForUnspecified, isTypeNode); + const visitEmbeddedStatement = makeVisitor(pipelineVisitorForEmbeddedStatement, isStatement, factory.liftToBlock); + const visitJsxAttributeValue = makeVisitor(pipelineVisitorForJsxAttributeValue, isStringLiteralOrJsxExpression); + const visitMappedTypeParameter = makeVisitor(pipelineVisitorForMappedTypeParameter, isTypeParameterDeclaration); + const visitConciseBody = makeVisitor(pipelineVisitorForConciseBody, isConciseBody); + const visitFunctionBody = makeVisitor(pipelineVisitorForUnspecified, isFunctionBody); + const visitList = makeListVisitor(pipelineVisitorForUnspecified); + const visitTypeNodeList = makeListVisitor(pipelineVisitorForUnspecified, isTypeNode); + const visitExpressionList = makeListVisitor(pipelineVisitorForExpression, isExpression); + const visitParameterList = makeListVisitor(pipelineVisitorForUnspecified, isParameter); + + function makeVisitor(outerVisitor: (node: Node) => Node | undefined, defaultTest?: (node: Node) => node is T, lift?: (nodes: readonly Node[]) => Node) { + function visit(node: T, test: (node: Node) => node is U): U; + function visit(node: T | undefined, test: (node: Node) => node is U): U | undefined; + function visit(node: T, test?: (node: Node) => node is T): T; + function visit(node: T | undefined, test?: (node: Node) => node is T): T | undefined; + function visit(node: Node | undefined, test?: (node: Node) => node is T): Node | undefined { + return visitNode(node, outerVisitor, test || defaultTest, lift); + } + return visit; + } + + function makeListVisitor(outerVisitor: (node: Node) => Node | undefined, defaultTest?: (node: Node) => node is T) { + function visitList(nodes: NodeArray, test: (node: Node) => node is U): NodeArray; + function visitList(nodes: NodeArray | undefined, test: (node: Node) => node is U): NodeArray | undefined; + function visitList(nodes: NodeArray, test?: (node: Node) => boolean): NodeArray; + function visitList(nodes: NodeArray | undefined, test?: (node: Node) => boolean): NodeArray | undefined; + function visitList(nodes: NodeArray | undefined, test: (node: Node) => boolean = defaultTest || returnTrue): NodeArray | undefined { + return visitNodes(nodes, outerVisitor, test); + } + return visitList; + } + + // Pipeline Visitors + // + // These visitors execute our existing pipeline logic for notification and substitution, + // but adapted to our visitor pattern. In some cases, we refine the `EmitHint` we pass + // to the `onEmitNode` and `substituteNode` APIs to ensure they receive the appropriate + // context. + // + // For example, the ConciseBody of an arrow function could be an Identifier, in which + // case we would want to use `EmitHint.Expression` to ensure we treat the identifier + // as an expression during substitution. + + function pipelineVisitorForSourceFile(node: SourceFile) { return pipelineVisitorWorker(EmitHint.SourceFile, node); } + function pipelineVisitorForExpression(node: Expression) { return pipelineVisitorWorker(EmitHint.Expression, node); } + function pipelineVisitorForIdentifierName(node: Identifier) { return pipelineVisitorWorker(EmitHint.IdentifierName, node); } + function pipelineVisitorForIdentifierNameOrUnspecified(node: Node) { return pipelineVisitorWorker(isIdentifier(node) ? EmitHint.IdentifierName : EmitHint.Unspecified, node); } + function pipelineVisitorForIdentifierReferenceOrUnspecified(node: Node) { return pipelineVisitorWorker(isIdentifier(node) ? EmitHint.Expression : EmitHint.Unspecified, node); } + function pipelineVisitorForForInitializer(node: ForInitializer) { return pipelineVisitorWorker(isVariableDeclarationList(node) ? EmitHint.Unspecified : EmitHint.Expression, node); } + function pipelineVisitorForMappedTypeParameter(node: TypeParameterDeclaration) { return pipelineVisitorWorker(EmitHint.MappedTypeParameter, node); } + function pipelineVisitorForEmbeddedStatement(node: Statement) { return pipelineVisitorWorker(isEmptyStatement(node) ? EmitHint.EmbeddedStatement : EmitHint.Unspecified, node); } + function pipelineVisitorForJsxAttributeValue(node: StringLiteral | JsxExpression) { return pipelineVisitorWorker(isStringLiteral(node) ? EmitHint.JsxAttributeValue : EmitHint.Unspecified, node); } + function pipelineVisitorForConciseBody(node: ConciseBody) { return pipelineVisitorWorker(isBlock(node) ? EmitHint.Unspecified : EmitHint.Expression, node); } + function pipelineVisitorForUnspecified(node: Node) { return pipelineVisitorWorker(EmitHint.Unspecified, node); } /** - * Generates a unique name for a ModuleDeclaration or EnumDeclaration. + * Adapts the emit pipeline API to work with the visitor API */ - function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { - const name = getTextOfNode(node.name); - // Use module/enum name itself if it is unique, otherwise make a unique variation - return isUniqueLocalName(name, node) ? name : makeUniqueName(name); + function pipelineVisitorWorker(hint: EmitHint, node: Node) { + resetPipelineResult(); + // Get the first supported pipeline phase for this node and evaluate it. We can skip several stack + // frames if we aren't doing emit notification, so we check for substitution and direct callbacks + // and execute those immediately. + const pipelinePhase = getPipelinePhase(PreprintPipelinePhase.Notification, node); + if (pipelinePhase === pipelineVisitDirect) { + return visitor(hint, node); + } + + if (pipelinePhase === pipelineVisitWithSubstitution) { + // The next phase after substitution is always direct visitation, so we can reduce the call stack + // depth by calling the visitor directly. + return visitor(hint, substituteNode(hint, node)); + } + + pipelinePhase(hint, node); + Debug.assertIsDefined(pipelineResult); + const result = pipelineResult; + resetPipelineResult(); + return result; } - /** - * Generates a unique name for an ImportDeclaration or ExportDeclaration. - */ - function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { - const expr = getExternalModuleName(node)!; // TODO: GH#18217 - const baseName = isStringLiteral(expr) ? - makeIdentifierFromModuleName(expr.text) : "module"; - return makeUniqueName(baseName); + function resetPipelineResult() { + pipelineResult = undefined; } /** - * Generates a unique name for a default export. + * Gets the pipeline callback to pass to the relevant API (i.e., `substituteNode` or `onEmitNode`) */ - function generateNameForExportDefault() { - return makeUniqueName("default"); + function getPipelinePhase(phase: PreprintPipelinePhase, node: Node) { + switch (phase) { + case PreprintPipelinePhase.Notification: + if (onEmitNode !== noEmitNotification && (!isEmitNotificationEnabled || isEmitNotificationEnabled(node))) { + return pipelineVisitWithNotification; + } + // falls through + case PreprintPipelinePhase.Substitution: + if (substituteNode !== noEmitSubstitution) { + return pipelineVisitWithSubstitution; + } + // falls through + default: + return pipelineVisitDirect; + } } /** - * Generates a unique name for a class expression. + * A callback that can be evaluated to trigger emit notification as part of the emit pipeline. */ - function generateNameForClassExpression() { - return makeUniqueName("class"); + function pipelineVisitWithNotification(hint: EmitHint, node: Node) { + onEmitNode(hint, node, getPipelinePhase(PreprintPipelinePhase.Substitution, node)); } - function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration) { - if (isIdentifier(node.name)) { - return generateNameCached(node.name); - } - return makeTempVariableName(TempFlags.Auto); + /** + * A callback that can be evaluated to trigger JIT substitution as part of the emit pipeline. + */ + function pipelineVisitWithSubstitution(hint: EmitHint, node: Node) { + // Next phase is always direct visitation, so we can reduce the call stack + // depth by calling the visitor directly. + pipelineResult = visitor(hint, substituteNode(hint, node)); } /** - * Generates a unique name from a node. + * A callback that can be evaluated to visit the subtree of a node. */ - function generateNameForNode(node: Node, flags?: GeneratedIdentifierFlags): string { - switch (node.kind) { - case SyntaxKind.Identifier: - return makeUniqueName( - getTextOfNode(node), - isUniqueName, - !!(flags! & GeneratedIdentifierFlags.Optimistic), - !!(flags! & GeneratedIdentifierFlags.ReservedInNestedScopes) - ); - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.EnumDeclaration: - return generateNameForModuleOrEnum(node); - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ExportDeclaration: - return generateNameForImportOrExportDeclaration(node); - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ExportAssignment: - return generateNameForExportDefault(); - case SyntaxKind.ClassExpression: - return generateNameForClassExpression(); - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return generateNameForMethodOrAccessor(node); - case SyntaxKind.ComputedPropertyName: - return makeTempVariableName(TempFlags.Auto, /*reserveInNestedScopes*/ true); - default: - return makeTempVariableName(TempFlags.Auto); - } + function pipelineVisitDirect(hint: EmitHint, node: Node) { + pipelineResult = visitor(hint, node); } /** - * Generates a unique identifier for a node. + * Re-enters the visitor pattern from the pipeline pattern to perform + * tree updates and trigger parenthesization rules. */ - function makeName(name: GeneratedIdentifier) { - switch (name.autoGenerateFlags & GeneratedIdentifierFlags.KindMask) { - case GeneratedIdentifierFlags.Auto: - return makeTempVariableName(TempFlags.Auto, !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes)); - case GeneratedIdentifierFlags.Loop: - return makeTempVariableName(TempFlags._i, !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes)); - case GeneratedIdentifierFlags.Unique: - return makeUniqueName( - idText(name), - (name.autoGenerateFlags & GeneratedIdentifierFlags.FileLevel) ? isFileLevelUniqueName : isUniqueName, - !!(name.autoGenerateFlags & GeneratedIdentifierFlags.Optimistic), - !!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes) - ); + function visitor(hint: EmitHint, node: Node): Node { + // This should align with the assertions in `pipelineEmitWithHint`. + if (hint === EmitHint.SourceFile) return preprintSourceFile(cast(node, isSourceFile)); + if (hint === EmitHint.IdentifierName) return preprintIdentifier(cast(node, isIdentifier)); + if (hint === EmitHint.JsxAttributeValue) return cast(node, isStringLiteral); + if (hint === EmitHint.MappedTypeParameter) return preprintTypeParameterDeclaration(cast(node, isTypeParameterDeclaration)); + if (hint === EmitHint.EmbeddedStatement) return cast(node, isEmptyStatement); + + const kind = node.kind; + // No need to visit nodes without children. + if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken) || kind === SyntaxKind.ThisType) { + return node; } - return Debug.fail("Unsupported GeneratedIdentifierKind."); - } + if (hint === EmitHint.Unspecified) { + if (isKeyword(node.kind)) return node; + + switch (node.kind) { + // Identifiers + case SyntaxKind.Identifier: + return preprintIdentifier(node as Identifier); + + // Names + case SyntaxKind.QualifiedName: + Debug.type(node); + return factory.updateQualifiedName(node, + visitEntityName(node.left), + visitIdentifierName(node.right)); + + case SyntaxKind.ComputedPropertyName: + Debug.type(node); + return factory.updateComputedPropertyName(node, + visitExpression(node.expression)); + + // Signature elements + case SyntaxKind.TypeParameter: + return preprintTypeParameterDeclaration(node as TypeParameterDeclaration); + + case SyntaxKind.Parameter: + Debug.type(node); + return factory.updateParameterDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visit(node.dotDotDotToken, isDotDotDotToken), + visitBindingName(node.name), + visit(node.questionToken, isQuestionToken), + visitTypeNode(node.type), + visitExpression(node.initializer)); + + case SyntaxKind.Decorator: + Debug.type(node); + return factory.updateDecorator(node, + visitExpression(node.expression)); + + // Type members + case SyntaxKind.PropertySignature: + Debug.type(node); + return factory.updatePropertySignature(node, + visitList(node.modifiers, isModifier), + visitPropertyName(node.name), + visit(node.questionToken, isQuestionToken), + visitTypeNode(node.type)); + + case SyntaxKind.PropertyDeclaration: + Debug.type(node); + return factory.updatePropertyDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitPropertyName(node.name), + visit(node.questionToken || node.exclamationToken, isQuestionOrExclamationToken), + visitTypeNode(node.type), + visitExpression(node.initializer)); + + case SyntaxKind.MethodSignature: + Debug.type(node); + return factory.updateMethodSignature(node, + visitList(node.modifiers, isModifier), + visitPropertyName(node.name), + visit(node.questionToken, isQuestionToken), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitParameterList(node.parameters), + visitTypeNode(node.type)); + + case SyntaxKind.MethodDeclaration: + Debug.type(node); + return factory.updateMethodDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visit(node.asteriskToken, isAsteriskToken), + visitPropertyName(node.name), + visit(node.questionToken, isQuestionToken), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitParameterList(node.parameters), + visitTypeNode(node.type), + visitFunctionBody(node.body)); + + case SyntaxKind.Constructor: + Debug.type(node); + return factory.updateConstructorDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitParameterList(node.parameters), + visitFunctionBody(node.body)); + + case SyntaxKind.GetAccessor: + Debug.type(node); + return factory.updateGetAccessorDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitPropertyName(node.name), + visitParameterList(node.parameters), + visitTypeNode(node.type), + visitFunctionBody(node.body)); + + case SyntaxKind.SetAccessor: + Debug.type(node); + return factory.updateSetAccessorDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitPropertyName(node.name), + visitParameterList(node.parameters), + visitFunctionBody(node.body)); + + case SyntaxKind.CallSignature: + Debug.type(node); + return factory.updateCallSignature(node, + visitList(node.typeParameters, isTypeParameterDeclaration), + visitParameterList(node.parameters), + visitTypeNode(node.type)); + + case SyntaxKind.ConstructSignature: + Debug.type(node); + return factory.updateConstructSignature(node, + visitList(node.typeParameters, isTypeParameterDeclaration), + visitParameterList(node.parameters), + visitTypeNode(node.type)); + + case SyntaxKind.IndexSignature: + Debug.type(node); + return factory.updateIndexSignature(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitParameterList(node.parameters), + visitTypeNode(node.type)); + + // Types + case SyntaxKind.TypePredicate: + Debug.type(node); + return factory.updateTypePredicateNode(node, + visit(node.assertsModifier, isAssertsKeyword), + visit(node.parameterName, isIdentifierOrThisTypeNode), + visitTypeNode(node.type)); + + case SyntaxKind.TypeReference: + Debug.type(node); + return factory.updateTypeReferenceNode(node, + visitEntityName(node.typeName), + visitTypeNodeList(node.typeArguments)); + + case SyntaxKind.FunctionType: + Debug.type(node); + return factory.updateFunctionTypeNode(node, + visitList(node.typeParameters, isTypeParameterDeclaration), + visitNodes(node.parameters, pipelineVisitorForUnspecified, isParameterDeclaration), + visitTypeNode(node.type)); + + case SyntaxKind.ConstructorType: + Debug.type(node); + return factory.updateConstructorTypeNode(node, + visitNodes(node.modifiers, pipelineVisitorForUnspecified, isModifier), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitParameterList(node.parameters), + visitTypeNode(node.type)); + + case SyntaxKind.TypeQuery: + Debug.type(node); + return factory.updateTypeQueryNode(node, + visitEntityName(node.exprName)); + + case SyntaxKind.TypeLiteral: + Debug.type(node); + return factory.updateTypeLiteralNode(node, + visitList(node.members, isTypeElement)); + + case SyntaxKind.ArrayType: + Debug.type(node); + return factory.updateArrayTypeNode(node, + visitTypeNode(node.elementType)); + + case SyntaxKind.TupleType: + Debug.type(node); + return factory.updateTupleTypeNode(node, + visitTypeNodeList(node.elements)); + + case SyntaxKind.OptionalType: + Debug.type(node); + return factory.updateOptionalTypeNode(node, + visitTypeNode(node.type)); + + case SyntaxKind.RestType: + Debug.type(node); + return factory.updateRestTypeNode(node, + visitTypeNode(node.type)); + + case SyntaxKind.UnionType: + Debug.type(node); + return factory.updateUnionTypeNode(node, + visitTypeNodeList(node.types)); + + case SyntaxKind.IntersectionType: + Debug.type(node); + return factory.updateIntersectionTypeNode(node, + visitTypeNodeList(node.types)); + + case SyntaxKind.ConditionalType: + Debug.type(node); + return factory.updateConditionalTypeNode(node, + visitTypeNode(node.checkType), + visitTypeNode(node.extendsType), + visitTypeNode(node.trueType), + visitTypeNode(node.falseType)); + + case SyntaxKind.InferType: + Debug.type(node); + return factory.updateInferTypeNode(node, + visit(node.typeParameter, isTypeParameterDeclaration)); + + case SyntaxKind.ImportType: + Debug.type(node); + return factory.updateImportTypeNode(node, + visitTypeNode(node.argument), + visitEntityName(node.qualifier), + visitTypeNodeList(node.typeArguments), + node.isTypeOf + ); + + case SyntaxKind.NamedTupleMember: + Debug.type(node); + return factory.updateNamedTupleMember(node, + visit(node.dotDotDotToken, isDotDotDotToken), + visitIdentifierName(node.name), + visit(node.questionToken, isQuestionToken), + visitTypeNode(node.type), + ); + + case SyntaxKind.ParenthesizedType: + Debug.type(node); + return factory.updateParenthesizedType(node, + visitTypeNode(node.type)); + + case SyntaxKind.ExpressionWithTypeArguments: + Debug.type(node); + return factory.updateExpressionWithTypeArguments(node, + visitExpression(node.expression), + visitTypeNodeList(node.typeArguments)); + + case SyntaxKind.TypeOperator: + Debug.type(node); + return factory.updateTypeOperatorNode(node, + visitTypeNode(node.type)); + + case SyntaxKind.IndexedAccessType: + Debug.type(node); + return factory.updateIndexedAccessTypeNode(node, + visitTypeNode(node.objectType), + visitTypeNode(node.indexType)); + + case SyntaxKind.MappedType: + Debug.type(node); + return factory.updateMappedTypeNode(node, + visit(node.readonlyToken, isReadonlyKeywordOrPlusOrMinusToken), + visitMappedTypeParameter(node.typeParameter), + visitTypeNode(node.nameType), + visit(node.questionToken, isQuestionOrPlusOrMinusToken), + visitTypeNode(node.type)); + + case SyntaxKind.LiteralType: + Debug.type(node); + return factory.updateLiteralTypeNode(node, + visitExpression(node.literal, isLiteralTypeLikeExpression)); + + case SyntaxKind.TemplateLiteralType: + Debug.type(node); + return factory.updateTemplateLiteralType(node, + visit(node.head, isTemplateHead), + visitList(node.templateSpans, isTemplateLiteralTypeSpan)); + + case SyntaxKind.TemplateLiteralTypeSpan: + Debug.type(node); + return factory.updateTemplateLiteralTypeSpan(node, + visitTypeNode(node.type), + visit(node.literal, isTemplateMiddleOrTemplateTail)); + + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + Debug.type(node); + return factory.updateObjectBindingPattern(node, + visitList(node.elements, isBindingElement)); + + case SyntaxKind.ArrayBindingPattern: + Debug.type(node); + return factory.updateArrayBindingPattern(node, + visitList(node.elements, isArrayBindingElement)); + + case SyntaxKind.BindingElement: + Debug.type(node); + return factory.updateBindingElement(node, + visit(node.dotDotDotToken, isDotDotDotToken), + visitPropertyName(node.propertyName), + visitBindingName(node.name), + visitExpression(node.initializer)); + + // Misc + case SyntaxKind.TemplateSpan: + Debug.type(node); + return factory.updateTemplateSpan(node, + visitExpression(node.expression), + visit(node.literal, isTemplateMiddleOrTemplateTail)); + + // Element + case SyntaxKind.Block: + Debug.type(node); + return factory.updateBlock(node, + visitList(node.statements, isStatement)); + + case SyntaxKind.VariableStatement: + Debug.type(node); + return factory.updateVariableStatement(node, + visitList(node.modifiers, isModifier), + visit(node.declarationList, isVariableDeclarationList)); + + case SyntaxKind.ExpressionStatement: + Debug.type(node); + return factory.updateExpressionStatement(node, + visitExpression(node.expression)); + + case SyntaxKind.IfStatement: + Debug.type(node); + return factory.updateIfStatement(node, + visitExpression(node.expression), + visitEmbeddedStatement(node.thenStatement), + visitEmbeddedStatement(node.elseStatement)); + + case SyntaxKind.DoStatement: + Debug.type(node); + return factory.updateDoStatement(node, + visitEmbeddedStatement(node.statement), + visitExpression(node.expression)); + + case SyntaxKind.WhileStatement: + Debug.type(node); + return factory.updateWhileStatement(node, + visitExpression(node.expression), + visitEmbeddedStatement(node.statement)); + + case SyntaxKind.ForStatement: + Debug.type(node); + return factory.updateForStatement(node, + visitForInitializer(node.initializer), + visitExpression(node.condition), + visitExpression(node.incrementor), + visitEmbeddedStatement(node.statement)); + + case SyntaxKind.ForInStatement: + Debug.type(node); + return factory.updateForInStatement(node, + visitForInitializer(node.initializer), + visitExpression(node.expression), + visitEmbeddedStatement(node.statement)); + + case SyntaxKind.ForOfStatement: + Debug.type(node); + return factory.updateForOfStatement(node, + visit(node.awaitModifier, isAwaitKeyword), + visitForInitializer(node.initializer), + visitExpression(node.expression), + visitEmbeddedStatement(node.statement)); + + case SyntaxKind.ContinueStatement: + Debug.type(node); + return factory.updateContinueStatement(node, + visitIdentifierName(node.label)); + + case SyntaxKind.BreakStatement: + Debug.type(node); + return factory.updateBreakStatement(node, + visitIdentifierName(node.label)); + + case SyntaxKind.ReturnStatement: + Debug.type(node); + return factory.updateReturnStatement(node, + visitExpression(node.expression)); + + case SyntaxKind.WithStatement: + Debug.type(node); + return factory.updateWithStatement(node, + visitExpression(node.expression), + visitEmbeddedStatement(node.statement)); + + case SyntaxKind.SwitchStatement: + Debug.type(node); + return factory.updateSwitchStatement(node, + visitExpression(node.expression), + visit(node.caseBlock, isCaseBlock)); + + case SyntaxKind.LabeledStatement: + Debug.type(node); + return factory.updateLabeledStatement(node, + visitIdentifierName(node.label), + visitEmbeddedStatement(node.statement)); + + case SyntaxKind.ThrowStatement: + Debug.type(node); + return factory.updateThrowStatement(node, + visitExpression(node.expression)); + + case SyntaxKind.TryStatement: + Debug.type(node); + return factory.updateTryStatement(node, + visit(node.tryBlock, isBlock), + visit(node.catchClause, isCatchClause), + visit(node.finallyBlock, isBlock)); + + // Declarations + case SyntaxKind.VariableDeclaration: + Debug.type(node); + return factory.updateVariableDeclaration(node, + visitBindingName(node.name), + visit(node.exclamationToken, isExclamationToken), + visitTypeNode(node.type), + visitExpression(node.initializer)); + + case SyntaxKind.VariableDeclarationList: + Debug.type(node); + return factory.updateVariableDeclarationList(node, + visitList(node.declarations, isVariableDeclaration)); + + case SyntaxKind.FunctionDeclaration: + Debug.type(node); + return factory.updateFunctionDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visit(node.asteriskToken, isAsteriskToken), + visitIdentifierName(node.name), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitParameterList(node.parameters), + visitTypeNode(node.type), + visitFunctionBody(node.body)); + + case SyntaxKind.ClassDeclaration: + Debug.type(node); + return factory.updateClassDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitIdentifierName(node.name), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitList(node.heritageClauses, isHeritageClause), + visitList(node.members, isClassElement)); + + case SyntaxKind.InterfaceDeclaration: + Debug.type(node); + return factory.updateInterfaceDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitIdentifierName(node.name), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitList(node.heritageClauses, isHeritageClause), + visitList(node.members, isTypeElement)); + + case SyntaxKind.TypeAliasDeclaration: + Debug.type(node); + return factory.updateTypeAliasDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitIdentifierName(node.name), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitTypeNode(node.type)); + + case SyntaxKind.EnumDeclaration: + Debug.type(node); + return factory.updateEnumDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitIdentifierName(node.name), + visitList(node.members, isEnumMember)); + + case SyntaxKind.ModuleDeclaration: + Debug.type(node); + return factory.updateModuleDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitModuleName(node.name), + visit(node.body, isModuleBody)); + + case SyntaxKind.ModuleBlock: + Debug.type(node); + return factory.updateModuleBlock(node, + visitList(node.statements, isStatement)); + + case SyntaxKind.CaseBlock: + Debug.type(node); + return factory.updateCaseBlock(node, + visitList(node.clauses, isCaseOrDefaultClause)); + + case SyntaxKind.NamespaceExportDeclaration: + Debug.type(node); + return factory.updateNamespaceExportDeclaration(node, + visitIdentifierName(node.name)); + + case SyntaxKind.ImportEqualsDeclaration: + Debug.type(node); + return factory.updateImportEqualsDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + node.isTypeOnly, + visitIdentifierName(node.name), + visit(node.moduleReference, isModuleReference)); + + case SyntaxKind.ImportDeclaration: + Debug.type(node); + return factory.updateImportDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visit(node.importClause, isImportClause), + visitExpression(node.moduleSpecifier)); + + case SyntaxKind.ImportClause: + Debug.type(node); + return factory.updateImportClause(node, + node.isTypeOnly, + visitIdentifierName(node.name), + visit(node.namedBindings, isNamedImportBindings)); + + case SyntaxKind.NamespaceImport: + Debug.type(node); + return factory.updateNamespaceImport(node, + visitIdentifierName(node.name)); + + case SyntaxKind.NamespaceExport: + Debug.type(node); + return factory.updateNamespaceExport(node, + visitIdentifierName(node.name)); - /** - * Gets the node from which a name should be generated. - */ - function getNodeForGeneratedName(name: GeneratedIdentifier) { - const autoGenerateId = name.autoGenerateId; - let node = name as Node; - let original = node.original; - while (original) { - node = original; + case SyntaxKind.NamedImports: + Debug.type(node); + return factory.updateNamedImports(node, + visitList(node.elements, isImportSpecifier)); - // if "node" is a different generated name (having a different - // "autoGenerateId"), use it and stop traversing. - if (isIdentifier(node) - && !!(node.autoGenerateFlags! & GeneratedIdentifierFlags.Node) - && node.autoGenerateId !== autoGenerateId) { - break; - } + case SyntaxKind.ImportSpecifier: + Debug.type(node); + return factory.updateImportSpecifier(node, + visitIdentifierName(node.propertyName), + visitIdentifierName(node.name)); - original = node.original; - } + case SyntaxKind.ExportAssignment: + Debug.type(node); + return factory.updateExportAssignment(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitExpression(node.expression)); - // otherwise, return the original node for the source; - return node; - } + case SyntaxKind.ExportDeclaration: + Debug.type(node); + return factory.updateExportDeclaration(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + node.isTypeOnly, + visit(node.exportClause, isNamedExportBindings), + visitExpression(node.moduleSpecifier)); - // Comments + case SyntaxKind.NamedExports: + Debug.type(node); + return factory.updateNamedExports(node, + visitList(node.elements, isExportSpecifier)); - function pipelineEmitWithComments(hint: EmitHint, node: Node) { - Debug.assert(lastNode === node || lastSubstitution === node); - enterComment(); - hasWrittenComment = false; - const emitFlags = getEmitFlags(node); - const { pos, end } = getCommentRange(node); - const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement; + case SyntaxKind.ExportSpecifier: + Debug.type(node); + return factory.updateExportSpecifier(node, + visitIdentifierName(node.propertyName), + visitIdentifierName(node.name)); - // We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation. - // It is expensive to walk entire tree just to set one kind of node to have no comments. - const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0 || node.kind === SyntaxKind.JsxText; - const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0 || node.kind === SyntaxKind.JsxText; + case SyntaxKind.MissingDeclaration: + return node; - // Save current container state on the stack. - const savedContainerPos = containerPos; - const savedContainerEnd = containerEnd; - const savedDeclarationListContainerEnd = declarationListContainerEnd; - if ((pos > 0 || end > 0) && pos !== end) { - // Emit leading comments if the position is not synthesized and the node - // has not opted out from emitting leading comments. - if (!skipLeadingComments) { - emitLeadingComments(pos, isEmittedNode); - } + // Module references + case SyntaxKind.ExternalModuleReference: + Debug.type(node); + return factory.updateExternalModuleReference(node, + visitExpression(node.expression)); - if (!skipLeadingComments || (pos >= 0 && (emitFlags & EmitFlags.NoLeadingComments) !== 0)) { - // Advance the container position if comments get emitted or if they've been disabled explicitly using NoLeadingComments. - containerPos = pos; - } + // JSX (non-expression) + case SyntaxKind.JsxOpeningElement: + Debug.type(node); + return factory.updateJsxOpeningElement(node, + visitExpression(node.tagName, isJsxTagNameExpression), + visitList(node.typeArguments, isTypeNode), + visit(node.attributes, isJsxAttributes)); - if (!skipTrailingComments || (end >= 0 && (emitFlags & EmitFlags.NoTrailingComments) !== 0)) { - // As above. - containerEnd = end; + case SyntaxKind.JsxClosingElement: + Debug.type(node); + return factory.updateJsxClosingElement(node, + visitExpression(node.tagName, isJsxTagNameExpression)); - // To avoid invalid comment emit in a down-level binding pattern, we - // keep track of the last declaration list container's end - if (node.kind === SyntaxKind.VariableDeclarationList) { - declarationListContainerEnd = end; - } - } - } - forEach(getSyntheticLeadingComments(node), emitLeadingSynthesizedComment); - exitComment(); + case SyntaxKind.JsxAttribute: + Debug.type(node); + return factory.updateJsxAttribute(node, + visitIdentifierName(node.name), + visitJsxAttributeValue(node.initializer)); - const pipelinePhase = getNextPipelinePhase(PipelinePhase.Comments, hint, node); - if (emitFlags & EmitFlags.NoNestedComments) { - commentsDisabled = true; - pipelinePhase(hint, node); - commentsDisabled = false; - } - else { - pipelinePhase(hint, node); - } + case SyntaxKind.JsxAttributes: + Debug.type(node); + return factory.updateJsxAttributes(node, + visitList(node.properties, isJsxAttributeLike)); - enterComment(); - forEach(getSyntheticTrailingComments(node), emitTrailingSynthesizedComment); - if ((pos > 0 || end > 0) && pos !== end) { - // Restore previous container state. - containerPos = savedContainerPos; - containerEnd = savedContainerEnd; - declarationListContainerEnd = savedDeclarationListContainerEnd; + case SyntaxKind.JsxSpreadAttribute: + Debug.type(node); + return factory.updateJsxSpreadAttribute(node, + visitExpression(node.expression)); - // Emit trailing comments if the position is not synthesized and the node - // has not opted out from emitting leading comments and is an emitted node. - if (!skipTrailingComments && isEmittedNode) { - emitTrailingComments(end); - } - } - exitComment(); - Debug.assert(lastNode === node || lastSubstitution === node); - } + case SyntaxKind.JsxExpression: + Debug.type(node); + return factory.updateJsxExpression(node, + visitExpression(node.expression)); - function emitLeadingSynthesizedComment(comment: SynthesizedComment) { - if (comment.hasLeadingNewline || comment.kind === SyntaxKind.SingleLineCommentTrivia) { - writer.writeLine(); - } - writeSynthesizedComment(comment); - if (comment.hasTrailingNewLine || comment.kind === SyntaxKind.SingleLineCommentTrivia) { - writer.writeLine(); - } - else { - writer.writeSpace(" "); - } - } + // Clauses + case SyntaxKind.CaseClause: + Debug.type(node); + return factory.updateCaseClause(node, + visitExpression(node.expression), + visitList(node.statements, isStatement)); - function emitTrailingSynthesizedComment(comment: SynthesizedComment) { - if (!writer.isAtStartOfLine()) { - writer.writeSpace(" "); - } - writeSynthesizedComment(comment); - if (comment.hasTrailingNewLine) { - writer.writeLine(); - } - } + case SyntaxKind.DefaultClause: + Debug.type(node); + return factory.updateDefaultClause(node, + visitList(node.statements, isStatement)); - function writeSynthesizedComment(comment: SynthesizedComment) { - const text = formatSynthesizedComment(comment); - const lineMap = comment.kind === SyntaxKind.MultiLineCommentTrivia ? computeLineStarts(text) : undefined; - writeCommentRange(text, lineMap!, writer, 0, text.length, newLine); - } + case SyntaxKind.HeritageClause: + Debug.type(node); + return factory.updateHeritageClause(node, + visitList(node.types, isExpressionWithTypeArguments)); - function formatSynthesizedComment(comment: SynthesizedComment) { - return comment.kind === SyntaxKind.MultiLineCommentTrivia - ? `/*${comment.text}*/` - : `//${comment.text}`; - } + case SyntaxKind.CatchClause: + Debug.type(node); + return factory.updateCatchClause(node, + visit(node.variableDeclaration, isVariableDeclaration), + visit(node.block, isBlock)); - function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) { - enterComment(); - const { pos, end } = detachedRange; - const emitFlags = getEmitFlags(node); - const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0; - const skipTrailingComments = commentsDisabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0; - if (!skipLeadingComments) { - emitDetachedCommentsAndUpdateCommentsInfo(detachedRange); - } + // Property assignments + case SyntaxKind.PropertyAssignment: + Debug.type(node); + return factory.updatePropertyAssignment(node, + visitPropertyName(node.name), + visitExpression(node.initializer)); - exitComment(); - if (emitFlags & EmitFlags.NoNestedComments && !commentsDisabled) { - commentsDisabled = true; - emitCallback(node); - commentsDisabled = false; - } - else { - emitCallback(node); - } + case SyntaxKind.ShorthandPropertyAssignment: + Debug.type(node); + return factory.updateShorthandPropertyAssignment(node, + visitIdentifierName(node.name, isIdentifier), + visitExpression(node.objectAssignmentInitializer)); - enterComment(); - if (!skipTrailingComments) { - emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true); - if (hasWrittenComment && !writer.isAtStartOfLine()) { - writer.writeLine(); - } - } - exitComment(); + case SyntaxKind.SpreadAssignment: + Debug.type(node); + return factory.updateSpreadAssignment(node, + visitExpression(node.expression)); - } + // Enum + case SyntaxKind.EnumMember: + Debug.type(node); + return factory.updateEnumMember(node, + visitPropertyName(node.name), + visitExpression(node.initializer)); - function emitLeadingComments(pos: number, isEmittedNode: boolean) { - hasWrittenComment = false; + // JSDoc nodes (only used in codefixes currently) + case SyntaxKind.JSDocTypeExpression: + case SyntaxKind.JSDocNameReference: + case SyntaxKind.JSDocAllType: + case SyntaxKind.JSDocUnknownType: + case SyntaxKind.JSDocNullableType: + case SyntaxKind.JSDocNonNullableType: + case SyntaxKind.JSDocOptionalType: + case SyntaxKind.JSDocFunctionType: + case SyntaxKind.JSDocVariadicType: + case SyntaxKind.JSDocNamepathType: + case SyntaxKind.JSDocComment: + case SyntaxKind.JSDocTypeLiteral: + case SyntaxKind.JSDocSignature: + case SyntaxKind.JSDocTag: + case SyntaxKind.JSDocAugmentsTag: + case SyntaxKind.JSDocImplementsTag: + case SyntaxKind.JSDocAuthorTag: + case SyntaxKind.JSDocDeprecatedTag: + case SyntaxKind.JSDocClassTag: + case SyntaxKind.JSDocPublicTag: + case SyntaxKind.JSDocPrivateTag: + case SyntaxKind.JSDocProtectedTag: + case SyntaxKind.JSDocReadonlyTag: + case SyntaxKind.JSDocCallbackTag: + case SyntaxKind.JSDocEnumTag: + case SyntaxKind.JSDocParameterTag: + case SyntaxKind.JSDocPropertyTag: + case SyntaxKind.JSDocReturnTag: + case SyntaxKind.JSDocThisTag: + case SyntaxKind.JSDocTypeTag: + case SyntaxKind.JSDocTemplateTag: + case SyntaxKind.JSDocTypedefTag: + case SyntaxKind.JSDocSeeTag: + return node; - if (isEmittedNode) { - if (pos === 0 && currentSourceFile?.isDeclarationFile) { - forEachLeadingCommentToEmit(pos, emitNonTripleSlashLeadingComment); + // Transformation nodes (ignored) } - else { - forEachLeadingCommentToEmit(pos, emitLeadingComment); + + if (isExpression(node)) { + // If this was an expression that was originally in an `Unspecified` hint, + // re-trigger substitution using the correct hint. + hint = EmitHint.Expression; + if (substituteNode !== noEmitSubstitution) { + node = substituteNode(hint, node); + } + } + else if (isSourceFile(node)) { + return preprintSourceFile(node); } } - else if (pos === 0) { - // If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node, - // unless it is a triple slash comment at the top of the file. - // For Example: - // /// - // declare var x; - // /// - // interface F {} - // The first /// will NOT be removed while the second one will be removed even though both node will not be emitted - forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment); - } - } - function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) { - if (isTripleSlashComment(commentPos, commentEnd)) { - emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); - } - } + if (hint === EmitHint.Expression) { + switch (node.kind) { + // Identifiers + case SyntaxKind.Identifier: + return preprintIdentifier(node as Identifier); - function emitNonTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) { - if (!isTripleSlashComment(commentPos, commentEnd)) { - emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); - } - } + // Expression + case SyntaxKind.ArrayLiteralExpression: + Debug.type(node); + return factory.updateArrayLiteralExpression(node, + visitExpressionList(node.elements)); - function shouldWriteComment(text: string, pos: number) { - if (printerOptions.onlyPrintJsDocStyle) { - return (isJSDocLikeText(text, pos) || isPinnedComment(text, pos)); - } - return true; - } + case SyntaxKind.ObjectLiteralExpression: + Debug.type(node); + return factory.updateObjectLiteralExpression(node, + visitList(node.properties, isObjectLiteralElementLike)); - function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) { - if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; - if (!hasWrittenComment) { - emitNewLineBeforeLeadingCommentOfPosition(getCurrentLineMap(), writer, rangePos, commentPos); - hasWrittenComment = true; - } + case SyntaxKind.PropertyAccessExpression: + if (node.flags & NodeFlags.OptionalChain) { + Debug.type(node); + return factory.updatePropertyAccessChain(node, + visitExpression(node.expression), + visit(node.questionDotToken, isQuestionDotToken), + visitMemberName(node.name)); + } + Debug.type(node); + return factory.updatePropertyAccessExpression(node, + visitExpression(node.expression), + visitMemberName(node.name)); - // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitPos(commentPos); - writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); + case SyntaxKind.ElementAccessExpression: + if (node.flags & NodeFlags.OptionalChain) { + Debug.type(node); + return factory.updateElementAccessChain(node, + visitExpression(node.expression), + visit(node.questionDotToken, isQuestionDotToken), + visitExpression(node.argumentExpression)); + } + Debug.type(node); + return factory.updateElementAccessExpression(node, + visitExpression(node.expression), + visitExpression(node.argumentExpression)); + + case SyntaxKind.CallExpression: + if (node.flags & NodeFlags.OptionalChain) { + Debug.type(node); + return factory.updateCallChain(node, + visitExpression(node.expression), + visit(node.questionDotToken, isQuestionDotToken), + visitTypeNodeList(node.typeArguments), + visitExpressionList(node.arguments)); + } + Debug.type(node); + return factory.updateCallExpression(node, + visitExpression(node.expression), + visitTypeNodeList(node.typeArguments), + visitExpressionList(node.arguments)); + + case SyntaxKind.NewExpression: + Debug.type(node); + return factory.updateNewExpression(node, + visitExpression(node.expression), + visitTypeNodeList(node.typeArguments), + visitExpressionList(node.arguments)); + + case SyntaxKind.TaggedTemplateExpression: + Debug.type(node); + return factory.updateTaggedTemplateExpression(node, + visitExpression(node.tag), + visitTypeNodeList(node.typeArguments), + visitExpression(node.template, isTemplateLiteral)); - if (hasTrailingNewLine) { - writer.writeLine(); - } - else if (kind === SyntaxKind.MultiLineCommentTrivia) { - writer.writeSpace(" "); - } - } + case SyntaxKind.TypeAssertionExpression: + Debug.type(node); + return factory.updateTypeAssertion(node, + visitTypeNode(node.type), + visitExpression(node.expression)); - function emitLeadingCommentsOfPosition(pos: number) { - if (commentsDisabled || pos === -1) { - return; - } + case SyntaxKind.ParenthesizedExpression: + Debug.type(node); + return factory.updateParenthesizedExpression(node, + visitExpression(node.expression)); - emitLeadingComments(pos, /*isEmittedNode*/ true); - } + case SyntaxKind.FunctionExpression: + Debug.type(node); + return factory.updateFunctionExpression(node, + visitList(node.modifiers, isModifier), + visit(node.asteriskToken, isAsteriskToken), + visitIdentifierName(node.name), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitParameterList(node.parameters), + visitTypeNode(node.type), + visitFunctionBody(node.body)); - function emitTrailingComments(pos: number) { - forEachTrailingCommentToEmit(pos, emitTrailingComment); - } + case SyntaxKind.ArrowFunction: + Debug.type(node); + return factory.updateArrowFunction(node, + visitList(node.modifiers, isModifier), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitParameterList(node.parameters), + visitTypeNode(node.type), + visit(node.equalsGreaterThanToken, isEqualsGreaterThanToken), + visitConciseBody(node.body)); - function emitTrailingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) { - if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; - // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/ - if (!writer.isAtStartOfLine()) { - writer.writeSpace(" "); - } + case SyntaxKind.DeleteExpression: + Debug.type(node); + return factory.updateDeleteExpression(node, + visitExpression(node.expression)); - emitPos(commentPos); - writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); + case SyntaxKind.TypeOfExpression: + Debug.type(node); + return factory.updateTypeOfExpression(node, + visitExpression(node.expression)); - if (hasTrailingNewLine) { - writer.writeLine(); - } - } + case SyntaxKind.VoidExpression: + Debug.type(node); + return factory.updateVoidExpression(node, + visitExpression(node.expression)); - function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean, forceNoNewline?: boolean) { - if (commentsDisabled) { - return; - } - enterComment(); - forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : forceNoNewline ? emitTrailingCommentOfPositionNoNewline : emitTrailingCommentOfPosition); - exitComment(); - } + case SyntaxKind.AwaitExpression: + Debug.type(node); + return factory.updateAwaitExpression(node, + visitExpression(node.expression)); - function emitTrailingCommentOfPositionNoNewline(commentPos: number, commentEnd: number, kind: SyntaxKind) { - // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space + case SyntaxKind.PrefixUnaryExpression: + Debug.type(node); + return factory.updatePrefixUnaryExpression(node, + visitExpression(node.operand)); - emitPos(commentPos); - writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); + case SyntaxKind.PostfixUnaryExpression: + Debug.type(node); + return factory.updatePostfixUnaryExpression(node, + visitExpression(node.operand)); - if (kind === SyntaxKind.SingleLineCommentTrivia) { - writer.writeLine(); // still write a newline for single-line comments, so closing tokens aren't written on the same line - } - } + case SyntaxKind.BinaryExpression: + return preprintBinaryExpression(node as BinaryExpression); - function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) { - // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space + case SyntaxKind.ConditionalExpression: + Debug.type(node); + return factory.updateConditionalExpression(node, + visitExpression(node.condition), + visit(node.questionToken, isQuestionToken), + visitExpression(node.whenTrue), + visit(node.colonToken, isColonToken), + visitExpression(node.whenFalse)); - emitPos(commentPos); - writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); + case SyntaxKind.TemplateExpression: + Debug.type(node); + return factory.updateTemplateExpression(node, + visit(node.head, isTemplateHead), + visitList(node.templateSpans, isTemplateSpan)); - if (hasTrailingNewLine) { - writer.writeLine(); - } - else { - writer.writeSpace(" "); - } - } + case SyntaxKind.YieldExpression: + Debug.type(node); + return factory.updateYieldExpression(node, + visit(node.asteriskToken, isAsteriskToken), + visitExpression(node.expression)); - function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) { - // Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments - if (currentSourceFile && (containerPos === -1 || pos !== containerPos)) { - if (hasDetachedComments(pos)) { - forEachLeadingCommentWithoutDetachedComments(cb); - } - else { - forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos); - } - } - } + case SyntaxKind.SpreadElement: + Debug.type(node); + return factory.updateSpreadElement(node, + visitExpression(node.expression)); - function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { - // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments - if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { - forEachTrailingCommentRange(currentSourceFile.text, end, cb); - } - } + case SyntaxKind.ClassExpression: + Debug.type(node); + return factory.updateClassExpression(node, + visitList(node.decorators, isDecorator), + visitList(node.modifiers, isModifier), + visitIdentifierName(node.name), + visitList(node.typeParameters, isTypeParameterDeclaration), + visitList(node.heritageClauses, isHeritageClause), + visitList(node.members, isClassElement)); - function hasDetachedComments(pos: number) { - return detachedCommentsInfo !== undefined && last(detachedCommentsInfo).nodePos === pos; - } + case SyntaxKind.AsExpression: + Debug.type(node); + return factory.updateAsExpression(node, + visitExpression(node.expression), + visitTypeNode(node.type)); - function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) { - // get the leading comments from detachedPos - const pos = last(detachedCommentsInfo!).detachedCommentEndPos; - if (detachedCommentsInfo!.length - 1) { - detachedCommentsInfo!.pop(); - } - else { - detachedCommentsInfo = undefined; - } + case SyntaxKind.NonNullExpression: + if (node.flags & NodeFlags.OptionalChain) { + Debug.type(node); + return factory.updateNonNullChain(node, + visitExpression(node.expression)); + } + Debug.type(node); + return factory.updateNonNullExpression(node, + visitExpression(node.expression)); - forEachLeadingCommentRange(currentSourceFile!.text, pos, cb, /*state*/ pos); - } + case SyntaxKind.MetaProperty: + Debug.type(node); + return factory.updateMetaProperty(node, + visitIdentifierName(node.name)); - function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) { - const currentDetachedCommentInfo = emitDetachedComments(currentSourceFile!.text, getCurrentLineMap(), writer, emitComment, range, newLine, commentsDisabled); - if (currentDetachedCommentInfo) { - if (detachedCommentsInfo) { - detachedCommentsInfo.push(currentDetachedCommentInfo); - } - else { - detachedCommentsInfo = [currentDetachedCommentInfo]; - } - } - } - function emitComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) { - if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; - emitPos(commentPos); - writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); - } + // JSX (expression only) + case SyntaxKind.JsxElement: + Debug.type(node); + return factory.updateJsxElement(node, + visit(node.openingElement, isJsxOpeningElement), + visitList(node.children, isJsxChild), + visit(node.closingElement, isJsxClosingElement)); - /** - * Determine if the given comment is a triple-slash - * - * @return true if the comment is a triple-slash comment else false - */ - function isTripleSlashComment(commentPos: number, commentEnd: number) { - return isRecognizedTripleSlashComment(currentSourceFile!.text, commentPos, commentEnd); - } + case SyntaxKind.JsxSelfClosingElement: + Debug.type(node); + return factory.updateJsxSelfClosingElement(node, + visitExpression(node.tagName, isJsxTagNameExpression), + visitList(node.typeArguments, isTypeNode), + visit(node.attributes, isJsxAttributes)); - // Source Maps + case SyntaxKind.JsxFragment: + Debug.type(node); + return factory.updateJsxFragment(node, + visit(node.openingFragment, isJsxOpeningFragment), + visitList(node.children, isJsxChild), + visit(node.closingFragment, isJsxClosingFragment)); - function getParsedSourceMap(node: UnparsedSource) { - if (node.parsedSourceMap === undefined && node.sourceMapText !== undefined) { - node.parsedSourceMap = tryParseRawSourceMap(node.sourceMapText) || false; - } - return node.parsedSourceMap || undefined; - } + // Transformation nodes + case SyntaxKind.PartiallyEmittedExpression: + Debug.type(node); + return factory.updatePartiallyEmittedExpression(node, + visitExpression(node.expression)); - function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) { - Debug.assert(lastNode === node || lastSubstitution === node); - const pipelinePhase = getNextPipelinePhase(PipelinePhase.SourceMaps, hint, node); - if (isUnparsedSource(node) || isUnparsedPrepend(node)) { - pipelinePhase(hint, node); - } - else if (isUnparsedNode(node)) { - const parsed = getParsedSourceMap(node.parent); - if (parsed && sourceMapGenerator) { - sourceMapGenerator.appendSourceMap( - writer.getLine(), - writer.getColumn(), - parsed, - node.parent.sourceMapPath!, - node.parent.getLineAndCharacterOfPosition(node.pos), - node.parent.getLineAndCharacterOfPosition(node.end) - ); + case SyntaxKind.CommaListExpression: + Debug.type(node); + return factory.updateCommaListExpression(node, + visitExpressionList(node.elements, isExpression)); } - pipelinePhase(hint, node); } - else { - const { pos, end, source = sourceMapSource } = getSourceMapRange(node); - const emitFlags = getEmitFlags(node); - if (node.kind !== SyntaxKind.NotEmittedStatement - && (emitFlags & EmitFlags.NoLeadingSourceMap) === 0 - && pos >= 0) { - emitSourcePos(source, skipSourceTrivia(source, pos)); - } - - if (emitFlags & EmitFlags.NoNestedSourceMaps) { - sourceMapsDisabled = true; - pipelinePhase(hint, node); - sourceMapsDisabled = false; - } - else { - pipelinePhase(hint, node); - } - if (node.kind !== SyntaxKind.NotEmittedStatement - && (emitFlags & EmitFlags.NoTrailingSourceMap) === 0 - && end >= 0) { - emitSourcePos(source, end); - } + if (Debug.shouldAssert(AssertionLevel.Normal)) { + // Any other node should not have children or this list isn't up to date. + Debug.assertMissingNode(forEachChild(node, identity), `Expected ${Debug.formatSyntaxKind(node.kind)} to contain no children.`); } - Debug.assert(lastNode === node || lastSubstitution === node); - } - /** - * Skips trivia such as comments and white-space that can be optionally overridden by the source-map source - */ - function skipSourceTrivia(source: SourceMapSource, pos: number): number { - return source.skipTrivia ? source.skipTrivia(pos) : skipTrivia(source.text, pos); + // No need to visit nodes with no children. + return node; } - /** - * Emits a mapping. - * - * If the position is synthetic (undefined or a negative value), no mapping will be - * created. - * - * @param pos The position. - */ - function emitPos(pos: number) { - if (sourceMapsDisabled || positionIsSynthesized(pos) || isJsonSourceMapSource(sourceMapSource)) { - return; - } + function preprintSourceFile(node: SourceFile) { + return factory.updateSourceFile(node, + visitList(node.statements, isStatement)); + } - const { line: sourceLine, character: sourceCharacter } = getLineAndCharacterOfPosition(sourceMapSource, pos); - sourceMapGenerator!.addMapping( - writer.getLine(), - writer.getColumn(), - sourceMapSourceIndex, - sourceLine, - sourceCharacter, - /*nameIndex*/ undefined); + function preprintIdentifier(node: Identifier) { + return factory.updateIdentifier(node, + visitList(node.typeArguments, isTypeNodeOrTypeParameterDeclaration)); } - function emitSourcePos(source: SourceMapSource, pos: number) { - if (source !== sourceMapSource) { - const savedSourceMapSource = sourceMapSource; - const savedSourceMapSourceIndex = sourceMapSourceIndex; - setSourceMapSource(source); - emitPos(pos); - resetSourceMapSource(savedSourceMapSource, savedSourceMapSourceIndex); - } - else { - emitPos(pos); - } + function preprintTypeParameterDeclaration(node: TypeParameterDeclaration) { + return factory.updateTypeParameterDeclaration(node, + visitIdentifierName(node.name), + visitTypeNode(node.constraint), + visitTypeNode(node.default)); } - /** - * Emits a token of a node with possible leading and trailing source maps. - * - * @param node The node containing the token. - * @param token The token to emit. - * @param tokenStartPos The start pos of the token. - * @param emitCallback The callback used to emit the token. - */ - function emitTokenWithSourceMap(node: Node | undefined, token: SyntaxKind, writer: (s: string) => void, tokenPos: number, emitCallback: (token: SyntaxKind, writer: (s: string) => void, tokenStartPos: number) => number) { - if (sourceMapsDisabled || node && isInJsonFile(node)) { - return emitCallback(token, writer, tokenPos); + function createPreprintBinaryExpression() { + interface WorkArea { + stackIndex: number; + leftStack: Expression[]; + operatorStack: BinaryOperatorToken[]; + rightStack: Expression[]; } - const emitNode = node && node.emitNode; - const emitFlags = emitNode && emitNode.flags || EmitFlags.None; - const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token]; - const source = range && range.source || sourceMapSource; + return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, foldState); - tokenPos = skipSourceTrivia(source, range ? range.pos : tokenPos); - if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) { - emitSourcePos(source, tokenPos); + function onEnter(node: BinaryExpression, state: WorkArea | undefined) { + if (state) { + state.stackIndex++; + state.leftStack[state.stackIndex] = node.left; + state.operatorStack[state.stackIndex] = node.operatorToken; + state.rightStack[state.stackIndex] = node.right; + } + else { + state = { + stackIndex: 0, + leftStack: [node.left], + operatorStack: [node.operatorToken], + rightStack: [node.right], + }; + } + return state; } - tokenPos = emitCallback(token, writer, tokenPos); - - if (range) tokenPos = range.end; - if ((emitFlags & EmitFlags.NoTokenTrailingSourceMaps) === 0 && tokenPos >= 0) { - emitSourcePos(source, tokenPos); + function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) { + return maybeVisitExpression(left, state, "left"); } - return tokenPos; - } - - function setSourceMapSource(source: SourceMapSource) { - if (sourceMapsDisabled) { - return; + function onOperator(operator: BinaryOperatorToken, state: WorkArea, _node: BinaryExpression) { + state.operatorStack[state.stackIndex] = visit(operator, isBinaryOperatorToken); } - sourceMapSource = source; - - if (source === mostRecentlyAddedSourceMapSource) { - // Fast path for when the new source map is the most recently added, in which case - // we use its captured index without going through the source map generator. - sourceMapSourceIndex = mostRecentlyAddedSourceMapSourceIndex; - return; + function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) { + return maybeVisitExpression(right, state, "right"); } - if (isJsonSourceMapSource(source)) { - return; + function onExit(node: BinaryExpression, state: WorkArea) { + const left = state.leftStack[state.stackIndex]; + const operator = state.operatorStack[state.stackIndex]; + const right = state.rightStack[state.stackIndex]; + if (state.stackIndex > 0) { + state.stackIndex--; + } + return factory.updateBinaryExpression(node, left, operator, right); } - sourceMapSourceIndex = sourceMapGenerator!.addSource(source.fileName); - if (printerOptions.inlineSources) { - sourceMapGenerator!.setSourceContent(sourceMapSourceIndex, source.text); + function foldState(state: WorkArea, result: BinaryExpression, side: "left" | "right") { + (side === "left" ? state.leftStack : state.rightStack)[state.stackIndex] = result; + return state; } - mostRecentlyAddedSourceMapSource = source; - mostRecentlyAddedSourceMapSourceIndex = sourceMapSourceIndex; + function maybeVisitExpression(node: Expression, state: WorkArea, side: "left" | "right") { + // Get the first supported pipeline phase for this node. We can skip several stack + // frames if we aren't doing emit notification, so we check for substitution and + // direct callbacks and execute those immediately. + let pipelinePhase = getPipelinePhase(PreprintPipelinePhase.Notification, node); + if (pipelinePhase === pipelineVisitWithSubstitution) { + // The next phase after substitution is always direct visitation, so we can reduce the call stack + // depth by proceding to the direct visitor. + node = cast(substituteNode(EmitHint.Expression, node), isExpression); + pipelinePhase = pipelineVisitDirect; + } + if (pipelinePhase === pipelineVisitDirect && isBinaryExpression(node)) { + // If we are visiting directly and the next node is a BinaryExpression, we can + // add it to the stack and continue the trampoline. + return node; + } + else { + // Visit the expression and store the result on whichever side we are currently visiting. + (side === "left" ? state.leftStack : state.rightStack)[state.stackIndex] = visitExpression(node, isExpression); + } + } } - function resetSourceMapSource(source: SourceMapSource, sourceIndex: number) { - sourceMapSource = source; - sourceMapSourceIndex = sourceIndex; - } + const preprintBinaryExpression = createPreprintBinaryExpression(); - function isJsonSourceMapSource(sourceFile: SourceMapSource) { - return fileExtensionIs(sourceFile.fileName, Extension.Json); + function preprint(hint: EmitHint, node: Node) { + // If we're not performing substitution or notification, we have no work to do here. + if (substituteNode === noEmitSubstitution && + onEmitNode === noEmitNotification) { + return node; + } + switch (hint) { + case EmitHint.SourceFile: return visitSourceFile(cast(node, isSourceFile)); + case EmitHint.Expression: return visitExpression(cast(node, isExpression)); + case EmitHint.IdentifierName: return visitIdentifierName(cast(node, isIdentifier)); + case EmitHint.MappedTypeParameter: return visitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); + case EmitHint.EmbeddedStatement: return visitEmbeddedStatement(cast(node, isStatement)); + case EmitHint.JsxAttributeValue: return visitJsxAttributeValue(cast(node, isStringLiteralOrJsxExpression)); + default: return visit(node); + } } + + return preprint; } function createBracketsMap() { diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 71b329e46d65c..0fbb50d9d1165 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -39,51 +39,114 @@ namespace ts { return node.kind === SyntaxKind.TemplateTail; } + // Punctuation + + export function isDotDotDotToken(node: Node): node is DotDotDotToken { + return node.kind === SyntaxKind.DotDotDotToken; + } + + /*@internal*/ + export function isCommaToken(node: Node): node is Token { + return node.kind === SyntaxKind.CommaToken; + } + + export function isPlusToken(node: Node): node is PlusToken { + return node.kind === SyntaxKind.PlusToken; + } + + export function isMinusToken(node: Node): node is MinusToken { + return node.kind === SyntaxKind.MinusToken; + } + + export function isAsteriskToken(node: Node): node is AsteriskToken { + return node.kind === SyntaxKind.AsteriskToken; + } + + /*@internal*/ + export function isExclamationToken(node: Node): node is ExclamationToken { + return node.kind === SyntaxKind.ExclamationToken; + } + + /*@internal*/ + export function isQuestionToken(node: Node): node is QuestionToken { + return node.kind === SyntaxKind.QuestionToken; + } + + /*@internal*/ + export function isColonToken(node: Node): node is ColonToken { + return node.kind === SyntaxKind.ColonToken; + } + + /*@internal*/ + export function isQuestionDotToken(node: Node): node is QuestionDotToken { + return node.kind === SyntaxKind.QuestionDotToken; + } + + /*@internal*/ + export function isEqualsGreaterThanToken(node: Node): node is EqualsGreaterThanToken { + return node.kind === SyntaxKind.EqualsGreaterThanToken; + } + // Identifiers export function isIdentifier(node: Node): node is Identifier { return node.kind === SyntaxKind.Identifier; } - // Names + export function isPrivateIdentifier(node: Node): node is PrivateIdentifier { + return node.kind === SyntaxKind.PrivateIdentifier; + } - export function isQualifiedName(node: Node): node is QualifiedName { - return node.kind === SyntaxKind.QualifiedName; + // Reserved Words + + /* @internal */ + export function isExportModifier(node: Node): node is ExportKeyword { + return node.kind === SyntaxKind.ExportKeyword; } - export function isComputedPropertyName(node: Node): node is ComputedPropertyName { - return node.kind === SyntaxKind.ComputedPropertyName; + /* @internal */ + export function isAsyncModifier(node: Node): node is AsyncKeyword { + return node.kind === SyntaxKind.AsyncKeyword; } - export function isPrivateIdentifier(node: Node): node is PrivateIdentifier { - return node.kind === SyntaxKind.PrivateIdentifier; + /* @internal */ + export function isAssertsKeyword(node: Node): node is AssertsKeyword { + return node.kind === SyntaxKind.AssertsKeyword; + } + + /* @internal */ + export function isAwaitKeyword(node: Node): node is AwaitKeyword { + return node.kind === SyntaxKind.AwaitKeyword; } - // Tokens + /* @internal */ + export function isReadonlyKeyword(node: Node): node is ReadonlyKeyword { + return node.kind === SyntaxKind.ReadonlyKeyword; + } + + /* @internal */ + export function isStaticModifier(node: Node): node is StaticKeyword { + return node.kind === SyntaxKind.StaticKeyword; + } /*@internal*/ - export function isSuperKeyword(node: Node): node is Token { + export function isSuperKeyword(node: Node): node is SuperExpression { return node.kind === SyntaxKind.SuperKeyword; } /*@internal*/ - export function isImportKeyword(node: Node): node is Token { + export function isImportKeyword(node: Node): node is ImportExpression { return node.kind === SyntaxKind.ImportKeyword; } - /*@internal*/ - export function isCommaToken(node: Node): node is Token { - return node.kind === SyntaxKind.CommaToken; - } + // Names - /*@internal*/ - export function isQuestionToken(node: Node): node is Token { - return node.kind === SyntaxKind.QuestionToken; + export function isQualifiedName(node: Node): node is QualifiedName { + return node.kind === SyntaxKind.QualifiedName; } - /*@internal*/ - export function isExclamationToken(node: Node): node is Token { - return node.kind === SyntaxKind.ExclamationToken; + export function isComputedPropertyName(node: Node): node is ComputedPropertyName { + return node.kind === SyntaxKind.ComputedPropertyName; } // Signature elements diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 8b18c3152e3ff..357e50d7fa58b 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -13,7 +13,7 @@ namespace ts { } else { const expression = setTextRange( - isIdentifierOrPrivateIdentifier(memberName) + isMemberName(memberName) ? factory.createPropertyAccessExpression(target, memberName) : factory.createElementAccessExpression(target, memberName), memberName @@ -412,7 +412,7 @@ namespace ts { const helperNames: string[] = []; for (const helper of helpers) { if (!helper.scoped) { - const importName = (helper as UnscopedEmitHelper).importName; + const importName = helper.importName; if (importName) { pushIfUnique(helperNames, importName); } @@ -815,18 +815,300 @@ namespace ts { || kind === SyntaxKind.ExportDeclaration; } - /* @internal */ - export function isExportModifier(node: Modifier): node is ExportKeyword { - return node.kind === SyntaxKind.ExportKeyword; + export const isTypeNodeOrTypeParameterDeclaration = or(isTypeNode, isTypeParameterDeclaration) as (node: Node) => node is TypeNode | TypeParameterDeclaration; + export const isQuestionOrExclamationToken = or(isQuestionToken, isExclamationToken) as (node: Node) => node is QuestionToken | ExclamationToken; + export const isIdentifierOrThisTypeNode = or(isIdentifier, isThisTypeNode) as (node: Node) => node is Identifier | ThisTypeNode; + export const isReadonlyKeywordOrPlusOrMinusToken = or(isReadonlyKeyword, isPlusToken, isMinusToken) as (node: Node) => node is ReadonlyKeyword | PlusToken | MinusToken; + export const isQuestionOrPlusOrMinusToken = or(isQuestionToken, isPlusToken, isMinusToken) as (node: Node) => node is QuestionToken | PlusToken | MinusToken; + export const isModuleName = or(isIdentifier, isStringLiteral) as (node: Node) => node is ModuleName; + + export function isLiteralTypeLikeExpression(node: Node): node is NullLiteral | BooleanLiteral | LiteralExpression | PrefixUnaryExpression { + const kind = node.kind; + return kind === SyntaxKind.NullKeyword + || kind === SyntaxKind.TrueKeyword + || kind === SyntaxKind.FalseKeyword + || isLiteralExpression(node) + || isPrefixUnaryExpression(node); } - /* @internal */ - export function isAsyncModifier(node: Modifier): node is AsyncKeyword { - return node.kind === SyntaxKind.AsyncKeyword; + function isExponentiationOperator(kind: SyntaxKind): kind is ExponentiationOperator { + return kind === SyntaxKind.AsteriskAsteriskToken; } - /* @internal */ - export function isStaticModifier(node: Modifier): node is StaticKeyword { - return node.kind === SyntaxKind.StaticKeyword; + function isMultiplicativeOperator(kind: SyntaxKind): kind is MultiplicativeOperator { + return kind === SyntaxKind.AsteriskToken + || kind === SyntaxKind.SlashToken + || kind === SyntaxKind.PercentToken; + } + + function isMultiplicativeOperatorOrHigher(kind: SyntaxKind): kind is MultiplicativeOperatorOrHigher { + return isExponentiationOperator(kind) + || isMultiplicativeOperator(kind); + } + + function isAdditiveOperator(kind: SyntaxKind): kind is AdditiveOperator { + return kind === SyntaxKind.PlusToken + || kind === SyntaxKind.MinusToken; + } + + function isAdditiveOperatorOrHigher(kind: SyntaxKind): kind is AdditiveOperatorOrHigher { + return isAdditiveOperator(kind) + || isMultiplicativeOperatorOrHigher(kind); + } + + function isShiftOperator(kind: SyntaxKind): kind is ShiftOperator { + return kind === SyntaxKind.LessThanLessThanToken + || kind === SyntaxKind.GreaterThanGreaterThanToken + || kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken; + } + + function isShiftOperatorOrHigher(kind: SyntaxKind): kind is ShiftOperatorOrHigher { + return isShiftOperator(kind) + || isAdditiveOperatorOrHigher(kind); + } + + function isRelationalOperator(kind: SyntaxKind): kind is RelationalOperator { + return kind === SyntaxKind.LessThanToken + || kind === SyntaxKind.LessThanEqualsToken + || kind === SyntaxKind.GreaterThanToken + || kind === SyntaxKind.GreaterThanEqualsToken + || kind === SyntaxKind.InstanceOfKeyword + || kind === SyntaxKind.InKeyword; + } + + function isRelationalOperatorOrHigher(kind: SyntaxKind): kind is RelationalOperatorOrHigher { + return isRelationalOperator(kind) + || isShiftOperatorOrHigher(kind); + } + + function isEqualityOperator(kind: SyntaxKind): kind is EqualityOperator { + return kind === SyntaxKind.EqualsEqualsToken + || kind === SyntaxKind.EqualsEqualsEqualsToken + || kind === SyntaxKind.ExclamationEqualsToken + || kind === SyntaxKind.ExclamationEqualsEqualsToken; + } + + function isEqualityOperatorOrHigher(kind: SyntaxKind): kind is EqualityOperatorOrHigher { + return isEqualityOperator(kind) + || isRelationalOperatorOrHigher(kind); + } + + function isBitwiseOperator(kind: SyntaxKind): kind is BitwiseOperator { + return kind === SyntaxKind.AmpersandToken + || kind === SyntaxKind.BarToken + || kind === SyntaxKind.CaretToken; + } + + function isBitwiseOperatorOrHigher(kind: SyntaxKind): kind is BitwiseOperatorOrHigher { + return isBitwiseOperator(kind) + || isEqualityOperatorOrHigher(kind); + } + + // NOTE: The version in utilities includes ExclamationToken, which is not a binary operator. + function isLogicalOperator(kind: SyntaxKind): kind is LogicalOperator { + return kind === SyntaxKind.AmpersandAmpersandToken + || kind === SyntaxKind.BarBarToken; + } + + function isLogicalOperatorOrHigher(kind: SyntaxKind): kind is LogicalOperatorOrHigher { + return isLogicalOperator(kind) + || isBitwiseOperatorOrHigher(kind); + } + + function isAssignmentOperatorOrHigher(kind: SyntaxKind): kind is AssignmentOperatorOrHigher { + return kind === SyntaxKind.QuestionQuestionToken + || isLogicalOperatorOrHigher(kind) + || isAssignmentOperator(kind); + } + + function isBinaryOperator(kind: SyntaxKind): kind is BinaryOperator { + return isAssignmentOperatorOrHigher(kind) + || kind === SyntaxKind.CommaToken; + } + + export function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken { + return isBinaryOperator(node.kind); + } + + type BinaryExpressionState = (machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }) => number; + + namespace BinaryExpressionState { + /** + * Handles walking into a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ + export function enter(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }): number { + const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined; + Debug.assertEqual(stateStack[stackIndex], enter); + userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState); + stateStack[stackIndex] = nextState(machine, enter); + return stackIndex; + } + + /** + * Handles walking the `left` side of a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ + export function left(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }): number { + Debug.assertEqual(stateStack[stackIndex], left); + Debug.assertIsDefined(machine.onLeft); + stateStack[stackIndex] = nextState(machine, left); + const nextNode = machine.onLeft(nodeStack[stackIndex].left, userStateStack[stackIndex], nodeStack[stackIndex]); + if (nextNode) { + checkCircularity(stackIndex, nodeStack, nextNode); + return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); + } + return stackIndex; + } + + /** + * Handles walking the `operatorToken` of a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ + export function operator(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }): number { + Debug.assertEqual(stateStack[stackIndex], operator); + Debug.assertIsDefined(machine.onOperator); + stateStack[stackIndex] = nextState(machine, operator); + machine.onOperator(nodeStack[stackIndex].operatorToken, userStateStack[stackIndex], nodeStack[stackIndex]); + return stackIndex; + } + + /** + * Handles walking the `right` side of a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ + export function right(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }): number { + Debug.assertEqual(stateStack[stackIndex], right); + Debug.assertIsDefined(machine.onRight); + stateStack[stackIndex] = nextState(machine, right); + const nextNode = machine.onRight(nodeStack[stackIndex].right, userStateStack[stackIndex], nodeStack[stackIndex]); + if (nextNode) { + checkCircularity(stackIndex, nodeStack, nextNode); + return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); + } + return stackIndex; + } + + /** + * Handles walking out of a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ + export function exit(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }): number { + Debug.assertEqual(stateStack[stackIndex], exit); + stateStack[stackIndex] = nextState(machine, exit); + const result = machine.onExit(nodeStack[stackIndex], userStateStack[stackIndex]); + if (stackIndex > 0) { + stackIndex--; + if (machine.foldState) { + const side = stateStack[stackIndex] === exit ? "right" : "left"; + userStateStack[stackIndex] = machine.foldState(userStateStack[stackIndex], result, side); + } + } + else { + resultHolder.value = result; + } + return stackIndex; + } + + /** + * Handles a frame that is already done. + * @returns The `done` state. + */ + export function done(_machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], _nodeStack: BinaryExpression[], _userStateStack: TState[], _resultHolder: { value: TResult }): number { + Debug.assertEqual(stateStack[stackIndex], done); + return stackIndex; + } + + export function nextState(machine: BinaryExpressionStateMachine, currentState: BinaryExpressionState) { + switch (currentState) { + case enter: + if (machine.onLeft) return left; + // falls through + case left: + if (machine.onOperator) return operator; + // falls through + case operator: + if (machine.onRight) return right; + // falls through + case right: return exit; + case exit: return done; + case done: return done; + default: Debug.fail("Invalid state"); + } + } + + function pushStack(stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], node: BinaryExpression) { + stackIndex++; + stateStack[stackIndex] = enter; + nodeStack[stackIndex] = node; + userStateStack[stackIndex] = undefined!; + return stackIndex; + } + + function checkCircularity(stackIndex: number, nodeStack: BinaryExpression[], node: BinaryExpression) { + if (Debug.shouldAssert(AssertionLevel.Aggressive)) { + while (stackIndex >= 0) { + Debug.assert(nodeStack[stackIndex] !== node, "Circular traversal detected."); + stackIndex--; + } + } + } + } + + /** + * Holds state machine handler functions + */ + class BinaryExpressionStateMachine { + constructor( + readonly onEnter: (node: BinaryExpression, prev: TState | undefined) => TState, + readonly onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + readonly onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, + readonly onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + readonly onExit: (node: BinaryExpression, userState: TState) => TResult, + readonly foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, + ) { + } + } + + /** + * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. + * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. + * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side. + * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node. + * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame. + * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. + * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node. + */ + export function createBinaryExpressionTrampoline( + onEnter: (node: BinaryExpression, prev: TState | undefined) => TState, + onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, + onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + onExit: (node: BinaryExpression, userState: TState) => TResult, + foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, + ) { + const machine = new BinaryExpressionStateMachine(onEnter, onLeft, onOperator, onRight, onExit, foldState); + return (node: BinaryExpression) => { + const resultHolder: { value: TResult } = { value: undefined! }; + const stateStack: BinaryExpressionState[] = [BinaryExpressionState.enter]; + const nodeStack: BinaryExpression[] = [node]; + const userStateStack: TState[] = [undefined!]; + let stackIndex = 0; + while (stateStack[stackIndex] !== BinaryExpressionState.done) { + stackIndex = stateStack[stackIndex](machine, stackIndex, stateStack, nodeStack, userStateStack, resultHolder); + } + Debug.assertEqual(stackIndex, 0); + return resultHolder.value; + }; } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 24d962c6e2f28..a2bee2104b9c9 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -1864,17 +1864,22 @@ namespace ts { if (exportedNames) { let expression: Expression = node.kind === SyntaxKind.PostfixUnaryExpression ? setTextRange( - factory.createBinaryExpression( - node.operand, - factory.createToken(node.operator === SyntaxKind.PlusPlusToken ? SyntaxKind.PlusEqualsToken : SyntaxKind.MinusEqualsToken), - factory.createNumericLiteral(1) + factory.createPrefixUnaryExpression( + node.operator, + node.operand ), /*location*/ node) : node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. noSubstitution[getNodeId(expression)] = true; - expression = factory.createParenthesizedExpression(createExportExpression(exportName, expression)); + expression = createExportExpression(exportName, expression); + } + if (node.kind === SyntaxKind.PostfixUnaryExpression) { + noSubstitution[getNodeId(expression)] = true; + expression = node.operator === SyntaxKind.PlusPlusToken + ? factory.createSubtract(expression, factory.createNumericLiteral(1)) + : factory.createAdd(expression, factory.createNumericLiteral(1)); } return expression; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3585ff50a8f3b..73667d209bf93 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -361,17 +361,14 @@ namespace ts { // JSDoc nodes JSDocTypeExpression, JSDocNameReference, - // The * type - JSDocAllType, - // The ? type - JSDocUnknownType, + JSDocAllType, // The * type + JSDocUnknownType, // The ? type JSDocNullableType, JSDocNonNullableType, JSDocOptionalType, JSDocFunctionType, JSDocVariadicType, - // https://jsdoc.app/about-namepaths.html - JSDocNamepathType, + JSDocNamepathType, // https://jsdoc.app/about-namepaths.html JSDocComment, JSDocTypeLiteral, JSDocSignature, @@ -1118,6 +1115,8 @@ namespace ts { export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier; + export type MemberName = Identifier | PrivateIdentifier; + export type DeclarationName = | Identifier | PrivateIdentifier @@ -2224,7 +2223,7 @@ namespace ts { readonly kind: SyntaxKind.PropertyAccessExpression; readonly expression: LeftHandSideExpression; readonly questionDotToken?: QuestionDotToken; - readonly name: Identifier | PrivateIdentifier; + readonly name: MemberName; } /*@internal*/ @@ -2234,7 +2233,7 @@ namespace ts { export interface PropertyAccessChain extends PropertyAccessExpression { _optionalChainBrand: any; - readonly name: Identifier | PrivateIdentifier; + readonly name: MemberName; } /* @internal */ @@ -4130,9 +4129,9 @@ namespace ts { */ /* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined; getApparentType(type: Type): Type; - /* @internal */ getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined; + /* @internal */ getSuggestedSymbolForNonexistentProperty(name: MemberName | string, containingType: Type): Symbol | undefined; /* @internal */ getSuggestedSymbolForNonexistentJSXAttribute(name: Identifier | string, containingType: Type): Symbol | undefined; - /* @internal */ getSuggestionForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): string | undefined; + /* @internal */ getSuggestionForNonexistentProperty(name: MemberName | string, containingType: Type): string | undefined; /* @internal */ getSuggestedSymbolForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined; /* @internal */ getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; /* @internal */ getSuggestedSymbolForNonexistentModule(node: Identifier, target: Symbol): Symbol | undefined; @@ -6570,7 +6569,7 @@ namespace ts { /*@internal*/ IgnoreSourceNewlines = 1 << 27, // Overrides `printerOptions.preserveSourceNewlines` to print this node (and all descendants) with default whitespace. } - export interface EmitHelper { + export interface EmitHelperBase { readonly name: string; // A unique name for this helper. readonly scoped: boolean; // Indicates whether the helper MUST be emitted in the current scope. readonly text: string | ((node: EmitHelperUniqueNameCallback) => string); // ES3-compatible raw script text, or a function yielding such a string @@ -6578,13 +6577,19 @@ namespace ts { readonly dependencies?: EmitHelper[] } - export interface UnscopedEmitHelper extends EmitHelper { + export interface ScopedEmitHelper extends EmitHelperBase { + readonly scoped: true; + } + + export interface UnscopedEmitHelper extends EmitHelperBase { readonly scoped: false; // Indicates whether the helper MUST be emitted in the current scope. /* @internal */ readonly importName?: string; // The name of the helper to use when importing via `--importHelpers`. readonly text: string; // ES3-compatible raw script text, or a function yielding such a string } + export type EmitHelper = ScopedEmitHelper | UnscopedEmitHelper; + /* @internal */ export type UniqueNameHandler = (baseName: string, checkFn?: (name: string) => boolean, optimistic?: boolean) => string; @@ -6943,10 +6948,10 @@ namespace ts { updateArrayLiteralExpression(node: ArrayLiteralExpression, elements: readonly Expression[]): ArrayLiteralExpression; createObjectLiteralExpression(properties?: readonly ObjectLiteralElementLike[], multiLine?: boolean): ObjectLiteralExpression; updateObjectLiteralExpression(node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]): ObjectLiteralExpression; - createPropertyAccessExpression(expression: Expression, name: string | Identifier | PrivateIdentifier): PropertyAccessExpression; - updatePropertyAccessExpression(node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateIdentifier): PropertyAccessExpression; - createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier | PrivateIdentifier): PropertyAccessChain; - updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier | PrivateIdentifier): PropertyAccessChain; + createPropertyAccessExpression(expression: Expression, name: string | MemberName): PropertyAccessExpression; + updatePropertyAccessExpression(node: PropertyAccessExpression, expression: Expression, name: MemberName): PropertyAccessExpression; + createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | MemberName): PropertyAccessChain; + updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName): PropertyAccessChain; createElementAccessExpression(expression: Expression, index: number | Expression): ElementAccessExpression; updateElementAccessExpression(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression): ElementAccessExpression; createElementAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, index: number | Expression): ElementAccessChain; @@ -7782,13 +7787,13 @@ namespace ts { * }); * ``` */ - onEmitNode?(hint: EmitHint, node: Node | undefined, emitCallback: (hint: EmitHint, node: Node | undefined) => void): void; + onEmitNode?(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; /** * A hook used to check if an emit notification is required for a node. * @param node The node to emit. */ - isEmitNotificationEnabled?(node: Node | undefined): boolean; + isEmitNotificationEnabled?(node: Node): boolean; /** * A hook used by the Printer to perform just-in-time substitution of a node. This is * primarily used by node transformations that need to substitute one node for another, @@ -7810,6 +7815,8 @@ namespace ts { /*@internal*/ onEmitSourceMapOfToken?: (node: Node | undefined, token: SyntaxKind, writer: (s: string) => void, pos: number, emitCallback: (token: SyntaxKind, writer: (s: string) => void, pos: number) => number) => number; /*@internal*/ onEmitSourceMapOfPosition?: (pos: number) => void; /*@internal*/ onSetSourceFile?: (node: SourceFile) => void; + /*@internal*/ onBeforeEmitNode?: (node: Node | undefined) => void; + /*@internal*/ onAfterEmitNode?: (node: Node | undefined) => void; /*@internal*/ onBeforeEmitNodeArray?: (nodes: NodeArray | undefined) => void; /*@internal*/ onAfterEmitNodeArray?: (nodes: NodeArray | undefined) => void; /*@internal*/ onBeforeEmitToken?: (node: Node) => void; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8c3ff87cbbfad..082971f01748b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3210,11 +3210,11 @@ namespace ts { } } export function getTextOfIdentifierOrLiteral(node: PropertyNameLiteral): string { - return isIdentifierOrPrivateIdentifier(node) ? idText(node) : node.text; + return isMemberName(node) ? idText(node) : node.text; } export function getEscapedTextOfIdentifierOrLiteral(node: PropertyNameLiteral): __String { - return isIdentifierOrPrivateIdentifier(node) ? node.escapedText : escapeLeadingUnderscores(node.text); + return isMemberName(node) ? node.escapedText : escapeLeadingUnderscores(node.text); } export function getPropertyNameForUniqueESSymbol(symbol: Symbol): __String { @@ -3602,6 +3602,7 @@ namespace ts { case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: + case SyntaxKind.MetaProperty: return OperatorPrecedence.Member; case SyntaxKind.AsExpression: diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index d45eb73478d5a..e6a27fb6492d4 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -930,7 +930,7 @@ namespace ts { // #region - export function isIdentifierOrPrivateIdentifier(node: Node): node is Identifier | PrivateIdentifier { + export function isMemberName(node: Node): node is MemberName { return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier; } @@ -1055,13 +1055,22 @@ namespace ts { return kind >= SyntaxKind.FirstNode; } + /** + * True if kind is of some token syntax kind. + * For example, this is true for an IfKeyword but not for an IfStatement. + * Literals are considered tokens, except TemplateLiteral, but does include TemplateHead/Middle/Tail. + */ + export function isTokenKind(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken; + } + /** * True if node is of some token syntax kind. * For example, this is true for an IfKeyword but not for an IfStatement. * Literals are considered tokens, except TemplateLiteral, but does include TemplateHead/Middle/Tail. */ export function isToken(n: Node): boolean { - return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken; + return isTokenKind(n.kind); } // Node Arrays diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 958b7b1bdd511..d673014b99035 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -1,6 +1,4 @@ namespace ts { - const isTypeNodeOrTypeParameterDeclaration = or(isTypeNode, isTypeParameterDeclaration); - /** * Visits a Node using the supplied visitor, possibly returning a new Node in its place. * @@ -350,755 +348,889 @@ namespace ts { // Names case SyntaxKind.Identifier: - return factory.updateIdentifier(node, - nodesVisitor((node).typeArguments, visitor, isTypeNodeOrTypeParameterDeclaration)); + Debug.type(node); + return factory.updateIdentifier(node, + nodesVisitor(node.typeArguments, visitor, isTypeNodeOrTypeParameterDeclaration)); case SyntaxKind.QualifiedName: - return factory.updateQualifiedName(node, - nodeVisitor((node).left, visitor, isEntityName), - nodeVisitor((node).right, visitor, isIdentifier)); + Debug.type(node); + return factory.updateQualifiedName(node, + nodeVisitor(node.left, visitor, isEntityName), + nodeVisitor(node.right, visitor, isIdentifier)); case SyntaxKind.ComputedPropertyName: - return factory.updateComputedPropertyName(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateComputedPropertyName(node, + nodeVisitor(node.expression, visitor, isExpression)); // Signature elements case SyntaxKind.TypeParameter: - return factory.updateTypeParameterDeclaration(node, - nodeVisitor((node).name, visitor, isIdentifier), - nodeVisitor((node).constraint, visitor, isTypeNode), - nodeVisitor((node).default, visitor, isTypeNode)); + Debug.type(node); + return factory.updateTypeParameterDeclaration(node, + nodeVisitor(node.name, visitor, isIdentifier), + nodeVisitor(node.constraint, visitor, isTypeNode), + nodeVisitor(node.default, visitor, isTypeNode)); case SyntaxKind.Parameter: - return factory.updateParameterDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).dotDotDotToken, tokenVisitor, isToken), - nodeVisitor((node).name, visitor, isBindingName), - nodeVisitor((node).questionToken, tokenVisitor, isToken), - nodeVisitor((node).type, visitor, isTypeNode), - nodeVisitor((node).initializer, visitor, isExpression)); + Debug.type(node); + return factory.updateParameterDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.dotDotDotToken, tokenVisitor, isDotDotDotToken), + nodeVisitor(node.name, visitor, isBindingName), + nodeVisitor(node.questionToken, tokenVisitor, isQuestionToken), + nodeVisitor(node.type, visitor, isTypeNode), + nodeVisitor(node.initializer, visitor, isExpression)); case SyntaxKind.Decorator: - return factory.updateDecorator(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateDecorator(node, + nodeVisitor(node.expression, visitor, isExpression)); // Type elements case SyntaxKind.PropertySignature: - return factory.updatePropertySignature((node), - nodesVisitor((node).modifiers, visitor, isToken), - nodeVisitor((node).name, visitor, isPropertyName), - nodeVisitor((node).questionToken, tokenVisitor, isToken), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updatePropertySignature(node, + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isPropertyName), + nodeVisitor(node.questionToken, tokenVisitor, isToken), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.PropertyDeclaration: - return factory.updatePropertyDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isPropertyName), + Debug.type(node); + return factory.updatePropertyDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isPropertyName), // QuestionToken and ExclamationToken is uniqued in Property Declaration and the signature of 'updateProperty' is that too - nodeVisitor((node).questionToken || (node).exclamationToken, tokenVisitor, isToken), - nodeVisitor((node).type, visitor, isTypeNode), - nodeVisitor((node).initializer, visitor, isExpression)); + nodeVisitor(node.questionToken || node.exclamationToken, tokenVisitor, isQuestionOrExclamationToken), + nodeVisitor(node.type, visitor, isTypeNode), + nodeVisitor(node.initializer, visitor, isExpression)); case SyntaxKind.MethodSignature: - return factory.updateMethodSignature(node, - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isPropertyName), - nodeVisitor((node).questionToken, tokenVisitor, isToken), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodesVisitor((node).parameters, visitor, isParameterDeclaration), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateMethodSignature(node, + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isPropertyName), + nodeVisitor(node.questionToken, tokenVisitor, isQuestionToken), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodesVisitor(node.parameters, visitor, isParameterDeclaration), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.MethodDeclaration: - return factory.updateMethodDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).asteriskToken, tokenVisitor, isToken), - nodeVisitor((node).name, visitor, isPropertyName), - nodeVisitor((node).questionToken, tokenVisitor, isToken), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - nodeVisitor((node).type, visitor, isTypeNode), - visitFunctionBody((node).body!, visitor, context, nodeVisitor)); + Debug.type(node); + return factory.updateMethodDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.asteriskToken, tokenVisitor, isAsteriskToken), + nodeVisitor(node.name, visitor, isPropertyName), + nodeVisitor(node.questionToken, tokenVisitor, isQuestionToken), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + visitParameterList(node.parameters, visitor, context, nodesVisitor), + nodeVisitor(node.type, visitor, isTypeNode), + visitFunctionBody(node.body!, visitor, context, nodeVisitor)); case SyntaxKind.Constructor: - return factory.updateConstructorDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - visitFunctionBody((node).body!, visitor, context, nodeVisitor)); + Debug.type(node); + return factory.updateConstructorDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + visitParameterList(node.parameters, visitor, context, nodesVisitor), + visitFunctionBody(node.body!, visitor, context, nodeVisitor)); case SyntaxKind.GetAccessor: - return factory.updateGetAccessorDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isPropertyName), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - nodeVisitor((node).type, visitor, isTypeNode), - visitFunctionBody((node).body!, visitor, context, nodeVisitor)); + Debug.type(node); + return factory.updateGetAccessorDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isPropertyName), + visitParameterList(node.parameters, visitor, context, nodesVisitor), + nodeVisitor(node.type, visitor, isTypeNode), + visitFunctionBody(node.body!, visitor, context, nodeVisitor)); case SyntaxKind.SetAccessor: - return factory.updateSetAccessorDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isPropertyName), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - visitFunctionBody((node).body!, visitor, context, nodeVisitor)); + Debug.type(node); + return factory.updateSetAccessorDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isPropertyName), + visitParameterList(node.parameters, visitor, context, nodesVisitor), + visitFunctionBody(node.body!, visitor, context, nodeVisitor)); case SyntaxKind.CallSignature: - return factory.updateCallSignature(node, - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodesVisitor((node).parameters, visitor, isParameterDeclaration), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateCallSignature(node, + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodesVisitor(node.parameters, visitor, isParameterDeclaration), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.ConstructSignature: - return factory.updateConstructSignature(node, - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodesVisitor((node).parameters, visitor, isParameterDeclaration), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateConstructSignature(node, + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodesVisitor(node.parameters, visitor, isParameterDeclaration), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.IndexSignature: - return factory.updateIndexSignature(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodesVisitor((node).parameters, visitor, isParameterDeclaration), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateIndexSignature(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodesVisitor(node.parameters, visitor, isParameterDeclaration), + nodeVisitor(node.type, visitor, isTypeNode)); // Types case SyntaxKind.TypePredicate: - return factory.updateTypePredicateNode(node, - nodeVisitor((node).assertsModifier, visitor), - nodeVisitor((node).parameterName, visitor), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateTypePredicateNode(node, + nodeVisitor(node.assertsModifier, visitor, isAssertsKeyword), + nodeVisitor(node.parameterName, visitor, isIdentifierOrThisTypeNode), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.TypeReference: - return factory.updateTypeReferenceNode(node, - nodeVisitor((node).typeName, visitor, isEntityName), - nodesVisitor((node).typeArguments, visitor, isTypeNode)); + Debug.type(node); + return factory.updateTypeReferenceNode(node, + nodeVisitor(node.typeName, visitor, isEntityName), + nodesVisitor(node.typeArguments, visitor, isTypeNode)); case SyntaxKind.FunctionType: - return factory.updateFunctionTypeNode(node, - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodesVisitor((node).parameters, visitor, isParameterDeclaration), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateFunctionTypeNode(node, + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodesVisitor(node.parameters, visitor, isParameterDeclaration), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.ConstructorType: - return factory.updateConstructorTypeNode(node, - nodesVisitor((node).modifiers, visitor, isModifier), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodesVisitor((node).parameters, visitor, isParameterDeclaration), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateConstructorTypeNode(node, + nodesVisitor(node.modifiers, visitor, isModifier), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodesVisitor(node.parameters, visitor, isParameterDeclaration), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.TypeQuery: - return factory.updateTypeQueryNode((node), - nodeVisitor((node).exprName, visitor, isEntityName)); + Debug.type(node); + return factory.updateTypeQueryNode(node, + nodeVisitor(node.exprName, visitor, isEntityName)); case SyntaxKind.TypeLiteral: - return factory.updateTypeLiteralNode((node), - nodesVisitor((node).members, visitor, isTypeElement)); + Debug.type(node); + return factory.updateTypeLiteralNode(node, + nodesVisitor(node.members, visitor, isTypeElement)); case SyntaxKind.ArrayType: - return factory.updateArrayTypeNode(node, - nodeVisitor((node).elementType, visitor, isTypeNode)); + Debug.type(node); + return factory.updateArrayTypeNode(node, + nodeVisitor(node.elementType, visitor, isTypeNode)); case SyntaxKind.TupleType: - return factory.updateTupleTypeNode((node), - nodesVisitor((node).elements, visitor, isTypeNode)); + Debug.type(node); + return factory.updateTupleTypeNode(node, + nodesVisitor(node.elements, visitor, isTypeNode)); case SyntaxKind.OptionalType: - return factory.updateOptionalTypeNode((node), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateOptionalTypeNode(node, + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.RestType: - return factory.updateRestTypeNode((node), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateRestTypeNode(node, + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.UnionType: - return factory.updateUnionTypeNode(node, - nodesVisitor((node).types, visitor, isTypeNode)); + Debug.type(node); + return factory.updateUnionTypeNode(node, + nodesVisitor(node.types, visitor, isTypeNode)); case SyntaxKind.IntersectionType: - return factory.updateIntersectionTypeNode(node, - nodesVisitor((node).types, visitor, isTypeNode)); + Debug.type(node); + return factory.updateIntersectionTypeNode(node, + nodesVisitor(node.types, visitor, isTypeNode)); case SyntaxKind.ConditionalType: - return factory.updateConditionalTypeNode(node, - nodeVisitor((node).checkType, visitor, isTypeNode), - nodeVisitor((node).extendsType, visitor, isTypeNode), - nodeVisitor((node).trueType, visitor, isTypeNode), - nodeVisitor((node).falseType, visitor, isTypeNode)); + Debug.type(node); + return factory.updateConditionalTypeNode(node, + nodeVisitor(node.checkType, visitor, isTypeNode), + nodeVisitor(node.extendsType, visitor, isTypeNode), + nodeVisitor(node.trueType, visitor, isTypeNode), + nodeVisitor(node.falseType, visitor, isTypeNode)); case SyntaxKind.InferType: - return factory.updateInferTypeNode(node, - nodeVisitor((node).typeParameter, visitor, isTypeParameterDeclaration)); + Debug.type(node); + return factory.updateInferTypeNode(node, + nodeVisitor(node.typeParameter, visitor, isTypeParameterDeclaration)); case SyntaxKind.ImportType: - return factory.updateImportTypeNode(node, - nodeVisitor((node).argument, visitor, isTypeNode), - nodeVisitor((node).qualifier, visitor, isEntityName), - visitNodes((node).typeArguments, visitor, isTypeNode), - (node).isTypeOf + Debug.type(node); + return factory.updateImportTypeNode(node, + nodeVisitor(node.argument, visitor, isTypeNode), + nodeVisitor(node.qualifier, visitor, isEntityName), + visitNodes(node.typeArguments, visitor, isTypeNode), + node.isTypeOf ); case SyntaxKind.NamedTupleMember: - return factory.updateNamedTupleMember(node, - visitNode((node).dotDotDotToken, visitor, isToken), - visitNode((node).name, visitor, isIdentifier), - visitNode((node).questionToken, visitor, isToken), - visitNode((node).type, visitor, isTypeNode), + Debug.type(node); + return factory.updateNamedTupleMember(node, + visitNode(node.dotDotDotToken, visitor, isDotDotDotToken), + visitNode(node.name, visitor, isIdentifier), + visitNode(node.questionToken, visitor, isQuestionToken), + visitNode(node.type, visitor, isTypeNode), ); case SyntaxKind.ParenthesizedType: - return factory.updateParenthesizedType(node, - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateParenthesizedType(node, + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.TypeOperator: - return factory.updateTypeOperatorNode(node, - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateTypeOperatorNode(node, + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.IndexedAccessType: - return factory.updateIndexedAccessTypeNode((node), - nodeVisitor((node).objectType, visitor, isTypeNode), - nodeVisitor((node).indexType, visitor, isTypeNode)); + Debug.type(node); + return factory.updateIndexedAccessTypeNode(node, + nodeVisitor(node.objectType, visitor, isTypeNode), + nodeVisitor(node.indexType, visitor, isTypeNode)); case SyntaxKind.MappedType: - return factory.updateMappedTypeNode((node), - nodeVisitor((node).readonlyToken, tokenVisitor, isToken), - nodeVisitor((node).typeParameter, visitor, isTypeParameterDeclaration), - nodeVisitor((node).nameType, visitor, isTypeNode), - nodeVisitor((node).questionToken, tokenVisitor, isToken), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateMappedTypeNode(node, + nodeVisitor(node.readonlyToken, tokenVisitor, isReadonlyKeywordOrPlusOrMinusToken), + nodeVisitor(node.typeParameter, visitor, isTypeParameterDeclaration), + nodeVisitor(node.nameType, visitor, isTypeNode), + nodeVisitor(node.questionToken, tokenVisitor, isQuestionOrPlusOrMinusToken), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.LiteralType: - return factory.updateLiteralTypeNode(node, - nodeVisitor((node).literal, visitor, isExpression)); + Debug.type(node); + return factory.updateLiteralTypeNode(node, + nodeVisitor(node.literal, visitor, isExpression)); case SyntaxKind.TemplateLiteralType: - return factory.updateTemplateLiteralType(node, - nodeVisitor((node).head, visitor, isTemplateHead), - nodesVisitor((node).templateSpans, visitor, isTemplateLiteralTypeSpan)); + Debug.type(node); + return factory.updateTemplateLiteralType(node, + nodeVisitor(node.head, visitor, isTemplateHead), + nodesVisitor(node.templateSpans, visitor, isTemplateLiteralTypeSpan)); case SyntaxKind.TemplateLiteralTypeSpan: - return factory.updateTemplateLiteralTypeSpan(node, - nodeVisitor((node).type, visitor, isTypeNode), - nodeVisitor((node).literal, visitor, isTemplateMiddleOrTemplateTail)); + Debug.type(node); + return factory.updateTemplateLiteralTypeSpan(node, + nodeVisitor(node.type, visitor, isTypeNode), + nodeVisitor(node.literal, visitor, isTemplateMiddleOrTemplateTail)); // Binding patterns case SyntaxKind.ObjectBindingPattern: - return factory.updateObjectBindingPattern(node, - nodesVisitor((node).elements, visitor, isBindingElement)); + Debug.type(node); + return factory.updateObjectBindingPattern(node, + nodesVisitor(node.elements, visitor, isBindingElement)); case SyntaxKind.ArrayBindingPattern: - return factory.updateArrayBindingPattern(node, - nodesVisitor((node).elements, visitor, isArrayBindingElement)); + Debug.type(node); + return factory.updateArrayBindingPattern(node, + nodesVisitor(node.elements, visitor, isArrayBindingElement)); case SyntaxKind.BindingElement: - return factory.updateBindingElement(node, - nodeVisitor((node).dotDotDotToken, tokenVisitor, isToken), - nodeVisitor((node).propertyName, visitor, isPropertyName), - nodeVisitor((node).name, visitor, isBindingName), - nodeVisitor((node).initializer, visitor, isExpression)); + Debug.type(node); + return factory.updateBindingElement(node, + nodeVisitor(node.dotDotDotToken, tokenVisitor, isDotDotDotToken), + nodeVisitor(node.propertyName, visitor, isPropertyName), + nodeVisitor(node.name, visitor, isBindingName), + nodeVisitor(node.initializer, visitor, isExpression)); // Expression case SyntaxKind.ArrayLiteralExpression: - return factory.updateArrayLiteralExpression(node, - nodesVisitor((node).elements, visitor, isExpression)); + Debug.type(node); + return factory.updateArrayLiteralExpression(node, + nodesVisitor(node.elements, visitor, isExpression)); case SyntaxKind.ObjectLiteralExpression: - return factory.updateObjectLiteralExpression(node, - nodesVisitor((node).properties, visitor, isObjectLiteralElementLike)); + Debug.type(node); + return factory.updateObjectLiteralExpression(node, + nodesVisitor(node.properties, visitor, isObjectLiteralElementLike)); case SyntaxKind.PropertyAccessExpression: if (node.flags & NodeFlags.OptionalChain) { - return factory.updatePropertyAccessChain(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).questionDotToken, tokenVisitor, isToken), - nodeVisitor((node).name, visitor, isIdentifier)); + Debug.type(node); + return factory.updatePropertyAccessChain(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.questionDotToken, tokenVisitor, isQuestionDotToken), + nodeVisitor(node.name, visitor, isMemberName)); } - return factory.updatePropertyAccessExpression(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).name, visitor, isIdentifierOrPrivateIdentifier)); + Debug.type(node); + return factory.updatePropertyAccessExpression(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.name, visitor, isMemberName)); case SyntaxKind.ElementAccessExpression: if (node.flags & NodeFlags.OptionalChain) { - return factory.updateElementAccessChain(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).questionDotToken, tokenVisitor, isToken), - nodeVisitor((node).argumentExpression, visitor, isExpression)); + Debug.type(node); + return factory.updateElementAccessChain(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.questionDotToken, tokenVisitor, isQuestionDotToken), + nodeVisitor(node.argumentExpression, visitor, isExpression)); } - return factory.updateElementAccessExpression(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).argumentExpression, visitor, isExpression)); + Debug.type(node); + return factory.updateElementAccessExpression(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.argumentExpression, visitor, isExpression)); case SyntaxKind.CallExpression: if (node.flags & NodeFlags.OptionalChain) { - return factory.updateCallChain(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).questionDotToken, tokenVisitor, isToken), - nodesVisitor((node).typeArguments, visitor, isTypeNode), - nodesVisitor((node).arguments, visitor, isExpression)); + Debug.type(node); + return factory.updateCallChain(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.questionDotToken, tokenVisitor, isQuestionDotToken), + nodesVisitor(node.typeArguments, visitor, isTypeNode), + nodesVisitor(node.arguments, visitor, isExpression)); } - return factory.updateCallExpression(node, - nodeVisitor((node).expression, visitor, isExpression), - nodesVisitor((node).typeArguments, visitor, isTypeNode), - nodesVisitor((node).arguments, visitor, isExpression)); + Debug.type(node); + return factory.updateCallExpression(node, + nodeVisitor(node.expression, visitor, isExpression), + nodesVisitor(node.typeArguments, visitor, isTypeNode), + nodesVisitor(node.arguments, visitor, isExpression)); case SyntaxKind.NewExpression: - return factory.updateNewExpression(node, - nodeVisitor((node).expression, visitor, isExpression), - nodesVisitor((node).typeArguments, visitor, isTypeNode), - nodesVisitor((node).arguments, visitor, isExpression)); + Debug.type(node); + return factory.updateNewExpression(node, + nodeVisitor(node.expression, visitor, isExpression), + nodesVisitor(node.typeArguments, visitor, isTypeNode), + nodesVisitor(node.arguments, visitor, isExpression)); case SyntaxKind.TaggedTemplateExpression: - return factory.updateTaggedTemplateExpression(node, - nodeVisitor((node).tag, visitor, isExpression), - visitNodes((node).typeArguments, visitor, isExpression), - nodeVisitor((node).template, visitor, isTemplateLiteral)); + Debug.type(node); + return factory.updateTaggedTemplateExpression(node, + nodeVisitor(node.tag, visitor, isExpression), + visitNodes(node.typeArguments, visitor, isTypeNode), + nodeVisitor(node.template, visitor, isTemplateLiteral)); case SyntaxKind.TypeAssertionExpression: - return factory.updateTypeAssertion(node, - nodeVisitor((node).type, visitor, isTypeNode), - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateTypeAssertion(node, + nodeVisitor(node.type, visitor, isTypeNode), + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.ParenthesizedExpression: - return factory.updateParenthesizedExpression(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateParenthesizedExpression(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.FunctionExpression: - return factory.updateFunctionExpression(node, - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).asteriskToken, tokenVisitor, isToken), - nodeVisitor((node).name, visitor, isIdentifier), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - nodeVisitor((node).type, visitor, isTypeNode), - visitFunctionBody((node).body, visitor, context, nodeVisitor)); + Debug.type(node); + return factory.updateFunctionExpression(node, + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.asteriskToken, tokenVisitor, isAsteriskToken), + nodeVisitor(node.name, visitor, isIdentifier), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + visitParameterList(node.parameters, visitor, context, nodesVisitor), + nodeVisitor(node.type, visitor, isTypeNode), + visitFunctionBody(node.body, visitor, context, nodeVisitor)); case SyntaxKind.ArrowFunction: - return factory.updateArrowFunction(node, - nodesVisitor((node).modifiers, visitor, isModifier), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - nodeVisitor((node).type, visitor, isTypeNode), - nodeVisitor((node).equalsGreaterThanToken, tokenVisitor, isToken), - visitFunctionBody((node).body, visitor, context, nodeVisitor)); + Debug.type(node); + return factory.updateArrowFunction(node, + nodesVisitor(node.modifiers, visitor, isModifier), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + visitParameterList(node.parameters, visitor, context, nodesVisitor), + nodeVisitor(node.type, visitor, isTypeNode), + nodeVisitor(node.equalsGreaterThanToken, tokenVisitor, isEqualsGreaterThanToken), + visitFunctionBody(node.body, visitor, context, nodeVisitor)); case SyntaxKind.DeleteExpression: - return factory.updateDeleteExpression(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateDeleteExpression(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.TypeOfExpression: - return factory.updateTypeOfExpression(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateTypeOfExpression(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.VoidExpression: - return factory.updateVoidExpression(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateVoidExpression(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.AwaitExpression: - return factory.updateAwaitExpression(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateAwaitExpression(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.PrefixUnaryExpression: - return factory.updatePrefixUnaryExpression(node, - nodeVisitor((node).operand, visitor, isExpression)); + Debug.type(node); + return factory.updatePrefixUnaryExpression(node, + nodeVisitor(node.operand, visitor, isExpression)); case SyntaxKind.PostfixUnaryExpression: - return factory.updatePostfixUnaryExpression(node, - nodeVisitor((node).operand, visitor, isExpression)); + Debug.type(node); + return factory.updatePostfixUnaryExpression(node, + nodeVisitor(node.operand, visitor, isExpression)); case SyntaxKind.BinaryExpression: - return factory.updateBinaryExpression(node, - nodeVisitor((node).left, visitor, isExpression), - nodeVisitor((node).operatorToken, tokenVisitor, isToken), - nodeVisitor((node).right, visitor, isExpression)); + Debug.type(node); + return factory.updateBinaryExpression(node, + nodeVisitor(node.left, visitor, isExpression), + nodeVisitor(node.operatorToken, tokenVisitor, isBinaryOperatorToken), + nodeVisitor(node.right, visitor, isExpression)); case SyntaxKind.ConditionalExpression: - return factory.updateConditionalExpression(node, - nodeVisitor((node).condition, visitor, isExpression), - nodeVisitor((node).questionToken, tokenVisitor, isToken), - nodeVisitor((node).whenTrue, visitor, isExpression), - nodeVisitor((node).colonToken, tokenVisitor, isToken), - nodeVisitor((node).whenFalse, visitor, isExpression)); + Debug.type(node); + return factory.updateConditionalExpression(node, + nodeVisitor(node.condition, visitor, isExpression), + nodeVisitor(node.questionToken, tokenVisitor, isQuestionToken), + nodeVisitor(node.whenTrue, visitor, isExpression), + nodeVisitor(node.colonToken, tokenVisitor, isColonToken), + nodeVisitor(node.whenFalse, visitor, isExpression)); case SyntaxKind.TemplateExpression: - return factory.updateTemplateExpression(node, - nodeVisitor((node).head, visitor, isTemplateHead), - nodesVisitor((node).templateSpans, visitor, isTemplateSpan)); + Debug.type(node); + return factory.updateTemplateExpression(node, + nodeVisitor(node.head, visitor, isTemplateHead), + nodesVisitor(node.templateSpans, visitor, isTemplateSpan)); case SyntaxKind.YieldExpression: - return factory.updateYieldExpression(node, - nodeVisitor((node).asteriskToken, tokenVisitor, isToken), - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateYieldExpression(node, + nodeVisitor(node.asteriskToken, tokenVisitor, isAsteriskToken), + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.SpreadElement: - return factory.updateSpreadElement(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateSpreadElement(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.ClassExpression: - return factory.updateClassExpression(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isIdentifier), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodesVisitor((node).heritageClauses, visitor, isHeritageClause), - nodesVisitor((node).members, visitor, isClassElement)); + Debug.type(node); + return factory.updateClassExpression(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isIdentifier), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodesVisitor(node.heritageClauses, visitor, isHeritageClause), + nodesVisitor(node.members, visitor, isClassElement)); case SyntaxKind.ExpressionWithTypeArguments: - return factory.updateExpressionWithTypeArguments(node, - nodeVisitor((node).expression, visitor, isExpression), - nodesVisitor((node).typeArguments, visitor, isTypeNode)); + Debug.type(node); + return factory.updateExpressionWithTypeArguments(node, + nodeVisitor(node.expression, visitor, isExpression), + nodesVisitor(node.typeArguments, visitor, isTypeNode)); case SyntaxKind.AsExpression: - return factory.updateAsExpression(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateAsExpression(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.NonNullExpression: if (node.flags & NodeFlags.OptionalChain) { - return factory.updateNonNullChain(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateNonNullChain(node, + nodeVisitor(node.expression, visitor, isExpression)); } - return factory.updateNonNullExpression(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateNonNullExpression(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.MetaProperty: - return factory.updateMetaProperty(node, - nodeVisitor((node).name, visitor, isIdentifier)); + Debug.type(node); + return factory.updateMetaProperty(node, + nodeVisitor(node.name, visitor, isIdentifier)); // Misc case SyntaxKind.TemplateSpan: - return factory.updateTemplateSpan(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).literal, visitor, isTemplateMiddleOrTemplateTail)); + Debug.type(node); + return factory.updateTemplateSpan(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.literal, visitor, isTemplateMiddleOrTemplateTail)); // Element case SyntaxKind.Block: - return factory.updateBlock(node, - nodesVisitor((node).statements, visitor, isStatement)); + Debug.type(node); + return factory.updateBlock(node, + nodesVisitor(node.statements, visitor, isStatement)); case SyntaxKind.VariableStatement: - return factory.updateVariableStatement(node, - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).declarationList, visitor, isVariableDeclarationList)); + Debug.type(node); + return factory.updateVariableStatement(node, + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.declarationList, visitor, isVariableDeclarationList)); case SyntaxKind.ExpressionStatement: - return factory.updateExpressionStatement(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateExpressionStatement(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.IfStatement: - return factory.updateIfStatement(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).thenStatement, visitor, isStatement, factory.liftToBlock), - nodeVisitor((node).elseStatement, visitor, isStatement, factory.liftToBlock)); + Debug.type(node); + return factory.updateIfStatement(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.thenStatement, visitor, isStatement, factory.liftToBlock), + nodeVisitor(node.elseStatement, visitor, isStatement, factory.liftToBlock)); case SyntaxKind.DoStatement: - return factory.updateDoStatement(node, - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock), - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateDoStatement(node, + nodeVisitor(node.statement, visitor, isStatement, factory.liftToBlock), + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.WhileStatement: - return factory.updateWhileStatement(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + Debug.type(node); + return factory.updateWhileStatement(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.statement, visitor, isStatement, factory.liftToBlock)); case SyntaxKind.ForStatement: - return factory.updateForStatement(node, - nodeVisitor((node).initializer, visitor, isForInitializer), - nodeVisitor((node).condition, visitor, isExpression), - nodeVisitor((node).incrementor, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + Debug.type(node); + return factory.updateForStatement(node, + nodeVisitor(node.initializer, visitor, isForInitializer), + nodeVisitor(node.condition, visitor, isExpression), + nodeVisitor(node.incrementor, visitor, isExpression), + nodeVisitor(node.statement, visitor, isStatement, factory.liftToBlock)); case SyntaxKind.ForInStatement: - return factory.updateForInStatement(node, - nodeVisitor((node).initializer, visitor, isForInitializer), - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + Debug.type(node); + return factory.updateForInStatement(node, + nodeVisitor(node.initializer, visitor, isForInitializer), + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.statement, visitor, isStatement, factory.liftToBlock)); case SyntaxKind.ForOfStatement: - return factory.updateForOfStatement(node, - nodeVisitor((node).awaitModifier, tokenVisitor, isToken), - nodeVisitor((node).initializer, visitor, isForInitializer), - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + Debug.type(node); + return factory.updateForOfStatement(node, + nodeVisitor(node.awaitModifier, tokenVisitor, isAwaitKeyword), + nodeVisitor(node.initializer, visitor, isForInitializer), + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.statement, visitor, isStatement, factory.liftToBlock)); case SyntaxKind.ContinueStatement: - return factory.updateContinueStatement(node, - nodeVisitor((node).label, visitor, isIdentifier)); + Debug.type(node); + return factory.updateContinueStatement(node, + nodeVisitor(node.label, visitor, isIdentifier)); case SyntaxKind.BreakStatement: - return factory.updateBreakStatement(node, - nodeVisitor((node).label, visitor, isIdentifier)); + Debug.type(node); + return factory.updateBreakStatement(node, + nodeVisitor(node.label, visitor, isIdentifier)); case SyntaxKind.ReturnStatement: - return factory.updateReturnStatement(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateReturnStatement(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.WithStatement: - return factory.updateWithStatement(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + Debug.type(node); + return factory.updateWithStatement(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.statement, visitor, isStatement, factory.liftToBlock)); case SyntaxKind.SwitchStatement: - return factory.updateSwitchStatement(node, - nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).caseBlock, visitor, isCaseBlock)); + Debug.type(node); + return factory.updateSwitchStatement(node, + nodeVisitor(node.expression, visitor, isExpression), + nodeVisitor(node.caseBlock, visitor, isCaseBlock)); case SyntaxKind.LabeledStatement: - return factory.updateLabeledStatement(node, - nodeVisitor((node).label, visitor, isIdentifier), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + Debug.type(node); + return factory.updateLabeledStatement(node, + nodeVisitor(node.label, visitor, isIdentifier), + nodeVisitor(node.statement, visitor, isStatement, factory.liftToBlock)); case SyntaxKind.ThrowStatement: - return factory.updateThrowStatement(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateThrowStatement(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.TryStatement: - return factory.updateTryStatement(node, - nodeVisitor((node).tryBlock, visitor, isBlock), - nodeVisitor((node).catchClause, visitor, isCatchClause), - nodeVisitor((node).finallyBlock, visitor, isBlock)); + Debug.type(node); + return factory.updateTryStatement(node, + nodeVisitor(node.tryBlock, visitor, isBlock), + nodeVisitor(node.catchClause, visitor, isCatchClause), + nodeVisitor(node.finallyBlock, visitor, isBlock)); case SyntaxKind.VariableDeclaration: - return factory.updateVariableDeclaration(node, - nodeVisitor((node).name, visitor, isBindingName), - nodeVisitor((node).exclamationToken, tokenVisitor, isToken), - nodeVisitor((node).type, visitor, isTypeNode), - nodeVisitor((node).initializer, visitor, isExpression)); + Debug.type(node); + return factory.updateVariableDeclaration(node, + nodeVisitor(node.name, visitor, isBindingName), + nodeVisitor(node.exclamationToken, tokenVisitor, isExclamationToken), + nodeVisitor(node.type, visitor, isTypeNode), + nodeVisitor(node.initializer, visitor, isExpression)); case SyntaxKind.VariableDeclarationList: - return factory.updateVariableDeclarationList(node, - nodesVisitor((node).declarations, visitor, isVariableDeclaration)); + Debug.type(node); + return factory.updateVariableDeclarationList(node, + nodesVisitor(node.declarations, visitor, isVariableDeclaration)); case SyntaxKind.FunctionDeclaration: - return factory.updateFunctionDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).asteriskToken, tokenVisitor, isToken), - nodeVisitor((node).name, visitor, isIdentifier), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - visitParameterList((node).parameters, visitor, context, nodesVisitor), - nodeVisitor((node).type, visitor, isTypeNode), - visitFunctionBody((node).body, visitor, context, nodeVisitor)); + Debug.type(node); + return factory.updateFunctionDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.asteriskToken, tokenVisitor, isAsteriskToken), + nodeVisitor(node.name, visitor, isIdentifier), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + visitParameterList(node.parameters, visitor, context, nodesVisitor), + nodeVisitor(node.type, visitor, isTypeNode), + visitFunctionBody(node.body, visitor, context, nodeVisitor)); case SyntaxKind.ClassDeclaration: - return factory.updateClassDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isIdentifier), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodesVisitor((node).heritageClauses, visitor, isHeritageClause), - nodesVisitor((node).members, visitor, isClassElement)); + Debug.type(node); + return factory.updateClassDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isIdentifier), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodesVisitor(node.heritageClauses, visitor, isHeritageClause), + nodesVisitor(node.members, visitor, isClassElement)); case SyntaxKind.InterfaceDeclaration: - return factory.updateInterfaceDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isIdentifier), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodesVisitor((node).heritageClauses, visitor, isHeritageClause), - nodesVisitor((node).members, visitor, isTypeElement)); + Debug.type(node); + return factory.updateInterfaceDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isIdentifier), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodesVisitor(node.heritageClauses, visitor, isHeritageClause), + nodesVisitor(node.members, visitor, isTypeElement)); case SyntaxKind.TypeAliasDeclaration: - return factory.updateTypeAliasDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isIdentifier), - nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration), - nodeVisitor((node).type, visitor, isTypeNode)); + Debug.type(node); + return factory.updateTypeAliasDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isIdentifier), + nodesVisitor(node.typeParameters, visitor, isTypeParameterDeclaration), + nodeVisitor(node.type, visitor, isTypeNode)); case SyntaxKind.EnumDeclaration: - return factory.updateEnumDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isIdentifier), - nodesVisitor((node).members, visitor, isEnumMember)); + Debug.type(node); + return factory.updateEnumDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isIdentifier), + nodesVisitor(node.members, visitor, isEnumMember)); case SyntaxKind.ModuleDeclaration: - return factory.updateModuleDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).name, visitor, isIdentifier), - nodeVisitor((node).body, visitor, isModuleBody)); + Debug.type(node); + return factory.updateModuleDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.name, visitor, isModuleName), + nodeVisitor(node.body, visitor, isModuleBody)); case SyntaxKind.ModuleBlock: - return factory.updateModuleBlock(node, - nodesVisitor((node).statements, visitor, isStatement)); + Debug.type(node); + return factory.updateModuleBlock(node, + nodesVisitor(node.statements, visitor, isStatement)); case SyntaxKind.CaseBlock: - return factory.updateCaseBlock(node, - nodesVisitor((node).clauses, visitor, isCaseOrDefaultClause)); + Debug.type(node); + return factory.updateCaseBlock(node, + nodesVisitor(node.clauses, visitor, isCaseOrDefaultClause)); case SyntaxKind.NamespaceExportDeclaration: - return factory.updateNamespaceExportDeclaration(node, - nodeVisitor((node).name, visitor, isIdentifier)); + Debug.type(node); + return factory.updateNamespaceExportDeclaration(node, + nodeVisitor(node.name, visitor, isIdentifier)); case SyntaxKind.ImportEqualsDeclaration: - return factory.updateImportEqualsDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - (node).isTypeOnly, - nodeVisitor((node).name, visitor, isIdentifier), - nodeVisitor((node).moduleReference, visitor, isModuleReference)); + Debug.type(node); + return factory.updateImportEqualsDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + node.isTypeOnly, + nodeVisitor(node.name, visitor, isIdentifier), + nodeVisitor(node.moduleReference, visitor, isModuleReference)); case SyntaxKind.ImportDeclaration: - return factory.updateImportDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).importClause, visitor, isImportClause), - nodeVisitor((node).moduleSpecifier, visitor, isExpression)); + Debug.type(node); + return factory.updateImportDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.importClause, visitor, isImportClause), + nodeVisitor(node.moduleSpecifier, visitor, isExpression)); case SyntaxKind.ImportClause: - return factory.updateImportClause(node, - (node).isTypeOnly, - nodeVisitor((node).name, visitor, isIdentifier), - nodeVisitor((node).namedBindings, visitor, isNamedImportBindings)); + Debug.type(node); + return factory.updateImportClause(node, + node.isTypeOnly, + nodeVisitor(node.name, visitor, isIdentifier), + nodeVisitor(node.namedBindings, visitor, isNamedImportBindings)); case SyntaxKind.NamespaceImport: - return factory.updateNamespaceImport(node, - nodeVisitor((node).name, visitor, isIdentifier)); + Debug.type(node); + return factory.updateNamespaceImport(node, + nodeVisitor(node.name, visitor, isIdentifier)); case SyntaxKind.NamespaceExport: - return factory.updateNamespaceExport(node, - nodeVisitor((node).name, visitor, isIdentifier)); + Debug.type(node); + return factory.updateNamespaceExport(node, + nodeVisitor(node.name, visitor, isIdentifier)); case SyntaxKind.NamedImports: - return factory.updateNamedImports(node, - nodesVisitor((node).elements, visitor, isImportSpecifier)); + Debug.type(node); + return factory.updateNamedImports(node, + nodesVisitor(node.elements, visitor, isImportSpecifier)); case SyntaxKind.ImportSpecifier: - return factory.updateImportSpecifier(node, - nodeVisitor((node).propertyName, visitor, isIdentifier), - nodeVisitor((node).name, visitor, isIdentifier)); + Debug.type(node); + return factory.updateImportSpecifier(node, + nodeVisitor(node.propertyName, visitor, isIdentifier), + nodeVisitor(node.name, visitor, isIdentifier)); case SyntaxKind.ExportAssignment: - return factory.updateExportAssignment(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateExportAssignment(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.ExportDeclaration: - return factory.updateExportDeclaration(node, - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier), - (node).isTypeOnly, - nodeVisitor((node).exportClause, visitor, isNamedExportBindings), - nodeVisitor((node).moduleSpecifier, visitor, isExpression)); + Debug.type(node); + return factory.updateExportDeclaration(node, + nodesVisitor(node.decorators, visitor, isDecorator), + nodesVisitor(node.modifiers, visitor, isModifier), + node.isTypeOnly, + nodeVisitor(node.exportClause, visitor, isNamedExportBindings), + nodeVisitor(node.moduleSpecifier, visitor, isExpression)); case SyntaxKind.NamedExports: - return factory.updateNamedExports(node, - nodesVisitor((node).elements, visitor, isExportSpecifier)); + Debug.type(node); + return factory.updateNamedExports(node, + nodesVisitor(node.elements, visitor, isExportSpecifier)); case SyntaxKind.ExportSpecifier: - return factory.updateExportSpecifier(node, - nodeVisitor((node).propertyName, visitor, isIdentifier), - nodeVisitor((node).name, visitor, isIdentifier)); + Debug.type(node); + return factory.updateExportSpecifier(node, + nodeVisitor(node.propertyName, visitor, isIdentifier), + nodeVisitor(node.name, visitor, isIdentifier)); // Module references case SyntaxKind.ExternalModuleReference: - return factory.updateExternalModuleReference(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateExternalModuleReference(node, + nodeVisitor(node.expression, visitor, isExpression)); // JSX case SyntaxKind.JsxElement: - return factory.updateJsxElement(node, - nodeVisitor((node).openingElement, visitor, isJsxOpeningElement), - nodesVisitor((node).children, visitor, isJsxChild), - nodeVisitor((node).closingElement, visitor, isJsxClosingElement)); + Debug.type(node); + return factory.updateJsxElement(node, + nodeVisitor(node.openingElement, visitor, isJsxOpeningElement), + nodesVisitor(node.children, visitor, isJsxChild), + nodeVisitor(node.closingElement, visitor, isJsxClosingElement)); case SyntaxKind.JsxSelfClosingElement: - return factory.updateJsxSelfClosingElement(node, - nodeVisitor((node).tagName, visitor, isJsxTagNameExpression), - nodesVisitor((node).typeArguments, visitor, isTypeNode), - nodeVisitor((node).attributes, visitor, isJsxAttributes)); + Debug.type(node); + return factory.updateJsxSelfClosingElement(node, + nodeVisitor(node.tagName, visitor, isJsxTagNameExpression), + nodesVisitor(node.typeArguments, visitor, isTypeNode), + nodeVisitor(node.attributes, visitor, isJsxAttributes)); case SyntaxKind.JsxOpeningElement: - return factory.updateJsxOpeningElement(node, - nodeVisitor((node).tagName, visitor, isJsxTagNameExpression), - nodesVisitor((node).typeArguments, visitor, isTypeNode), - nodeVisitor((node).attributes, visitor, isJsxAttributes)); + Debug.type(node); + return factory.updateJsxOpeningElement(node, + nodeVisitor(node.tagName, visitor, isJsxTagNameExpression), + nodesVisitor(node.typeArguments, visitor, isTypeNode), + nodeVisitor(node.attributes, visitor, isJsxAttributes)); case SyntaxKind.JsxClosingElement: - return factory.updateJsxClosingElement(node, - nodeVisitor((node).tagName, visitor, isJsxTagNameExpression)); + Debug.type(node); + return factory.updateJsxClosingElement(node, + nodeVisitor(node.tagName, visitor, isJsxTagNameExpression)); case SyntaxKind.JsxFragment: - return factory.updateJsxFragment(node, - nodeVisitor((node).openingFragment, visitor, isJsxOpeningFragment), - nodesVisitor((node).children, visitor, isJsxChild), - nodeVisitor((node).closingFragment, visitor, isJsxClosingFragment)); + Debug.type(node); + return factory.updateJsxFragment(node, + nodeVisitor(node.openingFragment, visitor, isJsxOpeningFragment), + nodesVisitor(node.children, visitor, isJsxChild), + nodeVisitor(node.closingFragment, visitor, isJsxClosingFragment)); case SyntaxKind.JsxAttribute: - return factory.updateJsxAttribute(node, - nodeVisitor((node).name, visitor, isIdentifier), - nodeVisitor((node).initializer, visitor, isStringLiteralOrJsxExpression)); + Debug.type(node); + return factory.updateJsxAttribute(node, + nodeVisitor(node.name, visitor, isIdentifier), + nodeVisitor(node.initializer, visitor, isStringLiteralOrJsxExpression)); case SyntaxKind.JsxAttributes: - return factory.updateJsxAttributes(node, - nodesVisitor((node).properties, visitor, isJsxAttributeLike)); + Debug.type(node); + return factory.updateJsxAttributes(node, + nodesVisitor(node.properties, visitor, isJsxAttributeLike)); case SyntaxKind.JsxSpreadAttribute: - return factory.updateJsxSpreadAttribute(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateJsxSpreadAttribute(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.JsxExpression: - return factory.updateJsxExpression(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateJsxExpression(node, + nodeVisitor(node.expression, visitor, isExpression)); // Clauses case SyntaxKind.CaseClause: - return factory.updateCaseClause(node, - nodeVisitor((node).expression, visitor, isExpression), - nodesVisitor((node).statements, visitor, isStatement)); + Debug.type(node); + return factory.updateCaseClause(node, + nodeVisitor(node.expression, visitor, isExpression), + nodesVisitor(node.statements, visitor, isStatement)); case SyntaxKind.DefaultClause: - return factory.updateDefaultClause(node, - nodesVisitor((node).statements, visitor, isStatement)); + Debug.type(node); + return factory.updateDefaultClause(node, + nodesVisitor(node.statements, visitor, isStatement)); case SyntaxKind.HeritageClause: - return factory.updateHeritageClause(node, - nodesVisitor((node).types, visitor, isExpressionWithTypeArguments)); + Debug.type(node); + return factory.updateHeritageClause(node, + nodesVisitor(node.types, visitor, isExpressionWithTypeArguments)); case SyntaxKind.CatchClause: - return factory.updateCatchClause(node, - nodeVisitor((node).variableDeclaration, visitor, isVariableDeclaration), - nodeVisitor((node).block, visitor, isBlock)); + Debug.type(node); + return factory.updateCatchClause(node, + nodeVisitor(node.variableDeclaration, visitor, isVariableDeclaration), + nodeVisitor(node.block, visitor, isBlock)); // Property assignments case SyntaxKind.PropertyAssignment: - return factory.updatePropertyAssignment(node, - nodeVisitor((node).name, visitor, isPropertyName), - nodeVisitor((node).initializer, visitor, isExpression)); + Debug.type(node); + return factory.updatePropertyAssignment(node, + nodeVisitor(node.name, visitor, isPropertyName), + nodeVisitor(node.initializer, visitor, isExpression)); case SyntaxKind.ShorthandPropertyAssignment: - return factory.updateShorthandPropertyAssignment(node, - nodeVisitor((node).name, visitor, isIdentifier), - nodeVisitor((node).objectAssignmentInitializer, visitor, isExpression)); + Debug.type(node); + return factory.updateShorthandPropertyAssignment(node, + nodeVisitor(node.name, visitor, isIdentifier), + nodeVisitor(node.objectAssignmentInitializer, visitor, isExpression)); case SyntaxKind.SpreadAssignment: - return factory.updateSpreadAssignment(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updateSpreadAssignment(node, + nodeVisitor(node.expression, visitor, isExpression)); // Enum case SyntaxKind.EnumMember: - return factory.updateEnumMember(node, - nodeVisitor((node).name, visitor, isPropertyName), - nodeVisitor((node).initializer, visitor, isExpression)); + Debug.type(node); + return factory.updateEnumMember(node, + nodeVisitor(node.name, visitor, isPropertyName), + nodeVisitor(node.initializer, visitor, isExpression)); // Top-level nodes case SyntaxKind.SourceFile: - return factory.updateSourceFile(node, - visitLexicalEnvironment((node).statements, visitor, context)); + Debug.type(node); + return factory.updateSourceFile(node, + visitLexicalEnvironment(node.statements, visitor, context)); // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: - return factory.updatePartiallyEmittedExpression(node, - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updatePartiallyEmittedExpression(node, + nodeVisitor(node.expression, visitor, isExpression)); case SyntaxKind.CommaListExpression: - return factory.updateCommaListExpression(node, - nodesVisitor((node).elements, visitor, isExpression)); + Debug.type(node); + return factory.updateCommaListExpression(node, + nodesVisitor(node.elements, visitor, isExpression)); default: // No need to visit nodes with no children. diff --git a/src/deprecatedCompat/deprecations.ts b/src/deprecatedCompat/deprecations.ts index b0573ad4ab75d..c04e34e8b1649 100644 --- a/src/deprecatedCompat/deprecations.ts +++ b/src/deprecatedCompat/deprecations.ts @@ -1354,4 +1354,24 @@ namespace ts { export interface Map extends ESMap { } // #endregion + + // DEPRECATION: Renamed node tests + // DEPRECATION PLAN: + // - soft: 4.2 + // - warn: 4.3 + // - error: TBD + // #region Renamed node Tests + + /** + * @deprecated Use `isMemberName` instead. + */ + export const isIdentifierOrPrivateIdentifier = Debug.deprecate(function isIdentifierOrPrivateIdentifier(node: Node): node is MemberName { + return isMemberName(node); + }, { + since: "4.2", + warnAfter: "4.3", + message: "Use `isMemberName` instead." + }); + + // #endregion Renamed node Tests } \ No newline at end of file diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 78d748589e837..ed748ad29c825 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -46,7 +46,7 @@ namespace ts.codefix { let suggestedSymbol: Symbol | undefined; if (isPropertyAccessExpression(parent) && parent.name === node) { - Debug.assert(isIdentifierOrPrivateIdentifier(node), "Expected an identifier for spelling (property access)"); + Debug.assert(isMemberName(node), "Expected an identifier for spelling (property access)"); let containingType = checker.getTypeAtLocation(parent.expression); if (parent.flags & NodeFlags.OptionalChain) { containingType = checker.getNonNullableType(containingType); diff --git a/src/services/completions.ts b/src/services/completions.ts index a3c957442cb0f..94cab4f3c8d17 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -941,7 +941,7 @@ namespace ts.Completions { // Check if the caret is at the end of an identifier; this is a partial identifier that we want to complete: e.g. a.toS| // Skip this partial identifier and adjust the contextToken to the token that precedes it. - if (contextToken && position <= contextToken.end && (isIdentifierOrPrivateIdentifier(contextToken) || isKeyword(contextToken.kind))) { + if (contextToken && position <= contextToken.end && (isMemberName(contextToken) || isKeyword(contextToken.kind))) { const start = timestamp(); contextToken = findPrecedingToken(contextToken.getFullStart(), sourceFile, /*startNode*/ undefined)!; // TODO: GH#18217 log("getCompletionData: Get previous token 2: " + (timestamp() - start)); diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 566c4c8c0f272..d5180d2fb28de 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -1037,11 +1037,12 @@ namespace ts.textChanges { let lastNonTriviaPosition = 0; const writer = createTextWriter(newLine); - const onEmitNode: PrintHandlers["onEmitNode"] = (hint, node, printCallback) => { + const onBeforeEmitNode: PrintHandlers["onBeforeEmitNode"] = node => { if (node) { setPos(node, lastNonTriviaPosition); } - printCallback(hint, node); + }; + const onAfterEmitNode: PrintHandlers["onAfterEmitNode"] = node => { if (node) { setEnd(node, lastNonTriviaPosition); } @@ -1163,7 +1164,8 @@ namespace ts.textChanges { } return { - onEmitNode, + onBeforeEmitNode, + onAfterEmitNode, onBeforeEmitNodeArray, onAfterEmitNodeArray, onBeforeEmitToken, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index be1c47fd86a47..ed2f8a371a64a 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -638,6 +638,7 @@ declare namespace ts { } export type EntityName = Identifier | QualifiedName; export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier; + export type MemberName = Identifier | PrivateIdentifier; export type DeclarationName = Identifier | PrivateIdentifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | ElementAccessExpression | BindingPattern | EntityNameExpression; export interface Declaration extends Node { _declarationBrand: any; @@ -1216,11 +1217,11 @@ declare namespace ts { readonly kind: SyntaxKind.PropertyAccessExpression; readonly expression: LeftHandSideExpression; readonly questionDotToken?: QuestionDotToken; - readonly name: Identifier | PrivateIdentifier; + readonly name: MemberName; } export interface PropertyAccessChain extends PropertyAccessExpression { _optionalChainBrand: any; - readonly name: Identifier | PrivateIdentifier; + readonly name: MemberName; } export interface SuperPropertyAccessExpression extends PropertyAccessExpression { readonly expression: SuperExpression; @@ -3128,17 +3129,21 @@ declare namespace ts { Iterator = 8388608, NoAsciiEscaping = 16777216, } - export interface EmitHelper { + export interface EmitHelperBase { readonly name: string; readonly scoped: boolean; readonly text: string | ((node: EmitHelperUniqueNameCallback) => string); readonly priority?: number; readonly dependencies?: EmitHelper[]; } - export interface UnscopedEmitHelper extends EmitHelper { + export interface ScopedEmitHelper extends EmitHelperBase { + readonly scoped: true; + } + export interface UnscopedEmitHelper extends EmitHelperBase { readonly scoped: false; readonly text: string; } + export type EmitHelper = ScopedEmitHelper | UnscopedEmitHelper; export type EmitHelperUniqueNameCallback = (name: string) => string; export enum EmitHint { SourceFile = 0, @@ -3284,10 +3289,10 @@ declare namespace ts { updateArrayLiteralExpression(node: ArrayLiteralExpression, elements: readonly Expression[]): ArrayLiteralExpression; createObjectLiteralExpression(properties?: readonly ObjectLiteralElementLike[], multiLine?: boolean): ObjectLiteralExpression; updateObjectLiteralExpression(node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]): ObjectLiteralExpression; - createPropertyAccessExpression(expression: Expression, name: string | Identifier | PrivateIdentifier): PropertyAccessExpression; - updatePropertyAccessExpression(node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateIdentifier): PropertyAccessExpression; - createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier | PrivateIdentifier): PropertyAccessChain; - updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier | PrivateIdentifier): PropertyAccessChain; + createPropertyAccessExpression(expression: Expression, name: string | MemberName): PropertyAccessExpression; + updatePropertyAccessExpression(node: PropertyAccessExpression, expression: Expression, name: MemberName): PropertyAccessExpression; + createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | MemberName): PropertyAccessChain; + updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName): PropertyAccessChain; createElementAccessExpression(expression: Expression, index: number | Expression): ElementAccessExpression; updateElementAccessExpression(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression): ElementAccessExpression; createElementAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, index: number | Expression): ElementAccessChain; @@ -3748,12 +3753,12 @@ declare namespace ts { * }); * ``` */ - onEmitNode?(hint: EmitHint, node: Node | undefined, emitCallback: (hint: EmitHint, node: Node | undefined) => void): void; + onEmitNode?(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; /** * A hook used to check if an emit notification is required for a node. * @param node The node to emit. */ - isEmitNotificationEnabled?(node: Node | undefined): boolean; + isEmitNotificationEnabled?(node: Node): boolean; /** * A hook used by the Printer to perform just-in-time substitution of a node. This is * primarily used by node transformations that need to substitute one node for another, @@ -4189,7 +4194,7 @@ declare namespace ts { */ function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[]; function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined; - function isIdentifierOrPrivateIdentifier(node: Node): node is Identifier | PrivateIdentifier; + function isMemberName(node: Node): node is MemberName; function isPropertyAccessChain(node: Node): node is PropertyAccessChain; function isElementAccessChain(node: Node): node is ElementAccessChain; function isCallChain(node: Node): node is CallChain; @@ -4204,6 +4209,12 @@ declare namespace ts { function isUnparsedTextLike(node: Node): node is UnparsedTextLike; function isUnparsedNode(node: Node): node is UnparsedNode; function isJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag; + /** + * True if kind is of some token syntax kind. + * For example, this is true for an IfKeyword but not for an IfStatement. + * Literals are considered tokens, except TemplateLiteral, but does include TemplateHead/Middle/Tail. + */ + function isTokenKind(kind: SyntaxKind): boolean; /** * True if node is of some token syntax kind. * For example, this is true for an IfKeyword but not for an IfStatement. @@ -4346,10 +4357,14 @@ declare namespace ts { function isTemplateHead(node: Node): node is TemplateHead; function isTemplateMiddle(node: Node): node is TemplateMiddle; function isTemplateTail(node: Node): node is TemplateTail; + function isDotDotDotToken(node: Node): node is DotDotDotToken; + function isPlusToken(node: Node): node is PlusToken; + function isMinusToken(node: Node): node is MinusToken; + function isAsteriskToken(node: Node): node is AsteriskToken; function isIdentifier(node: Node): node is Identifier; + function isPrivateIdentifier(node: Node): node is PrivateIdentifier; function isQualifiedName(node: Node): node is QualifiedName; function isComputedPropertyName(node: Node): node is ComputedPropertyName; - function isPrivateIdentifier(node: Node): node is PrivateIdentifier; function isTypeParameterDeclaration(node: Node): node is TypeParameterDeclaration; function isParameter(node: Node): node is ParameterDeclaration; function isDecorator(node: Node): node is Decorator; @@ -10306,13 +10321,13 @@ declare namespace ts { /** @deprecated Use `factory.updateObjectLiteralExpression` or the factory supplied by your transformation context instead. */ const updateObjectLiteral: (node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]) => ObjectLiteralExpression; /** @deprecated Use `factory.createPropertyAccessExpression` or the factory supplied by your transformation context instead. */ - const createPropertyAccess: (expression: Expression, name: string | Identifier | PrivateIdentifier) => PropertyAccessExpression; + const createPropertyAccess: (expression: Expression, name: string | MemberName) => PropertyAccessExpression; /** @deprecated Use `factory.updatePropertyAccessExpression` or the factory supplied by your transformation context instead. */ - const updatePropertyAccess: (node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateIdentifier) => PropertyAccessExpression; + const updatePropertyAccess: (node: PropertyAccessExpression, expression: Expression, name: MemberName) => PropertyAccessExpression; /** @deprecated Use `factory.createPropertyAccessChain` or the factory supplied by your transformation context instead. */ - const createPropertyAccessChain: (expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier | PrivateIdentifier) => PropertyAccessChain; + const createPropertyAccessChain: (expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | MemberName) => PropertyAccessChain; /** @deprecated Use `factory.updatePropertyAccessChain` or the factory supplied by your transformation context instead. */ - const updatePropertyAccessChain: (node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier | PrivateIdentifier) => PropertyAccessChain; + const updatePropertyAccessChain: (node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName) => PropertyAccessChain; /** @deprecated Use `factory.createElementAccessExpression` or the factory supplied by your transformation context instead. */ const createElementAccess: (expression: Expression, index: number | Expression) => ElementAccessExpression; /** @deprecated Use `factory.updateElementAccessExpression` or the factory supplied by your transformation context instead. */ @@ -10882,6 +10897,10 @@ declare namespace ts { */ interface Map extends ESMap { } + /** + * @deprecated Use `isMemberName` instead. + */ + const isIdentifierOrPrivateIdentifier: (node: Node) => node is MemberName; } export = ts; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index bba168d37e720..1542db06ebb63 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -638,6 +638,7 @@ declare namespace ts { } export type EntityName = Identifier | QualifiedName; export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier; + export type MemberName = Identifier | PrivateIdentifier; export type DeclarationName = Identifier | PrivateIdentifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | ElementAccessExpression | BindingPattern | EntityNameExpression; export interface Declaration extends Node { _declarationBrand: any; @@ -1216,11 +1217,11 @@ declare namespace ts { readonly kind: SyntaxKind.PropertyAccessExpression; readonly expression: LeftHandSideExpression; readonly questionDotToken?: QuestionDotToken; - readonly name: Identifier | PrivateIdentifier; + readonly name: MemberName; } export interface PropertyAccessChain extends PropertyAccessExpression { _optionalChainBrand: any; - readonly name: Identifier | PrivateIdentifier; + readonly name: MemberName; } export interface SuperPropertyAccessExpression extends PropertyAccessExpression { readonly expression: SuperExpression; @@ -3128,17 +3129,21 @@ declare namespace ts { Iterator = 8388608, NoAsciiEscaping = 16777216, } - export interface EmitHelper { + export interface EmitHelperBase { readonly name: string; readonly scoped: boolean; readonly text: string | ((node: EmitHelperUniqueNameCallback) => string); readonly priority?: number; readonly dependencies?: EmitHelper[]; } - export interface UnscopedEmitHelper extends EmitHelper { + export interface ScopedEmitHelper extends EmitHelperBase { + readonly scoped: true; + } + export interface UnscopedEmitHelper extends EmitHelperBase { readonly scoped: false; readonly text: string; } + export type EmitHelper = ScopedEmitHelper | UnscopedEmitHelper; export type EmitHelperUniqueNameCallback = (name: string) => string; export enum EmitHint { SourceFile = 0, @@ -3284,10 +3289,10 @@ declare namespace ts { updateArrayLiteralExpression(node: ArrayLiteralExpression, elements: readonly Expression[]): ArrayLiteralExpression; createObjectLiteralExpression(properties?: readonly ObjectLiteralElementLike[], multiLine?: boolean): ObjectLiteralExpression; updateObjectLiteralExpression(node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]): ObjectLiteralExpression; - createPropertyAccessExpression(expression: Expression, name: string | Identifier | PrivateIdentifier): PropertyAccessExpression; - updatePropertyAccessExpression(node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateIdentifier): PropertyAccessExpression; - createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier | PrivateIdentifier): PropertyAccessChain; - updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier | PrivateIdentifier): PropertyAccessChain; + createPropertyAccessExpression(expression: Expression, name: string | MemberName): PropertyAccessExpression; + updatePropertyAccessExpression(node: PropertyAccessExpression, expression: Expression, name: MemberName): PropertyAccessExpression; + createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | MemberName): PropertyAccessChain; + updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName): PropertyAccessChain; createElementAccessExpression(expression: Expression, index: number | Expression): ElementAccessExpression; updateElementAccessExpression(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression): ElementAccessExpression; createElementAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, index: number | Expression): ElementAccessChain; @@ -3748,12 +3753,12 @@ declare namespace ts { * }); * ``` */ - onEmitNode?(hint: EmitHint, node: Node | undefined, emitCallback: (hint: EmitHint, node: Node | undefined) => void): void; + onEmitNode?(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; /** * A hook used to check if an emit notification is required for a node. * @param node The node to emit. */ - isEmitNotificationEnabled?(node: Node | undefined): boolean; + isEmitNotificationEnabled?(node: Node): boolean; /** * A hook used by the Printer to perform just-in-time substitution of a node. This is * primarily used by node transformations that need to substitute one node for another, @@ -4189,7 +4194,7 @@ declare namespace ts { */ function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[]; function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined; - function isIdentifierOrPrivateIdentifier(node: Node): node is Identifier | PrivateIdentifier; + function isMemberName(node: Node): node is MemberName; function isPropertyAccessChain(node: Node): node is PropertyAccessChain; function isElementAccessChain(node: Node): node is ElementAccessChain; function isCallChain(node: Node): node is CallChain; @@ -4204,6 +4209,12 @@ declare namespace ts { function isUnparsedTextLike(node: Node): node is UnparsedTextLike; function isUnparsedNode(node: Node): node is UnparsedNode; function isJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag; + /** + * True if kind is of some token syntax kind. + * For example, this is true for an IfKeyword but not for an IfStatement. + * Literals are considered tokens, except TemplateLiteral, but does include TemplateHead/Middle/Tail. + */ + function isTokenKind(kind: SyntaxKind): boolean; /** * True if node is of some token syntax kind. * For example, this is true for an IfKeyword but not for an IfStatement. @@ -4346,10 +4357,14 @@ declare namespace ts { function isTemplateHead(node: Node): node is TemplateHead; function isTemplateMiddle(node: Node): node is TemplateMiddle; function isTemplateTail(node: Node): node is TemplateTail; + function isDotDotDotToken(node: Node): node is DotDotDotToken; + function isPlusToken(node: Node): node is PlusToken; + function isMinusToken(node: Node): node is MinusToken; + function isAsteriskToken(node: Node): node is AsteriskToken; function isIdentifier(node: Node): node is Identifier; + function isPrivateIdentifier(node: Node): node is PrivateIdentifier; function isQualifiedName(node: Node): node is QualifiedName; function isComputedPropertyName(node: Node): node is ComputedPropertyName; - function isPrivateIdentifier(node: Node): node is PrivateIdentifier; function isTypeParameterDeclaration(node: Node): node is TypeParameterDeclaration; function isParameter(node: Node): node is ParameterDeclaration; function isDecorator(node: Node): node is Decorator; @@ -6641,13 +6656,13 @@ declare namespace ts { /** @deprecated Use `factory.updateObjectLiteralExpression` or the factory supplied by your transformation context instead. */ const updateObjectLiteral: (node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]) => ObjectLiteralExpression; /** @deprecated Use `factory.createPropertyAccessExpression` or the factory supplied by your transformation context instead. */ - const createPropertyAccess: (expression: Expression, name: string | Identifier | PrivateIdentifier) => PropertyAccessExpression; + const createPropertyAccess: (expression: Expression, name: string | MemberName) => PropertyAccessExpression; /** @deprecated Use `factory.updatePropertyAccessExpression` or the factory supplied by your transformation context instead. */ - const updatePropertyAccess: (node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateIdentifier) => PropertyAccessExpression; + const updatePropertyAccess: (node: PropertyAccessExpression, expression: Expression, name: MemberName) => PropertyAccessExpression; /** @deprecated Use `factory.createPropertyAccessChain` or the factory supplied by your transformation context instead. */ - const createPropertyAccessChain: (expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier | PrivateIdentifier) => PropertyAccessChain; + const createPropertyAccessChain: (expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | MemberName) => PropertyAccessChain; /** @deprecated Use `factory.updatePropertyAccessChain` or the factory supplied by your transformation context instead. */ - const updatePropertyAccessChain: (node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier | PrivateIdentifier) => PropertyAccessChain; + const updatePropertyAccessChain: (node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName) => PropertyAccessChain; /** @deprecated Use `factory.createElementAccessExpression` or the factory supplied by your transformation context instead. */ const createElementAccess: (expression: Expression, index: number | Expression) => ElementAccessExpression; /** @deprecated Use `factory.updateElementAccessExpression` or the factory supplied by your transformation context instead. */ @@ -7217,6 +7232,10 @@ declare namespace ts { */ interface Map extends ESMap { } + /** + * @deprecated Use `isMemberName` instead. + */ + const isIdentifierOrPrivateIdentifier: (node: Node) => node is MemberName; } export = ts; \ No newline at end of file diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js index 18433ac6885e4..b57fce233b65f 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -32,6 +32,6 @@ exports.buzz = buzz; exports.buzz = buzz += 3; var bizz = 8; exports.bizz = bizz; -(exports.bizz = bizz += 1); // compiles to exports.bizz = bizz += 1 -(exports.bizz = bizz -= 1); // similarly -(exports.bizz = ++bizz); // compiles to exports.bizz = ++bizz +(exports.bizz = ++bizz) - 1; // compiles to exports.bizz = bizz += 1 +(exports.bizz = --bizz) + 1; // similarly +exports.bizz = ++bizz; // compiles to exports.bizz = ++bizz diff --git a/tests/baselines/reference/importMeta(module=commonjs,target=es5).js b/tests/baselines/reference/importMeta(module=commonjs,target=es5).js index c9ddd9d96e62d..c6bc5cf86f380 100644 --- a/tests/baselines/reference/importMeta(module=commonjs,target=es5).js +++ b/tests/baselines/reference/importMeta(module=commonjs,target=es5).js @@ -101,8 +101,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.z = exports.y = exports.x = void 0; -exports.x = (import.meta); -exports.y = (import.metal); +exports.x = import.meta; +exports.y = import.metal; exports.z = import.import.import.malkovich; //// [scriptLookingFile01.js] "use strict"; diff --git a/tests/baselines/reference/importMeta(module=commonjs,target=esnext).js b/tests/baselines/reference/importMeta(module=commonjs,target=esnext).js index f918b0480e27e..507aff838e989 100644 --- a/tests/baselines/reference/importMeta(module=commonjs,target=esnext).js +++ b/tests/baselines/reference/importMeta(module=commonjs,target=esnext).js @@ -55,8 +55,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.z = exports.y = exports.x = void 0; -exports.x = (import.meta); -exports.y = (import.metal); +exports.x = import.meta; +exports.y = import.metal; exports.z = import.import.import.malkovich; //// [scriptLookingFile01.js] "use strict"; diff --git a/tests/baselines/reference/importMeta(module=system,target=es5).js b/tests/baselines/reference/importMeta(module=system,target=es5).js index 3eda4156d8b21..e68407acd99b9 100644 --- a/tests/baselines/reference/importMeta(module=system,target=es5).js +++ b/tests/baselines/reference/importMeta(module=system,target=es5).js @@ -112,8 +112,8 @@ System.register([], function (exports_1, context_1) { return { setters: [], execute: function () { - exports_1("x", x = (context_1.meta)); - exports_1("y", y = (import.metal)); + exports_1("x", x = context_1.meta); + exports_1("y", y = import.metal); exports_1("z", z = import.import.import.malkovich); } }; @@ -126,8 +126,8 @@ System.register([], function (exports_1, context_1) { return { setters: [], execute: function () { - globalA = (context_1.meta); - globalB = (import.metal); + globalA = context_1.meta; + globalB = import.metal; globalC = import.import.import.malkovich; } }; diff --git a/tests/baselines/reference/importMeta(module=system,target=esnext).js b/tests/baselines/reference/importMeta(module=system,target=esnext).js index b3639b579a871..1510c190b2969 100644 --- a/tests/baselines/reference/importMeta(module=system,target=esnext).js +++ b/tests/baselines/reference/importMeta(module=system,target=esnext).js @@ -66,8 +66,8 @@ System.register([], function (exports_1, context_1) { return { setters: [], execute: function () { - exports_1("x", x = (context_1.meta)); - exports_1("y", y = (import.metal)); + exports_1("x", x = context_1.meta); + exports_1("y", y = import.metal); exports_1("z", z = import.import.import.malkovich); } }; @@ -80,8 +80,8 @@ System.register([], function (exports_1, context_1) { return { setters: [], execute: function () { - globalA = (context_1.meta); - globalB = (import.metal); + globalA = context_1.meta; + globalB = import.metal; globalC = import.import.import.malkovich; } }; diff --git a/tests/baselines/reference/inlineJsxFactoryDeclarationsLocalTypes.js b/tests/baselines/reference/inlineJsxFactoryDeclarationsLocalTypes.js index 7b29740e7f9d6..6621386076f09 100644 --- a/tests/baselines/reference/inlineJsxFactoryDeclarationsLocalTypes.js +++ b/tests/baselines/reference/inlineJsxFactoryDeclarationsLocalTypes.js @@ -161,6 +161,4 @@ var _brokenTree = renderer_1.dom(component_1.MySFC, { x: 1, y: 2 }, renderer_1.dom(component_1.MyClass, { x: 3, y: 4 }), renderer_1.dom(component_1.MyClass, { x: 5, y: 6 })); // Should fail, nondom isn't allowed as children of dom -var _brokenTree2 = renderer_1.dom(DOMSFC, { x: 1, y: 2 }, - component_1.tree, - component_1.tree); +var _brokenTree2 = renderer_1.dom(DOMSFC, { x: 1, y: 2 }, component_1.tree, component_1.tree); diff --git a/tests/baselines/reference/jsxCheckJsxNoTypeArgumentsAllowed.js b/tests/baselines/reference/jsxCheckJsxNoTypeArgumentsAllowed.js index a1a7692ca7655..8eb7b03f2b7ac 100644 --- a/tests/baselines/reference/jsxCheckJsxNoTypeArgumentsAllowed.js +++ b/tests/baselines/reference/jsxCheckJsxNoTypeArgumentsAllowed.js @@ -23,5 +23,5 @@ let x = a={10} b="hi" />; // error, no type arguments in js exports.__esModule = true; var component_1 = require("./component"); var React = require("react"); -var x = , a={10} b="hi" />; // error, no type arguments in js -; +var x = (, a={10} b="hi" />; // error, no type arguments in js +); diff --git a/tests/baselines/reference/moduleExportsUnaryExpression.js b/tests/baselines/reference/moduleExportsUnaryExpression.js index b3956233ed4dc..cb11efbd375b3 100644 --- a/tests/baselines/reference/moduleExportsUnaryExpression.js +++ b/tests/baselines/reference/moduleExportsUnaryExpression.js @@ -23,17 +23,17 @@ exports.x = exports.foo = void 0; var x = 1; exports.x = x; function foo(y) { - if (y <= (exports.x = x += 1)) - return y <= (exports.x = x += 1); - if (y <= (exports.x = x -= 1)) - return y <= (exports.x = x -= 1); + if (y <= (exports.x = ++x) - 1) + return y <= (exports.x = ++x) - 1; + if (y <= (exports.x = --x) + 1) + return y <= (exports.x = --x) + 1; if (y <= (exports.x = ++x)) return y <= (exports.x = ++x); if (y <= (exports.x = --x)) return y <= (exports.x = --x); - (exports.x = x += 1); - (exports.x = x -= 1); - (exports.x = ++x); - (exports.x = --x); + (exports.x = ++x) - 1; + (exports.x = --x) + 1; + exports.x = ++x; + exports.x = --x; } exports.foo = foo; diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.js b/tests/baselines/reference/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.js index 33ae1f6c8f0ee..e0aecb5f18814 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.js +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.js @@ -35,8 +35,7 @@ var y = { "typeof": }; var x = (_a = { - a: a, - : .b, + a: a, : .b, a: a }, _a["ss"] = , diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesErrorWithModule.js b/tests/baselines/reference/objectLiteralShorthandPropertiesErrorWithModule.js index f48a6c3a4e5d6..ee46d4741dd15 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesErrorWithModule.js +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesErrorWithModule.js @@ -25,8 +25,7 @@ var n; (function (n) { var z = 10000; n.y = { - m: m, - : .x // error + m: m, : .x // error }; })(n || (n = {})); m.y.x; diff --git a/tests/baselines/reference/objectTypesWithOptionalProperties2.js b/tests/baselines/reference/objectTypesWithOptionalProperties2.js index 284ecdea92ca7..03d2b162847ea 100644 --- a/tests/baselines/reference/objectTypesWithOptionalProperties2.js +++ b/tests/baselines/reference/objectTypesWithOptionalProperties2.js @@ -42,6 +42,5 @@ var C2 = /** @class */ (function () { return C2; }()); var b = { - x: function () { }, - 1: // error + x: function () { }, 1: // error }; diff --git a/tests/baselines/reference/parserErrorRecovery_ObjectLiteral2.js b/tests/baselines/reference/parserErrorRecovery_ObjectLiteral2.js index 6d723a00d4a9d..1cdf73ddadcb3 100644 --- a/tests/baselines/reference/parserErrorRecovery_ObjectLiteral2.js +++ b/tests/baselines/reference/parserErrorRecovery_ObjectLiteral2.js @@ -3,4 +3,5 @@ var v = { a return; //// [parserErrorRecovery_ObjectLiteral2.js] -var v = { a: a, "return": }; +var v = { a: a, + "return": };