diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e1c59b9eea0..1b307920c55 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -212,6 +212,7 @@ namespace ts { } = comments; let currentSourceFile: SourceFile; + let statementOffset: number; // We cache the index of first non prologue so we don't have to recalculate it multiple times like in emitBodyInDirect 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. @@ -275,6 +276,7 @@ namespace ts { const previousWriter = writer; setWriter(output); emitShebangIfNeeded(bundle); + emitPrologueDirectivesIfNeeded(bundle); emitHelpersIndirect(bundle); for (const sourceFile of bundle.sourceFiles) { print(EmitHint.SourceFile, sourceFile, sourceFile); @@ -287,6 +289,7 @@ namespace ts { const previousWriter = writer; setWriter(output); emitShebangIfNeeded(sourceFile); + emitPrologueDirectivesIfNeeded(sourceFile); print(EmitHint.SourceFile, sourceFile, sourceFile); reset(); writer = previousWriter; @@ -309,6 +312,7 @@ namespace ts { function setSourceFile(sourceFile: SourceFile) { currentSourceFile = sourceFile; + statementOffset = getIndexOfFirstNonPrologueDirectives(sourceFile.statements); comments.setSourceFile(sourceFile); if (onSetSourceFile) { onSetSourceFile(sourceFile); @@ -733,7 +737,11 @@ namespace ts { } function emitBodyIndirect(node: Node, elements: NodeArray, emitCallback: (node: Node) => void): void { - if (emitBodyWithDetachedComments) { + // If the node is a sourceFile and it has prologueDirective (statmentOffSet is not zero) that is synthesize + // We will need to emit detached comment here because emitPrologueDirective will not emit comments of such prologue directive + const shouldEmitDetachedComment = node.kind !== SyntaxKind.SourceFile ? true : + statementOffset === 0 || nodeIsSynthesized((node as SourceFile).statements[statementOffset - 1]); + if (emitBodyWithDetachedComments && shouldEmitDetachedComment) { emitBodyWithDetachedComments(node, elements, emitCallback); } else { @@ -2058,7 +2066,6 @@ namespace ts { function emitSourceFileWorker(node: SourceFile) { const statements = node.statements; - const statementOffset = emitPrologueDirectives(statements); pushNameGenerationScope(); emitHelpersIndirect(node); emitList(node, statements, ListFormat.MultiLine, statementOffset); @@ -2075,13 +2082,20 @@ namespace ts { * Emits any prologue directives at the start of a Statement list, returning the * number of prologue directives written to the output. */ - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean): number { + function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, seenPrologueDirectives?: Map): number { for (let i = 0; i < statements.length; i++) { - if (isPrologueDirective(statements[i])) { - if (startWithNewLine || i > 0) { - writeLine(); + 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, statement.expression.text); + } } - emit(statements[i]); } else { // return index of the first non prologue directive @@ -2092,9 +2106,19 @@ namespace ts { return statements.length; } - // - // Helpers - // + function emitPrologueDirectivesIfNeeded(sourceFileOrBundle: Bundle | SourceFile) { + if (sourceFileOrBundle.kind === SyntaxKind.SourceFile) { + setSourceFile(sourceFileOrBundle as SourceFile); + emitPrologueDirectives((sourceFileOrBundle as SourceFile).statements); + } + else { + const seenProgolueDirectives = createMap(); + for (const sourceFile of (sourceFileOrBundle as Bundle).sourceFiles) { + setSourceFile(sourceFile); + emitPrologueDirectives(sourceFile.statements, /*startWithNewLine*/ true, seenProgolueDirectives); + } + } + } function emitShebangIfNeeded(sourceFileOrBundle: Bundle | SourceFile) { if (sourceFileOrBundle.kind === SyntaxKind.SourceFile) { @@ -2119,6 +2143,19 @@ namespace ts { } } + // + // Helpers + // + + function getIndexOfFirstNonPrologueDirectives(statements: Statement[]): number { + for (let i = 0; i < statements.length; i++) { + if (!isPrologueDirective(statements[i])) { + return i; + } + } + return statements.length; + } + function emitModifiers(node: Node, modifiers: NodeArray) { if (modifiers && modifiers.length) { emitList(node, modifiers, ListFormat.Modifiers);