/// /// /// /// /// namespace ts { const delimiters = createDelimiterMap(); const brackets = createBracketsMap(); /*@internal*/ /** * Iterates over the source files that are expected to have an emit output. * * @param host An EmitHost. * @param action The action to execute. * @param sourceFilesOrTargetSourceFile * If an array, the full list of source files to emit. * Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit. */ export function forEachEmittedFile( host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => void, sourceFilesOrTargetSourceFile?: SourceFile[] | SourceFile, emitOnlyDtsFiles?: boolean) { const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile); const options = host.getCompilerOptions(); if (options.outFile || options.out) { if (sourceFiles.length) { const jsFilePath = options.outFile || options.out; const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + Extension.Dts : ""; action({ jsFilePath, sourceMapFilePath, declarationFilePath }, createBundle(sourceFiles), emitOnlyDtsFiles); } } else { for (const sourceFile of sourceFiles) { const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFile, emitOnlyDtsFiles); } } } function getSourceMapFilePath(jsFilePath: string, options: CompilerOptions) { return options.sourceMap ? jsFilePath + ".map" : undefined; } // JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also. // So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve. // For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve function getOutputExtension(sourceFile: SourceFile, options: CompilerOptions): Extension { if (options.jsx === JsxEmit.Preserve) { if (isSourceFileJavaScript(sourceFile)) { if (fileExtensionIs(sourceFile.fileName, Extension.Jsx)) { return Extension.Jsx; } } else if (sourceFile.languageVariant === LanguageVariant.JSX) { // TypeScript source file preserving JSX syntax return Extension.Jsx; } } return Extension.Js; } function getOriginalSourceFileOrBundle(sourceFileOrBundle: SourceFile | Bundle) { if (sourceFileOrBundle.kind === SyntaxKind.Bundle) { return updateBundle(sourceFileOrBundle, sameMap(sourceFileOrBundle.sourceFiles, getOriginalSourceFile)); } return getOriginalSourceFile(sourceFileOrBundle); } /*@internal*/ // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[]): EmitResult { const compilerOptions = host.getCompilerOptions(); const moduleKind = getEmitModuleKind(compilerOptions); const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined; const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined; const emitterDiagnostics = createDiagnosticCollection(); const newLine = host.getNewLine(); const writer = createTextWriter(newLine); const sourceMap = createSourceMapWriter(host, writer); let currentSourceFile: SourceFile; let bundledHelpers: Map; let isOwnFileEmit: boolean; let emitSkipped = false; const sourceFiles = getSourceFilesToEmit(host, targetSourceFile); // Transform the source files const transform = transformNodes(resolver, host, compilerOptions, sourceFiles, transformers, /*allowDtsFiles*/ false); // Create a printer to print the nodes const printer = createPrinter(compilerOptions, { // resolver hooks hasGlobalName: resolver.hasGlobalName, // transform hooks onEmitNode: transform.emitNodeWithNotification, substituteNode: transform.substituteNode, // sourcemap hooks onEmitSourceMapOfNode: sourceMap.emitNodeWithSourceMap, onEmitSourceMapOfToken: sourceMap.emitTokenWithSourceMap, onEmitSourceMapOfPosition: sourceMap.emitPos, // emitter hooks onEmitHelpers: emitHelpers, onSetSourceFile: setSourceFile, }); // Emit each output file performance.mark("beforePrint"); forEachEmittedFile(host, emitSourceFileOrBundle, transform.transformed, emitOnlyDtsFiles); performance.measure("printTime", "beforePrint"); // Clean up emit nodes on parse tree transform.dispose(); return { emitSkipped, diagnostics: emitterDiagnostics.getDiagnostics(), emittedFiles: emittedFilesList, sourceMaps: sourceMapDataList }; function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { // Make sure not to write js file and source map file if any of them cannot be written if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) { if (!emitOnlyDtsFiles) { printSourceFileOrBundle(jsFilePath, sourceMapFilePath, sourceFileOrBundle); } } else { emitSkipped = true; } if (declarationFilePath) { emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFileOrBundle(sourceFileOrBundle), host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped; } if (!emitSkipped && emittedFilesList) { if (!emitOnlyDtsFiles) { emittedFilesList.push(jsFilePath); } if (sourceMapFilePath) { emittedFilesList.push(sourceMapFilePath); } if (declarationFilePath) { emittedFilesList.push(declarationFilePath); } } } function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle) { const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined; const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined; const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile]; sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFileOrBundle); if (bundle) { bundledHelpers = createMap(); isOwnFileEmit = false; printer.writeBundle(bundle, writer); } else { isOwnFileEmit = true; printer.writeFile(sourceFile, writer); } writer.writeLine(); const sourceMappingURL = sourceMap.getSourceMappingURL(); if (sourceMappingURL) { writer.write(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Sometimes tools can sometimes see this line as a source mapping url comment } // Write the source map if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) { writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false, sourceFiles); } // Record source map data for the test harness. if (sourceMapDataList) { sourceMapDataList.push(sourceMap.getSourceMapData()); } // Write the output file writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM, sourceFiles); // Reset state sourceMap.reset(); writer.reset(); currentSourceFile = undefined; bundledHelpers = undefined; isOwnFileEmit = false; } function setSourceFile(node: SourceFile) { currentSourceFile = node; sourceMap.setSourceFile(node); } function emitHelpers(node: Node, writeLines: (text: string) => void) { let helpersEmitted = false; const bundle = node.kind === SyntaxKind.Bundle ? node : undefined; if (bundle && moduleKind === ModuleKind.None) { return; } const numNodes = bundle ? bundle.sourceFiles.length : 1; for (let i = 0; i < numNodes; i++) { const currentNode = bundle ? bundle.sourceFiles[i] : node; const sourceFile = isSourceFile(currentNode) ? currentNode : currentSourceFile; const shouldSkip = compilerOptions.noEmitHelpers || getExternalHelpersModuleName(sourceFile) !== undefined; const shouldBundle = isSourceFile(currentNode) && !isOwnFileEmit; const helpers = getEmitHelpers(currentNode); if (helpers) { for (const helper of stableSort(helpers, compareEmitHelpers)) { 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; } writeLines(helper.text); helpersEmitted = true; } } } return helpersEmitted; } } export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { const { hasGlobalName, onEmitSourceMapOfNode, onEmitSourceMapOfToken, onEmitSourceMapOfPosition, onEmitNode, onEmitHelpers, onSetSourceFile, substituteNode, onBeforeEmitNodeArray, onAfterEmitNodeArray, onBeforeEmitToken, onAfterEmitToken } = handlers; const newLine = getNewLineCharacter(printerOptions); const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition); const { emitNodeWithComments, emitBodyWithDetachedComments, emitTrailingCommentsOfPosition, emitLeadingCommentsOfPosition, } = comments; let currentSourceFile: SourceFile | undefined; let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes. let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables. let generatedNames: Map; // Set of names generated by the NameGenerator. let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes. let tempFlags: TempFlags; // TempFlags for the current name generation scope. let writer: EmitTextWriter; let ownWriter: EmitTextWriter; reset(); return { // public API printNode, printFile, printBundle, // internal API writeNode, writeFile, writeBundle }; function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { switch (hint) { case EmitHint.SourceFile: Debug.assert(isSourceFile(node), "Expected a SourceFile node."); break; case EmitHint.IdentifierName: Debug.assert(isIdentifier(node), "Expected an Identifier node."); break; case EmitHint.Expression: Debug.assert(isExpression(node), "Expected an Expression node."); break; } switch (node.kind) { case SyntaxKind.SourceFile: return printFile(node); case SyntaxKind.Bundle: return printBundle(node); } writeNode(hint, node, sourceFile, beginPrint()); return endPrint(); } function printBundle(bundle: Bundle): string { writeBundle(bundle, beginPrint()); return endPrint(); } function printFile(sourceFile: SourceFile): string { writeFile(sourceFile, beginPrint()); return endPrint(); } /** * If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`. */ function writeNode(hint: EmitHint, node: TypeNode, sourceFile: undefined, output: EmitTextWriter): void; function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter): void; function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); print(hint, node, sourceFile); reset(); writer = previousWriter; } function writeBundle(bundle: Bundle, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); emitShebangIfNeeded(bundle); emitPrologueDirectivesIfNeeded(bundle); emitHelpersIndirect(bundle); for (const sourceFile of bundle.sourceFiles) { print(EmitHint.SourceFile, sourceFile, sourceFile); } reset(); writer = previousWriter; } function writeFile(sourceFile: SourceFile, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); emitShebangIfNeeded(sourceFile); emitPrologueDirectivesIfNeeded(sourceFile); print(EmitHint.SourceFile, sourceFile, sourceFile); reset(); writer = previousWriter; } function beginPrint() { return ownWriter || (ownWriter = createTextWriter(newLine)); } function endPrint() { const text = ownWriter.getText(); ownWriter.reset(); return text; } function print(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined) { if (sourceFile) { setSourceFile(sourceFile); } pipelineEmitWithNotification(hint, node); } function setSourceFile(sourceFile: SourceFile) { currentSourceFile = sourceFile; comments.setSourceFile(sourceFile); if (onSetSourceFile) { onSetSourceFile(sourceFile); } } function setWriter(output: EmitTextWriter | undefined) { writer = output; comments.setWriter(output); } function reset() { nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; generatedNames = createMap(); tempFlagsStack = []; tempFlags = TempFlags.Auto; comments.reset(); setWriter(/*output*/ undefined); } // TODO: Should this just be `emit`? // See https://github.com/Microsoft/TypeScript/pull/18284#discussion_r137611034 function emitIfPresent(node: Node | undefined) { if (node) { emit(node); } } function emit(node: Node) { pipelineEmitWithNotification(EmitHint.Unspecified, node); } function emitIdentifierName(node: Identifier) { pipelineEmitWithNotification(EmitHint.IdentifierName, node); } function emitExpression(node: Expression) { pipelineEmitWithNotification(EmitHint.Expression, node); } function pipelineEmitWithNotification(hint: EmitHint, node: Node) { if (onEmitNode) { onEmitNode(hint, node, pipelineEmitWithComments); } else { pipelineEmitWithComments(hint, node); } } function pipelineEmitWithComments(hint: EmitHint, node: Node) { node = trySubstituteNode(hint, node); if (emitNodeWithComments && hint !== EmitHint.SourceFile) { emitNodeWithComments(hint, node, pipelineEmitWithSourceMap); } else { pipelineEmitWithSourceMap(hint, node); } } function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) { if (onEmitSourceMapOfNode && hint !== EmitHint.SourceFile && hint !== EmitHint.IdentifierName) { onEmitSourceMapOfNode(hint, node, pipelineEmitWithHint); } else { pipelineEmitWithHint(hint, node); } } function pipelineEmitWithHint(hint: EmitHint, node: Node): void { switch (hint) { case EmitHint.SourceFile: return pipelineEmitSourceFile(node); case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node); case EmitHint.Expression: return pipelineEmitExpression(node); case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); case EmitHint.Unspecified: return pipelineEmitUnspecified(node); } } function pipelineEmitSourceFile(node: Node): void { Debug.assertNode(node, isSourceFile); emitSourceFile(node); } function pipelineEmitIdentifierName(node: Node): void { Debug.assertNode(node, isIdentifier); emitIdentifier(node); } function emitMappedTypeParameter(node: TypeParameterDeclaration): void { emit(node.name); write(" in "); emit(node.constraint); } function pipelineEmitUnspecified(node: Node): void { const kind = node.kind; // Reserved words // Strict mode reserved words // Contextual keywords if (isKeyword(kind)) { writeTokenNode(node); return; } switch (kind) { // Pseudo-literals case SyntaxKind.TemplateHead: case SyntaxKind.TemplateMiddle: case SyntaxKind.TemplateTail: return emitLiteral(node); // Identifiers case SyntaxKind.Identifier: return emitIdentifier(node); // Parse tree nodes // Names case SyntaxKind.QualifiedName: return emitQualifiedName(node); case SyntaxKind.ComputedPropertyName: return emitComputedPropertyName(node); // Signature elements case SyntaxKind.TypeParameter: return emitTypeParameter(node); case SyntaxKind.Parameter: return emitParameter(node); case SyntaxKind.Decorator: return emitDecorator(node); // Type members case SyntaxKind.PropertySignature: return emitPropertySignature(node); case SyntaxKind.PropertyDeclaration: return emitPropertyDeclaration(node); case SyntaxKind.MethodSignature: return emitMethodSignature(node); case SyntaxKind.MethodDeclaration: return emitMethodDeclaration(node); case SyntaxKind.Constructor: return emitConstructor(node); case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return emitAccessorDeclaration(node); case SyntaxKind.CallSignature: return emitCallSignature(node); case SyntaxKind.ConstructSignature: return emitConstructSignature(node); case SyntaxKind.IndexSignature: return emitIndexSignature(node); // Types case SyntaxKind.TypePredicate: return emitTypePredicate(node); case SyntaxKind.TypeReference: return emitTypeReference(node); case SyntaxKind.FunctionType: return emitFunctionType(node); case SyntaxKind.ConstructorType: return emitConstructorType(node); case SyntaxKind.TypeQuery: return emitTypeQuery(node); case SyntaxKind.TypeLiteral: return emitTypeLiteral(node); case SyntaxKind.ArrayType: return emitArrayType(node); case SyntaxKind.TupleType: return emitTupleType(node); case SyntaxKind.UnionType: return emitUnionType(node); case SyntaxKind.IntersectionType: return emitIntersectionType(node); case SyntaxKind.ParenthesizedType: return emitParenthesizedType(node); 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); // 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(); 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.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: return emitJsxOpeningElement(node); case SyntaxKind.JsxClosingElement: return emitJsxClosingElement(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 (ignored) // Transformation nodes (ignored) } // If the node is an expression, try to emit it as an expression with // substitution. if (isExpression(node)) { return pipelineEmitExpression(trySubstituteNode(EmitHint.Expression, node)); } if (isToken(node)) { writeTokenNode(node); return; } } function pipelineEmitExpression(node: Node): void { const kind = node.kind; switch (kind) { // Literals case SyntaxKind.NumericLiteral: return emitNumericLiteral(node); case SyntaxKind.StringLiteral: case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: return emitLiteral(node); // 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); 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); // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: return emitPartiallyEmittedExpression(node); case SyntaxKind.CommaListExpression: return emitCommaList(node); } } function trySubstituteNode(hint: EmitHint, node: Node) { return node && substituteNode && substituteNode(hint, node) || node; } function emitHelpersIndirect(node: Node) { if (onEmitHelpers) { onEmitHelpers(node, writeLines); } } // // Literals/Pseudo-literals // // SyntaxKind.NumericLiteral function emitNumericLiteral(node: NumericLiteral) { emitLiteral(node); } // SyntaxKind.StringLiteral // SyntaxKind.RegularExpressionLiteral // SyntaxKind.NoSubstitutionTemplateLiteral // SyntaxKind.TemplateHead // SyntaxKind.TemplateMiddle // SyntaxKind.TemplateTail function emitLiteral(node: LiteralLikeNode) { const text = getLiteralTextOfNode(node); if ((printerOptions.sourceMap || printerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { writer.writeLiteral(text); } else { write(text); } } // // Identifiers // function emitIdentifier(node: Identifier) { write(getTextOfNode(node, /*includeTrivia*/ false)); emitTypeArguments(node, node.typeArguments); } // // Names // function emitQualifiedName(node: QualifiedName) { emitEntityName(node.left); write("."); emit(node.right); } function emitEntityName(node: EntityName) { if (node.kind === SyntaxKind.Identifier) { emitExpression(node); } else { emit(node); } } function emitComputedPropertyName(node: ComputedPropertyName) { write("["); emitExpression(node.expression); write("]"); } // // Signature elements // function emitTypeParameter(node: TypeParameterDeclaration) { emit(node.name); emitWithPrefix(" extends ", node.constraint); emitWithPrefix(" = ", node.default); } function emitParameter(node: ParameterDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emitIfPresent(node.dotDotDotToken); emit(node.name); emitIfPresent(node.questionToken); emitWithPrefix(": ", node.type); emitExpressionWithPrefix(" = ", node.initializer); } function emitDecorator(decorator: Decorator) { write("@"); emitExpression(decorator.expression); } // // Type members // function emitPropertySignature(node: PropertySignature) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emit(node.name); emitIfPresent(node.questionToken); emitWithPrefix(": ", node.type); write(";"); } function emitPropertyDeclaration(node: PropertyDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emit(node.name); emitIfPresent(node.questionToken); emitWithPrefix(": ", node.type); emitExpressionWithPrefix(" = ", node.initializer); write(";"); } function emitMethodSignature(node: MethodSignature) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emit(node.name); emitIfPresent(node.questionToken); emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); emitWithPrefix(": ", node.type); write(";"); } function emitMethodDeclaration(node: MethodDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emitIfPresent(node.asteriskToken); emit(node.name); emitIfPresent(node.questionToken); emitSignatureAndBody(node, emitSignatureHead); } function emitConstructor(node: ConstructorDeclaration) { emitModifiers(node, node.modifiers); write("constructor"); emitSignatureAndBody(node, emitSignatureHead); } function emitAccessorDeclaration(node: AccessorDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); emit(node.name); emitSignatureAndBody(node, emitSignatureHead); } function emitCallSignature(node: CallSignatureDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); emitWithPrefix(": ", node.type); write(";"); } function emitConstructSignature(node: ConstructSignatureDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); write("new "); emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); emitWithPrefix(": ", node.type); write(";"); } function emitIndexSignature(node: IndexSignatureDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); emitParametersForIndexSignature(node, node.parameters); emitWithPrefix(": ", node.type); write(";"); } function emitSemicolonClassElement() { write(";"); } // // Types // function emitTypePredicate(node: TypePredicateNode) { emit(node.parameterName); write(" is "); emit(node.type); } function emitTypeReference(node: TypeReferenceNode) { emit(node.typeName); emitTypeArguments(node, node.typeArguments); } function emitFunctionType(node: FunctionTypeNode) { emitTypeParameters(node, node.typeParameters); emitParametersForArrow(node, node.parameters); write(" => "); emit(node.type); } function emitConstructorType(node: ConstructorTypeNode) { write("new "); emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); write(" => "); emit(node.type); } function emitTypeQuery(node: TypeQueryNode) { write("typeof "); emit(node.exprName); } function emitTypeLiteral(node: TypeLiteralNode) { write("{"); const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers; emitList(node, node.members, flags | ListFormat.NoSpaceIfEmpty); write("}"); } function emitArrayType(node: ArrayTypeNode) { emit(node.elementType); write("[]"); } function emitTupleType(node: TupleTypeNode) { write("["); emitList(node, node.elementTypes, ListFormat.TupleTypeElements); write("]"); } function emitUnionType(node: UnionTypeNode) { emitList(node, node.types, ListFormat.UnionTypeConstituents); } function emitIntersectionType(node: IntersectionTypeNode) { emitList(node, node.types, ListFormat.IntersectionTypeConstituents); } function emitParenthesizedType(node: ParenthesizedTypeNode) { write("("); emit(node.type); write(")"); } function emitThisType() { write("this"); } function emitTypeOperator(node: TypeOperatorNode) { writeTokenText(node.operator); write(" "); emit(node.type); } function emitIndexedAccessType(node: IndexedAccessTypeNode) { emit(node.objectType); write("["); emit(node.indexType); write("]"); } function emitMappedType(node: MappedTypeNode) { const emitFlags = getEmitFlags(node); write("{"); if (emitFlags & EmitFlags.SingleLine) { write(" "); } else { writeLine(); increaseIndent(); } if (node.readonlyToken) { emit(node.readonlyToken); write(" "); } write("["); pipelineEmitWithNotification(EmitHint.MappedTypeParameter, node.typeParameter); write("]"); emitIfPresent(node.questionToken); write(": "); emit(node.type); write(";"); if (emitFlags & EmitFlags.SingleLine) { write(" "); } else { writeLine(); decreaseIndent(); } write("}"); } function emitLiteralType(node: LiteralTypeNode) { emitExpression(node.literal); } // // Binding patterns // function emitObjectBindingPattern(node: ObjectBindingPattern) { const elements = node.elements; if (elements.length === 0) { write("{}"); } else { write("{"); emitList(node, elements, ListFormat.ObjectBindingPatternElements); write("}"); } } function emitArrayBindingPattern(node: ArrayBindingPattern) { const elements = node.elements; if (elements.length === 0) { write("[]"); } else { write("["); emitList(node, node.elements, ListFormat.ArrayBindingPatternElements); write("]"); } } function emitBindingElement(node: BindingElement) { emitWithSuffix(node.propertyName, ": "); emitIfPresent(node.dotDotDotToken); emit(node.name); emitExpressionWithPrefix(" = ", node.initializer); } // // Expressions // function emitArrayLiteralExpression(node: ArrayLiteralExpression) { const elements = node.elements; const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); } function emitObjectLiteralExpression(node: ObjectLiteralExpression) { const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; if (indentedFlag) { increaseIndent(); } const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None; emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); if (indentedFlag) { decreaseIndent(); } } function emitPropertyAccessExpression(node: PropertyAccessExpression) { let indentBeforeDot = false; let indentAfterDot = false; if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { const dotRangeStart = node.expression.end; const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1; const dotToken = createToken(SyntaxKind.DotToken); dotToken.pos = dotRangeStart; dotToken.end = dotRangeEnd; indentBeforeDot = needsIndentation(node, node.expression, dotToken); indentAfterDot = needsIndentation(node, dotToken, node.name); } emitExpression(node.expression); increaseIndentIf(indentBeforeDot); const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); write(shouldEmitDotDot ? ".." : "."); increaseIndentIf(indentAfterDot); emit(node.name); decreaseIndentIf(indentBeforeDot, indentAfterDot); } // 1..toString is a valid property access, emit a dot after the literal // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal function needsDotDotForPropertyAccess(expression: Expression) { 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); return !expression.numericLiteralFlags && text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; } else if (isPropertyAccessExpression(expression) || isElementAccessExpression(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 && printerOptions.removeComments; } } function emitElementAccessExpression(node: ElementAccessExpression) { emitExpression(node.expression); write("["); emitExpression(node.argumentExpression); write("]"); } function emitCallExpression(node: CallExpression) { emitExpression(node.expression); emitTypeArguments(node, node.typeArguments); emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); } function emitNewExpression(node: NewExpression) { write("new "); emitExpression(node.expression); emitTypeArguments(node, node.typeArguments); emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); } function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { emitExpression(node.tag); write(" "); emitExpression(node.template); } function emitTypeAssertionExpression(node: TypeAssertion) { write("<"); emit(node.type); write(">"); emitExpression(node.expression); } function emitParenthesizedExpression(node: ParenthesizedExpression) { write("("); emitExpression(node.expression); write(")"); } function emitFunctionExpression(node: FunctionExpression) { 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); emitWithPrefix(": ", node.type); write(" "); emit(node.equalsGreaterThanToken); } function emitDeleteExpression(node: DeleteExpression) { write("delete "); emitExpression(node.expression); } function emitTypeOfExpression(node: TypeOfExpression) { write("typeof "); emitExpression(node.expression); } function emitVoidExpression(node: VoidExpression) { write("void "); emitExpression(node.expression); } function emitAwaitExpression(node: AwaitExpression) { write("await "); emitExpression(node.expression); } function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { writeTokenText(node.operator); if (shouldEmitWhitespaceBeforeOperand(node)) { write(" "); } 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); } function emitBinaryExpression(node: BinaryExpression) { const isCommaOperator = node.operatorToken.kind !== SyntaxKind.CommaToken; const indentBeforeOperator = needsIndentation(node, node.left, node.operatorToken); const indentAfterOperator = needsIndentation(node, node.operatorToken, node.right); emitExpression(node.left); increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); emitLeadingCommentsOfPosition(node.operatorToken.pos); writeTokenNode(node.operatorToken); emitTrailingCommentsOfPosition(node.operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts increaseIndentIf(indentAfterOperator, " "); emitExpression(node.right); decreaseIndentIf(indentBeforeOperator, indentAfterOperator); } function emitConditionalExpression(node: ConditionalExpression) { const indentBeforeQuestion = needsIndentation(node, node.condition, node.questionToken); const indentAfterQuestion = needsIndentation(node, node.questionToken, node.whenTrue); const indentBeforeColon = needsIndentation(node, node.whenTrue, node.colonToken); const indentAfterColon = needsIndentation(node, node.colonToken, node.whenFalse); emitExpression(node.condition); increaseIndentIf(indentBeforeQuestion, " "); emit(node.questionToken); increaseIndentIf(indentAfterQuestion, " "); emitExpression(node.whenTrue); decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion); increaseIndentIf(indentBeforeColon, " "); emit(node.colonToken); increaseIndentIf(indentAfterColon, " "); emitExpression(node.whenFalse); decreaseIndentIf(indentBeforeColon, indentAfterColon); } function emitTemplateExpression(node: TemplateExpression) { emit(node.head); emitList(node, node.templateSpans, ListFormat.TemplateExpressionSpans); } function emitYieldExpression(node: YieldExpression) { write("yield"); emit(node.asteriskToken); emitExpressionWithPrefix(" ", node.expression); } function emitSpreadExpression(node: SpreadElement) { write("..."); emitExpression(node.expression); } function emitClassExpression(node: ClassExpression) { emitClassDeclarationOrExpression(node); } function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { emitExpression(node.expression); emitTypeArguments(node, node.typeArguments); } function emitAsExpression(node: AsExpression) { emitExpression(node.expression); if (node.type) { write(" as "); emit(node.type); } } function emitNonNullExpression(node: NonNullExpression) { emitExpression(node.expression); write("!"); } function emitMetaProperty(node: MetaProperty) { writeToken(node.keywordToken, node.pos); write("."); emit(node.name); } // // Misc // function emitTemplateSpan(node: TemplateSpan) { emitExpression(node.expression); emit(node.literal); } // // Statements // function emitBlock(node: Block) { writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node); emitBlockStatements(node, /*forceSingleLine*/ !node.multiLine && isEmptyBlock(node)); // We have to call emitLeadingComments explicitly here because otherwise leading comments of the close brace token will not be emitted increaseIndent(); emitLeadingCommentsOfPosition(node.statements.end); decreaseIndent(); writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node); } function emitBlockStatements(node: BlockLike, forceSingleLine: boolean) { const format = forceSingleLine || getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineBlockStatements : ListFormat.MultiLineBlockStatements; emitList(node, node.statements, format); } function emitVariableStatement(node: VariableStatement) { emitModifiers(node, node.modifiers); emit(node.declarationList); write(";"); } function emitEmptyStatement() { write(";"); } function emitExpressionStatement(node: ExpressionStatement) { emitExpression(node.expression); write(";"); } function emitIfStatement(node: IfStatement) { const openParenPos = writeToken(SyntaxKind.IfKeyword, node.pos, node); write(" "); writeToken(SyntaxKind.OpenParenToken, openParenPos, node); emitExpression(node.expression); writeToken(SyntaxKind.CloseParenToken, node.expression.end, node); emitEmbeddedStatement(node, node.thenStatement); if (node.elseStatement) { writeLineOrSpace(node); writeToken(SyntaxKind.ElseKeyword, node.thenStatement.end, node); if (node.elseStatement.kind === SyntaxKind.IfStatement) { write(" "); emit(node.elseStatement); } else { emitEmbeddedStatement(node, node.elseStatement); } } } function emitDoStatement(node: DoStatement) { write("do"); emitEmbeddedStatement(node, node.statement); if (isBlock(node.statement)) { write(" "); } else { writeLineOrSpace(node); } write("while ("); emitExpression(node.expression); write(");"); } function emitWhileStatement(node: WhileStatement) { write("while ("); emitExpression(node.expression); write(")"); emitEmbeddedStatement(node, node.statement); } function emitForStatement(node: ForStatement) { const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); write(" "); writeToken(SyntaxKind.OpenParenToken, openParenPos, /*contextNode*/ node); emitForBinding(node.initializer); write(";"); emitExpressionWithPrefix(" ", node.condition); write(";"); emitExpressionWithPrefix(" ", node.incrementor); write(")"); emitEmbeddedStatement(node, node.statement); } function emitForInStatement(node: ForInStatement) { const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); write(" "); writeToken(SyntaxKind.OpenParenToken, openParenPos); emitForBinding(node.initializer); write(" in "); emitExpression(node.expression); writeToken(SyntaxKind.CloseParenToken, node.expression.end); emitEmbeddedStatement(node, node.statement); } function emitForOfStatement(node: ForOfStatement) { const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); write(" "); emitWithSuffix(node.awaitModifier, " "); writeToken(SyntaxKind.OpenParenToken, openParenPos); emitForBinding(node.initializer); write(" of "); emitExpression(node.expression); writeToken(SyntaxKind.CloseParenToken, node.expression.end); emitEmbeddedStatement(node, node.statement); } function emitForBinding(node: VariableDeclarationList | Expression) { if (node !== undefined) { if (node.kind === SyntaxKind.VariableDeclarationList) { emit(node); } else { emitExpression(node); } } } function emitContinueStatement(node: ContinueStatement) { writeToken(SyntaxKind.ContinueKeyword, node.pos); emitWithPrefix(" ", node.label); write(";"); } function emitBreakStatement(node: BreakStatement) { writeToken(SyntaxKind.BreakKeyword, node.pos); emitWithPrefix(" ", node.label); write(";"); } function emitTokenWithComment(token: SyntaxKind, pos: number, contextNode?: Node) { const node = contextNode && getParseTreeNode(contextNode); if (node && node.kind === contextNode.kind) { pos = skipTrivia(currentSourceFile.text, pos); } pos = writeToken(token, pos, /*contextNode*/ contextNode); if (node && node.kind === contextNode.kind) { emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ true); } return pos; } function emitReturnStatement(node: ReturnStatement) { emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); emitExpressionWithPrefix(" ", node.expression); write(";"); } function emitWithStatement(node: WithStatement) { write("with ("); emitExpression(node.expression); write(")"); emitEmbeddedStatement(node, node.statement); } function emitSwitchStatement(node: SwitchStatement) { const openParenPos = writeToken(SyntaxKind.SwitchKeyword, node.pos); write(" "); writeToken(SyntaxKind.OpenParenToken, openParenPos); emitExpression(node.expression); writeToken(SyntaxKind.CloseParenToken, node.expression.end); write(" "); emit(node.caseBlock); } function emitLabeledStatement(node: LabeledStatement) { emit(node.label); write(": "); emit(node.statement); } function emitThrowStatement(node: ThrowStatement) { write("throw"); emitExpressionWithPrefix(" ", node.expression); write(";"); } function emitTryStatement(node: TryStatement) { write("try "); emit(node.tryBlock); if (node.catchClause) { writeLineOrSpace(node); emit(node.catchClause); } if (node.finallyBlock) { writeLineOrSpace(node); write("finally "); emit(node.finallyBlock); } } function emitDebuggerStatement(node: DebuggerStatement) { writeToken(SyntaxKind.DebuggerKeyword, node.pos); write(";"); } // // Declarations // function emitVariableDeclaration(node: VariableDeclaration) { emit(node.name); emitWithPrefix(": ", node.type); emitExpressionWithPrefix(" = ", node.initializer); } function emitVariableDeclarationList(node: VariableDeclarationList) { write(isLet(node) ? "let " : isConst(node) ? "const " : "var "); emitList(node, node.declarations, ListFormat.VariableDeclarationList); } function emitFunctionDeclaration(node: FunctionDeclaration) { emitFunctionDeclarationOrExpression(node); } function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); write("function"); emitIfPresent(node.asteriskToken); write(" "); emitIdentifierName(node.name); emitSignatureAndBody(node, emitSignatureHead); } function emitBlockCallback(_hint: EmitHint, body: Node): void { emitBlockFunctionBody(body); } 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(); } if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { emitSignatureHead(node); if (onEmitNode) { onEmitNode(EmitHint.Unspecified, body, emitBlockCallback); } else { emitBlockFunctionBody(body); } } else { pushNameGenerationScope(); emitSignatureHead(node); if (onEmitNode) { onEmitNode(EmitHint.Unspecified, body, emitBlockCallback); } else { emitBlockFunctionBody(body); } popNameGenerationScope(); } if (indentedFlag) { decreaseIndent(); } } else { emitSignatureHead(node); write(" "); emitExpression(body); } } else { emitSignatureHead(node); write(";"); } } function emitSignatureHead(node: FunctionDeclaration | FunctionExpression | MethodDeclaration | AccessorDeclaration | ConstructorDeclaration) { emitTypeParameters(node, node.typeParameters); emitParameters(node, node.parameters); emitWithPrefix(": ", node.type); } 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 (shouldWriteLeadingLineTerminator(body, body.statements, ListFormat.PreserveLines) || shouldWriteClosingLineTerminator(body, body.statements, ListFormat.PreserveLines)) { return false; } let previousStatement: Statement; for (const statement of body.statements) { if (shouldWriteSeparatingLineTerminator(previousStatement, statement, ListFormat.PreserveLines)) { return false; } previousStatement = statement; } return true; } function emitBlockFunctionBody(body: Block) { write(" {"); increaseIndent(); const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body) ? emitBlockFunctionBodyOnSingleLine : emitBlockFunctionBodyWorker; if (emitBodyWithDetachedComments) { emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody); } else { emitBlockFunctionBody(body); } decreaseIndent(); writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body); } function emitBlockFunctionBodyOnSingleLine(body: Block) { emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true); } function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) { // Emit all the prologue directives (like "use strict"). const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); const pos = writer.getTextPos(); emitHelpersIndirect(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 emitClassDeclaration(node: ClassDeclaration) { emitClassDeclarationOrExpression(node); } function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); write("class"); emitNodeWithPrefix(" ", node.name, emitIdentifierName); const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; if (indentedFlag) { increaseIndent(); } emitTypeParameters(node, node.typeParameters); emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); pushNameGenerationScope(); write(" {"); emitList(node, node.members, ListFormat.ClassMembers); write("}"); popNameGenerationScope(); if (indentedFlag) { decreaseIndent(); } } function emitInterfaceDeclaration(node: InterfaceDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); write("interface "); emit(node.name); emitTypeParameters(node, node.typeParameters); emitList(node, node.heritageClauses, ListFormat.HeritageClauses); write(" {"); emitList(node, node.members, ListFormat.InterfaceMembers); write("}"); } function emitTypeAliasDeclaration(node: TypeAliasDeclaration) { emitDecorators(node, node.decorators); emitModifiers(node, node.modifiers); write("type "); emit(node.name); emitTypeParameters(node, node.typeParameters); write(" = "); emit(node.type); write(";"); } function emitEnumDeclaration(node: EnumDeclaration) { emitModifiers(node, node.modifiers); write("enum "); emit(node.name); pushNameGenerationScope(); write(" {"); emitList(node, node.members, ListFormat.EnumMembers); write("}"); popNameGenerationScope(); } function emitModuleDeclaration(node: ModuleDeclaration) { emitModifiers(node, node.modifiers); if (~node.flags & NodeFlags.GlobalAugmentation) { write(node.flags & NodeFlags.Namespace ? "namespace " : "module "); } emit(node.name); let body = node.body; while (body.kind === SyntaxKind.ModuleDeclaration) { write("."); emit((body).name); body = (body).body; } write(" "); emit(body); } function emitModuleBlock(node: ModuleBlock) { pushNameGenerationScope(); write("{"); emitBlockStatements(node, /*forceSingleLine*/ isEmptyBlock(node)); write("}"); popNameGenerationScope(); } function emitCaseBlock(node: CaseBlock) { writeToken(SyntaxKind.OpenBraceToken, node.pos); emitList(node, node.clauses, ListFormat.CaseBlockClauses); writeToken(SyntaxKind.CloseBraceToken, node.clauses.end); } function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { emitModifiers(node, node.modifiers); write("import "); emit(node.name); write(" = "); emitModuleReference(node.moduleReference); write(";"); } function emitModuleReference(node: ModuleReference) { if (node.kind === SyntaxKind.Identifier) { emitExpression(node); } else { emit(node); } } function emitImportDeclaration(node: ImportDeclaration) { emitModifiers(node, node.modifiers); write("import "); if (node.importClause) { emit(node.importClause); write(" from "); } emitExpression(node.moduleSpecifier); write(";"); } function emitImportClause(node: ImportClause) { emit(node.name); if (node.name && node.namedBindings) { write(", "); } emit(node.namedBindings); } function emitNamespaceImport(node: NamespaceImport) { write("* as "); emit(node.name); } function emitNamedImports(node: NamedImports) { emitNamedImportsOrExports(node); } function emitImportSpecifier(node: ImportSpecifier) { emitImportOrExportSpecifier(node); } function emitExportAssignment(node: ExportAssignment) { write(node.isExportEquals ? "export = " : "export default "); emitExpression(node.expression); write(";"); } function emitExportDeclaration(node: ExportDeclaration) { write("export "); if (node.exportClause) { emit(node.exportClause); } else { write("*"); } if (node.moduleSpecifier) { write(" from "); emitExpression(node.moduleSpecifier); } write(";"); } function emitNamespaceExportDeclaration(node: NamespaceExportDeclaration) { write("export as namespace "); emit(node.name); write(";"); } function emitNamedExports(node: NamedExports) { emitNamedImportsOrExports(node); } function emitExportSpecifier(node: ExportSpecifier) { emitImportOrExportSpecifier(node); } function emitNamedImportsOrExports(node: NamedImportsOrExports) { write("{"); emitList(node, node.elements, ListFormat.NamedImportsOrExportsElements); write("}"); } function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { if (node.propertyName) { emit(node.propertyName); write(" as "); } emit(node.name); } // // Module references // function emitExternalModuleReference(node: ExternalModuleReference) { write("require("); emitExpression(node.expression); write(")"); } // // JSX // function emitJsxElement(node: JsxElement) { emit(node.openingElement); emitList(node, node.children, ListFormat.JsxElementChildren); emit(node.closingElement); } function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { write("<"); emitJsxTagName(node.tagName); write(" "); // We are checking here so we won't re-enter the emiting pipeline and emit extra sourcemap if (node.attributes.properties && node.attributes.properties.length > 0) { emit(node.attributes); } write("/>"); } function emitJsxOpeningElement(node: JsxOpeningElement) { write("<"); emitJsxTagName(node.tagName); writeIfAny(node.attributes.properties, " "); // We are checking here so we won't re-enter the emitting pipeline and emit extra sourcemap if (node.attributes.properties && node.attributes.properties.length > 0) { emit(node.attributes); } write(">"); } function emitJsxText(node: JsxText) { writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true)); } function emitJsxClosingElement(node: JsxClosingElement) { write(""); } function emitJsxAttributes(node: JsxAttributes) { emitList(node, node.properties, ListFormat.JsxElementAttributes); } function emitJsxAttribute(node: JsxAttribute) { emit(node.name); emitWithPrefix("=", node.initializer); } function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { write("{..."); emitExpression(node.expression); write("}"); } function emitJsxExpression(node: JsxExpression) { if (node.expression) { write("{"); emitIfPresent(node.dotDotDotToken); emitExpression(node.expression); write("}"); } } function emitJsxTagName(node: JsxTagNameExpression) { if (node.kind === SyntaxKind.Identifier) { emitExpression(node); } else { emit(node); } } // // Clauses // function emitCaseClause(node: CaseClause) { write("case "); emitExpression(node.expression); write(":"); emitCaseOrDefaultClauseStatements(node, node.statements); } function emitDefaultClause(node: DefaultClause) { write("default:"); emitCaseOrDefaultClauseStatements(node, node.statements); } function emitCaseOrDefaultClauseStatements(parentNode: Node, statements: NodeArray) { 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) ); // e.g: // case 0: // Zero // case 1: // One // case 2: // two // return "hi"; // If there is no statements, emitNodeWithComments of the parentNode which is caseClause will take care of trailing comment. // So in example above, comment "// Zero" and "// One" will be emit in emitTrailingComments in emitNodeWithComments. // However, for "case 2", because parentNode which is caseClause has an "end" property to be end of the statements (in this case return statement) // comment "// two" will not be emitted in emitNodeWithComments. // Therefore, we have to do the check here to emit such comment. if (statements.length > 0) { // We use emitTrailingCommentsOfPosition instead of emitLeadingCommentsOfPosition because leading comments is defined as comments before the node after newline character separating it from previous line // Note: we can't use parentNode.end as such position includes statements. emitTrailingCommentsOfPosition(statements.pos); } let format = ListFormat.CaseOrDefaultClauseStatements; if (emitAsSingleStatement) { write(" "); format &= ~(ListFormat.MultiLine | ListFormat.Indented); } emitList(parentNode, statements, format); } function emitHeritageClause(node: HeritageClause) { write(" "); writeTokenText(node.token); write(" "); emitList(node, node.types, ListFormat.HeritageClauseTypes); } function emitCatchClause(node: CatchClause) { const openParenPos = writeToken(SyntaxKind.CatchKeyword, node.pos); write(" "); if (node.variableDeclaration) { writeToken(SyntaxKind.OpenParenToken, openParenPos); emit(node.variableDeclaration); writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration.end); write(" "); } emit(node.block); } // // Property assignments // function emitPropertyAssignment(node: PropertyAssignment) { emit(node.name); write(": "); // This is to ensure that we emit comment in the following case: // For example: // obj = { // id: /*comment1*/ ()=>void // } // "comment1" is not considered to be leading comment for node.initializer // but rather a trailing comment on the previous node. const initializer = node.initializer; if (emitTrailingCommentsOfPosition && (getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { const commentRange = getCommentRange(initializer); emitTrailingCommentsOfPosition(commentRange.pos); } emitExpression(initializer); } function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { emit(node.name); if (node.objectAssignmentInitializer) { write(" = "); emitExpression(node.objectAssignmentInitializer); } } function emitSpreadAssignment(node: SpreadAssignment) { if (node.expression) { write("..."); emitExpression(node.expression); } } // // Enum // function emitEnumMember(node: EnumMember) { emit(node.name); emitExpressionWithPrefix(" = ", node.initializer); } // // 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 emitSourceFileWorker(node: SourceFile) { const statements = node.statements; pushNameGenerationScope(); emitHelpersIndirect(node); const index = findIndex(statements, statement => !isPrologueDirective(statement)); emitList(node, statements, ListFormat.MultiLine, index === -1 ? statements.length : index); popNameGenerationScope(); } // Transformation nodes function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { emitExpression(node.expression); } function emitCommaList(node: CommaListExpression) { emitExpressionList(node, node.elements, ListFormat.CommaListElements); } /** * Emits any prologue directives at the start of a Statement list, returning the * number of prologue directives written to the output. */ function emitPrologueDirectives(statements: ReadonlyArray, startWithNewLine?: boolean, seenPrologueDirectives?: Map): number { 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 (startWithNewLine || i > 0) { writeLine(); } emit(statement); if (seenPrologueDirectives) { seenPrologueDirectives.set(statement.expression.text, true); } } } else { // return index of the first non prologue directive return i; } } return statements.length; } function emitPrologueDirectivesIfNeeded(sourceFileOrBundle: Bundle | SourceFile) { if (isSourceFile(sourceFileOrBundle)) { setSourceFile(sourceFileOrBundle as SourceFile); emitPrologueDirectives((sourceFileOrBundle as SourceFile).statements); } else { const seenPrologueDirectives = createMap(); for (const sourceFile of (sourceFileOrBundle as Bundle).sourceFiles) { setSourceFile(sourceFile); emitPrologueDirectives(sourceFile.statements, /*startWithNewLine*/ true, seenPrologueDirectives); } } } function emitShebangIfNeeded(sourceFileOrBundle: Bundle | SourceFile) { if (isSourceFile(sourceFileOrBundle)) { const shebang = getShebang(sourceFileOrBundle.text); if (shebang) { write(shebang); writeLine(); return true; } } else { for (const sourceFile of sourceFileOrBundle.sourceFiles) { // Emit only the first encountered shebang if (emitShebangIfNeeded(sourceFile)) { break; } } } } // // Helpers // function emitModifiers(node: Node, modifiers: NodeArray) { if (modifiers && modifiers.length) { emitList(node, modifiers, ListFormat.Modifiers); write(" "); } } function emitWithPrefix(prefix: string, node: Node) { emitNodeWithPrefix(prefix, node, emit); } function emitExpressionWithPrefix(prefix: string, node: Node) { emitNodeWithPrefix(prefix, node, emitExpression); } function emitNodeWithPrefix(prefix: string, node: Node, emit: (node: Node) => void) { if (node) { write(prefix); emit(node); } } function emitWithSuffix(node: Node, suffix: string) { if (node) { emit(node); write(suffix); } } function emitEmbeddedStatement(parent: Node, node: Statement) { if (isBlock(node) || getEmitFlags(parent) & EmitFlags.SingleLine) { write(" "); emit(node); } else { writeLine(); increaseIndent(); emit(node); decreaseIndent(); } } function emitDecorators(parentNode: Node, decorators: NodeArray) { emitList(parentNode, decorators, ListFormat.Decorators); } function emitTypeArguments(parentNode: Node, typeArguments: NodeArray) { emitList(parentNode, typeArguments, ListFormat.TypeArguments); } function emitTypeParameters(parentNode: Node, typeParameters: NodeArray) { 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) && 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 emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) { if (canEmitSimpleArrowHead(parentNode, parameters)) { emitList(parentNode, parameters, ListFormat.Parameters & ~ListFormat.Parenthesis); } else { emitParameters(parentNode, parameters); } } function emitParametersForIndexSignature(parentNode: Node, parameters: NodeArray) { emitList(parentNode, parameters, ListFormat.IndexSignatureParameters); } function emitList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { emitNodeList(emit, parentNode, children, format, start, count); } function emitExpressionList(parentNode: Node, children: NodeArray, format: ListFormat, start?: number, count?: number) { emitNodeList(emitExpression, parentNode, children, format, start, count); } function emitNodeList(emit: (node: Node) => void, parentNode: Node, children: NodeArray, format: ListFormat, start = 0, count = children ? children.length - start : 0) { const isUndefined = children === undefined; if (isUndefined && format & ListFormat.OptionalIfUndefined) { return; } const isEmpty = isUndefined || start >= children.length || count === 0; if (isEmpty && format & ListFormat.OptionalIfEmpty) { if (onBeforeEmitNodeArray) { onBeforeEmitNodeArray(children); } if (onAfterEmitNodeArray) { onAfterEmitNodeArray(children); } return; } if (format & ListFormat.BracketsMask) { write(getOpeningBracket(format)); } if (onBeforeEmitNodeArray) { onBeforeEmitNodeArray(children); } if (isEmpty) { // Write a line terminator if the parent node was multi-line if (format & ListFormat.MultiLine) { writeLine(); } else if (format & ListFormat.SpaceBetweenBraces && !(format & ListFormat.NoSpaceIfEmpty)) { write(" "); } } else { // Write the opening line terminator or leading whitespace. const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; let shouldEmitInterveningComments = mayEmitInterveningComments; if (shouldWriteLeadingLineTerminator(parentNode, children, format)) { writeLine(); shouldEmitInterveningComments = false; } else if (format & ListFormat.SpaceBetweenBraces) { write(" "); } // Increase the indent, if requested. if (format & ListFormat.Indented) { increaseIndent(); } // Emit each child. let previousSibling: Node; let shouldDecreaseIndentAfterEmit: boolean; const delimiter = getDelimiter(format); for (let i = 0; i < count; i++) { const child = children[start + i]; // Write the delimiter if this is not the first node. 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 (delimiter && previousSibling.end !== parentNode.end) { emitLeadingCommentsOfPosition(previousSibling.end); } write(delimiter); // Write either a line terminator or whitespace to separate the elements. if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) { // 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(); shouldEmitInterveningComments = false; } else if (previousSibling && format & ListFormat.SpaceBetweenSiblings) { write(" "); } } // Emit this child. if (shouldEmitInterveningComments) { if (emitTrailingCommentsOfPosition) { const commentRange = getCommentRange(child); emitTrailingCommentsOfPosition(commentRange.pos); } } else { shouldEmitInterveningComments = mayEmitInterveningComments; } emit(child); if (shouldDecreaseIndentAfterEmit) { decreaseIndent(); shouldDecreaseIndentAfterEmit = false; } previousSibling = child; } // Write a trailing comma, if requested. const hasTrailingComma = (format & ListFormat.AllowTrailingComma) && children.hasTrailingComma; if (format & ListFormat.CommaDelimited && hasTrailingComma) { write(","); } // Emit any trailing comment of the last element in the list // i.e // var array = [... // 2 // /* end of element 2 */ // ]; if (previousSibling && delimiter && previousSibling.end !== parentNode.end && !(getEmitFlags(previousSibling) & EmitFlags.NoTrailingComments)) { emitLeadingCommentsOfPosition(previousSibling.end); } // Decrease the indent, if requested. if (format & ListFormat.Indented) { decreaseIndent(); } // Write the closing line terminator or closing whitespace. if (shouldWriteClosingLineTerminator(parentNode, children, format)) { writeLine(); } else if (format & ListFormat.SpaceBetweenBraces) { write(" "); } } if (onAfterEmitNodeArray) { onAfterEmitNodeArray(children); } if (format & ListFormat.BracketsMask) { write(getClosingBracket(format)); } } function write(s: string) { writer.write(s); } function writeLine() { writer.writeLine(); } function increaseIndent() { writer.increaseIndent(); } function decreaseIndent() { writer.decreaseIndent(); } function writeIfAny(nodes: NodeArray, text: string) { if (some(nodes)) { write(text); } } function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) { return onEmitSourceMapOfToken ? onEmitSourceMapOfToken(contextNode, token, pos, writeTokenText) : writeTokenText(token, pos); } function writeTokenNode(node: Node) { if (onBeforeEmitToken) { onBeforeEmitToken(node); } write(tokenToString(node.kind)); if (onAfterEmitToken) { onAfterEmitToken(node); } } function writeTokenText(token: SyntaxKind, pos?: number) { const tokenString = tokenToString(token); write(tokenString); return pos < 0 ? pos : pos + tokenString.length; } function writeLineOrSpace(node: Node) { if (getEmitFlags(node) & EmitFlags.SingleLine) { write(" "); } else { writeLine(); } } function writeLines(text: string): void { const lines = text.split(/\r\n?|\n/g); const indentation = guessIndentation(lines); for (let i = 0; i < lines.length; i++) { const line = indentation ? lines[i].slice(indentation) : lines[i]; if (line.length) { writeLine(); write(line); writeLine(); } } } function guessIndentation(lines: string[]) { let indentation: number; for (const line of lines) { for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) { if (!isWhiteSpaceLike(line.charCodeAt(i))) { if (indentation === undefined || i < indentation) { indentation = i; break; } } } } return indentation; } function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) { if (value) { increaseIndent(); writeLine(); } else if (valueToWriteWhenNotIndenting) { write(valueToWriteWhenNotIndenting); } } // Helper function to decrease the indent if we previously indented. Allows multiple // previous indent values to be considered at a time. This also allows caller to just // call this once, passing in all their appropriate indent values, instead of needing // to call this helper function multiple times. function decreaseIndentIf(value1: boolean, value2?: boolean) { if (value1) { decreaseIndent(); } if (value2) { decreaseIndent(); } } function shouldWriteLeadingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { if (format & ListFormat.MultiLine) { return true; } if (format & ListFormat.PreserveLines) { if (format & ListFormat.PreferNewLine) { return true; } const firstChild = children[0]; if (firstChild === undefined) { return !rangeIsOnSingleLine(parentNode, currentSourceFile); } else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { return synthesizedNodeStartsOnNewLine(firstChild, format); } else { return !rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile); } } else { return false; } } function shouldWriteSeparatingLineTerminator(previousNode: Node, nextNode: Node, format: ListFormat) { if (format & ListFormat.MultiLine) { return true; } else if (format & ListFormat.PreserveLines) { if (previousNode === undefined || nextNode === undefined) { return false; } else if (nodeIsSynthesized(previousNode) || nodeIsSynthesized(nextNode)) { return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format); } else { return !rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile); } } else { return nextNode.startsOnNewLine; } } function shouldWriteClosingLineTerminator(parentNode: Node, children: NodeArray, format: ListFormat) { if (format & ListFormat.MultiLine) { return (format & ListFormat.NoTrailingNewLine) === 0; } else if (format & ListFormat.PreserveLines) { if (format & ListFormat.PreferNewLine) { return true; } const lastChild = lastOrUndefined(children); if (lastChild === undefined) { return !rangeIsOnSingleLine(parentNode, currentSourceFile); } else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { return synthesizedNodeStartsOnNewLine(lastChild, format); } else { return !rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile); } } else { return false; } } function synthesizedNodeStartsOnNewLine(node: Node, format?: ListFormat) { if (nodeIsSynthesized(node)) { const startsOnNewLine = node.startsOnNewLine; if (startsOnNewLine === undefined) { return (format & ListFormat.PreferNewLine) !== 0; } return startsOnNewLine; } return (format & ListFormat.PreferNewLine) !== 0; } function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { parent = skipSynthesizedParentheses(parent); node1 = skipSynthesizedParentheses(node1); node2 = skipSynthesizedParentheses(node2); // Always use a newline for synthesized code if the synthesizer desires it. if (node2.startsOnNewLine) { return true; } return !nodeIsSynthesized(parent) && !nodeIsSynthesized(node1) && !nodeIsSynthesized(node2) && !rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile); } 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 getTextOfNode(node: Node, includeTrivia?: boolean): string { if (isGeneratedIdentifier(node)) { return generateName(node); } else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) { return unescapeLeadingUnderscores(node.escapedText); } 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 getLiteralTextOfNode(node: LiteralLikeNode): string { if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { const textSourceNode = (node).textSourceNode; if (isIdentifier(textSourceNode)) { return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? `"${escapeString(getTextOfNode(textSourceNode))}"` : `"${escapeNonAsciiString(getTextOfNode(textSourceNode))}"`; } else { return getLiteralTextOfNode(textSourceNode); } } return getLiteralText(node, currentSourceFile); } /** * Push a new name generation scope. */ function pushNameGenerationScope() { tempFlagsStack.push(tempFlags); tempFlags = 0; } /** * Pop the current name generation scope. */ function popNameGenerationScope() { tempFlags = tempFlagsStack.pop(); } /** * Generate the text for a generated identifier. */ function generateName(name: GeneratedIdentifier) { if (name.autoGenerateKind === GeneratedIdentifierKind.Node) { // Node names generate unique names based on their original node // and are cached based on that node's id. const node = getNodeForGeneratedName(name); return generateNameCached(node); } 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 generateNameCached(node: Node) { const nodeId = getNodeId(node); return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node)); } /** * Returns a value indicating whether a name is unique globally, within the current file, * or within the NameGenerator. */ function isUniqueName(name: string): boolean { return !(hasGlobalName && hasGlobalName(name)) && !currentSourceFile.identifiers.has(name) && !generatedNames.has(name); } /** * 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; } /** * 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): string { if (flags && !(tempFlags & flags)) { const name = flags === TempFlags._i ? "_i" : "_n"; if (isUniqueName(name)) { tempFlags |= flags; return name; } } 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)) { return name; } } } } /** * 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. */ function makeUniqueName(baseName: string): string { // Find the first unique 'name_n', where n is a positive number if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { baseName += "_"; } let i = 1; while (true) { const generatedName = baseName + i; if (isUniqueName(generatedName)) { generatedNames.set(generatedName, true); return generatedName; } i++; } } /** * 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); } /** * Generates a unique name for an ImportDeclaration or ExportDeclaration. */ function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { const expr = getExternalModuleName(node); const baseName = isStringLiteral(expr) ? makeIdentifierFromModuleName(expr.text) : "module"; return makeUniqueName(baseName); } /** * Generates a unique name for a default export. */ function generateNameForExportDefault() { return makeUniqueName("default"); } /** * Generates a unique name for a class expression. */ function generateNameForClassExpression() { return makeUniqueName("class"); } function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration) { if (isIdentifier(node.name)) { return generateNameCached(node.name); } return makeTempVariableName(TempFlags.Auto); } /** * Generates a unique name from a node. */ function generateNameForNode(node: Node): string { switch (node.kind) { case SyntaxKind.Identifier: return makeUniqueName(getTextOfNode(node)); 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); default: return makeTempVariableName(TempFlags.Auto); } } /** * Generates a unique identifier for a node. */ function makeName(name: GeneratedIdentifier) { switch (name.autoGenerateKind) { case GeneratedIdentifierKind.Auto: return makeTempVariableName(TempFlags.Auto); case GeneratedIdentifierKind.Loop: return makeTempVariableName(TempFlags._i); case GeneratedIdentifierKind.Unique: return makeUniqueName(unescapeLeadingUnderscores(name.escapedText)); } Debug.fail("Unsupported GeneratedIdentifierKind."); } /** * 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; // if "node" is a different generated name (having a different // "autoGenerateId"), use it and stop traversing. if (isIdentifier(node) && node.autoGenerateKind === GeneratedIdentifierKind.Node && node.autoGenerateId !== autoGenerateId) { break; } original = node.original; } // otherwise, return the original node for the source; return node; } } function createDelimiterMap() { const delimiters: string[] = []; delimiters[ListFormat.None] = ""; delimiters[ListFormat.CommaDelimited] = ","; delimiters[ListFormat.BarDelimited] = " |"; delimiters[ListFormat.AmpersandDelimited] = " &"; return delimiters; } function getDelimiter(format: ListFormat) { return delimiters[format & ListFormat.DelimitersMask]; } function createBracketsMap() { const brackets: string[][] = []; brackets[ListFormat.Braces] = ["{", "}"]; brackets[ListFormat.Parenthesis] = ["(", ")"]; brackets[ListFormat.AngleBrackets] = ["<", ">"]; brackets[ListFormat.SquareBrackets] = ["[", "]"]; return brackets; } function getOpeningBracket(format: ListFormat) { return brackets[format & ListFormat.BracketsMask][0]; } function getClosingBracket(format: ListFormat) { return brackets[format & ListFormat.BracketsMask][1]; } // Flags enum to track count of temp variables and a few dedicated names const enum TempFlags { Auto = 0x00000000, // No preferred name CountMask = 0x0FFFFFFF, // Temp variable counter _i = 0x10000000, // Use/preference flag for '_i' } const enum ListFormat { None = 0, // Line separators SingleLine = 0, // Prints the list on a single line (default). MultiLine = 1 << 0, // Prints the list on multiple lines. PreserveLines = 1 << 1, // Prints the list using line preservation if possible. LinesMask = SingleLine | MultiLine | PreserveLines, // Delimiters NotDelimited = 0, // There is no delimiter between list items (default). BarDelimited = 1 << 2, // Each list item is space-and-bar (" |") delimited. AmpersandDelimited = 1 << 3, // Each list item is space-and-ampersand (" &") delimited. CommaDelimited = 1 << 4, // Each list item is comma (",") delimited. DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited, AllowTrailingComma = 1 << 5, // Write a trailing comma (",") if present. // Whitespace Indented = 1 << 6, // The list should be indented. SpaceBetweenBraces = 1 << 7, // Inserts a space after the opening brace and before the closing brace. SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node. // Brackets/Braces Braces = 1 << 9, // The list is surrounded by "{" and "}". Parenthesis = 1 << 10, // The list is surrounded by "(" and ")". AngleBrackets = 1 << 11, // The list is surrounded by "<" and ">". SquareBrackets = 1 << 12, // The list is surrounded by "[" and "]". BracketsMask = Braces | Parenthesis | AngleBrackets | SquareBrackets, OptionalIfUndefined = 1 << 13, // Do not emit brackets if the list is undefined. OptionalIfEmpty = 1 << 14, // Do not emit brackets if the list is empty. Optional = OptionalIfUndefined | OptionalIfEmpty, // Other PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes. NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. NoInterveningComments = 1 << 17, // Do not emit comments between each node NoSpaceIfEmpty = 1 << 18, // If the literal is empty, do not add spaces between braces. SingleElement = 1 << 19, // Precomputed Formats Modifiers = SingleLine | SpaceBetweenSiblings | NoInterveningComments, HeritageClauses = SingleLine | SpaceBetweenSiblings, SingleLineTypeLiteralMembers = SingleLine | SpaceBetweenBraces | SpaceBetweenSiblings | Indented, MultiLineTypeLiteralMembers = MultiLine | Indented, TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces | NoSpaceIfEmpty, ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets, CommaListElements = CommaDelimited | SpaceBetweenSiblings | SingleLine, CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis, NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined, TemplateExpressionSpans = SingleLine | NoInterveningComments, SingleLineBlockStatements = SpaceBetweenBraces | SpaceBetweenSiblings | SingleLine, MultiLineBlockStatements = Indented | MultiLine, VariableDeclarationList = CommaDelimited | SpaceBetweenSiblings | SingleLine, SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenSiblings | SpaceBetweenBraces, MultiLineFunctionBodyStatements = MultiLine, ClassHeritageClauses = SingleLine | SpaceBetweenSiblings, ClassMembers = Indented | MultiLine, InterfaceMembers = Indented | MultiLine, EnumMembers = CommaDelimited | Indented | MultiLine, CaseBlockClauses = Indented | MultiLine, NamedImportsOrExportsElements = CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | SingleLine | SpaceBetweenBraces, JsxElementChildren = SingleLine | NoInterveningComments, JsxElementAttributes = SingleLine | SpaceBetweenSiblings | NoInterveningComments, CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, HeritageClauseTypes = CommaDelimited | SpaceBetweenSiblings | SingleLine, SourceFileStatements = MultiLine | NoTrailingNewLine, Decorators = MultiLine | Optional, TypeArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, TypeParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | AngleBrackets | Optional, Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | Parenthesis, IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets, } }