Skip to content

Commit 294ac30

Browse files
authored
Merge pull request #14274 from Microsoft/master-14217
[Master] Fix 14217 emit shebang at the top of output file
2 parents 3ab7c86 + af731e0 commit 294ac30

File tree

4,846 files changed

+27247
-31779
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

4,846 files changed

+27247
-31779
lines changed

src/compiler/emitter.ts

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ namespace ts {
276276
function writeBundle(bundle: Bundle, output: EmitTextWriter) {
277277
const previousWriter = writer;
278278
setWriter(output);
279+
emitShebangIfNeeded(bundle);
280+
emitPrologueDirectivesIfNeeded(bundle);
279281
emitHelpersIndirect(bundle);
280282
for (const sourceFile of bundle.sourceFiles) {
281283
print(EmitHint.SourceFile, sourceFile, sourceFile);
@@ -287,6 +289,8 @@ namespace ts {
287289
function writeFile(sourceFile: SourceFile, output: EmitTextWriter) {
288290
const previousWriter = writer;
289291
setWriter(output);
292+
emitShebangIfNeeded(sourceFile);
293+
emitPrologueDirectivesIfNeeded(sourceFile);
290294
print(EmitHint.SourceFile, sourceFile, sourceFile);
291295
reset();
292296
writer = previousWriter;
@@ -737,15 +741,6 @@ namespace ts {
737741
return node && substituteNode && substituteNode(hint, node) || node;
738742
}
739743

740-
function emitBodyIndirect(node: Node, elements: NodeArray<Node>, emitCallback: (node: Node) => void): void {
741-
if (emitBodyWithDetachedComments) {
742-
emitBodyWithDetachedComments(node, elements, emitCallback);
743-
}
744-
else {
745-
emitCallback(node);
746-
}
747-
}
748-
749744
function emitHelpersIndirect(node: Node) {
750745
if (onEmitHelpers) {
751746
onEmitHelpers(node, writeLines);
@@ -1663,7 +1658,12 @@ namespace ts {
16631658
? emitBlockFunctionBodyOnSingleLine
16641659
: emitBlockFunctionBodyWorker;
16651660

1666-
emitBodyIndirect(body, body.statements, emitBlockFunctionBody);
1661+
if (emitBodyWithDetachedComments) {
1662+
emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody);
1663+
}
1664+
else {
1665+
emitBlockFunctionBody(body);
1666+
}
16671667

16681668
decreaseIndent();
16691669
writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body);
@@ -2072,16 +2072,27 @@ namespace ts {
20722072

20732073
function emitSourceFile(node: SourceFile) {
20742074
writeLine();
2075-
emitShebang();
2076-
emitBodyIndirect(node, node.statements, emitSourceFileWorker);
2075+
const statements = node.statements;
2076+
if (emitBodyWithDetachedComments) {
2077+
// Emit detached comment if there are no prologue directives or if the first node is synthesized.
2078+
// The synthesized node will have no leading comment so some comments may be missed.
2079+
const shouldEmitDetachedComment = statements.length === 0 ||
2080+
!isPrologueDirective(statements[0]) ||
2081+
nodeIsSynthesized(statements[0]);
2082+
if (shouldEmitDetachedComment) {
2083+
emitBodyWithDetachedComments(node, statements, emitSourceFileWorker);
2084+
return;
2085+
}
2086+
}
2087+
emitSourceFileWorker(node);
20772088
}
20782089

20792090
function emitSourceFileWorker(node: SourceFile) {
20802091
const statements = node.statements;
2081-
const statementOffset = emitPrologueDirectives(statements);
20822092
pushNameGenerationScope();
20832093
emitHelpersIndirect(node);
2084-
emitList(node, statements, ListFormat.MultiLine, statementOffset);
2094+
const index = findIndex(statements, statement => !isPrologueDirective(statement));
2095+
emitList(node, statements, ListFormat.MultiLine, index === -1 ? statements.length : index);
20852096
popNameGenerationScope();
20862097
}
20872098

@@ -2095,13 +2106,20 @@ namespace ts {
20952106
* Emits any prologue directives at the start of a Statement list, returning the
20962107
* number of prologue directives written to the output.
20972108
*/
2098-
function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean): number {
2109+
function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, seenPrologueDirectives?: Map<String>): number {
20992110
for (let i = 0; i < statements.length; i++) {
2100-
if (isPrologueDirective(statements[i])) {
2101-
if (startWithNewLine || i > 0) {
2102-
writeLine();
2111+
const statement = statements[i];
2112+
if (isPrologueDirective(statement)) {
2113+
const shouldEmitPrologueDirective = seenPrologueDirectives ? !seenPrologueDirectives.has(statement.expression.text) : true;
2114+
if (shouldEmitPrologueDirective) {
2115+
if (startWithNewLine || i > 0) {
2116+
writeLine();
2117+
}
2118+
emit(statement);
2119+
if (seenPrologueDirectives) {
2120+
seenPrologueDirectives.set(statement.expression.text, statement.expression.text);
2121+
}
21032122
}
2104-
emit(statements[i]);
21052123
}
21062124
else {
21072125
// return index of the first non prologue directive
@@ -2112,18 +2130,43 @@ namespace ts {
21122130
return statements.length;
21132131
}
21142132

2115-
//
2116-
// Helpers
2117-
//
2133+
function emitPrologueDirectivesIfNeeded(sourceFileOrBundle: Bundle | SourceFile) {
2134+
if (isSourceFile(sourceFileOrBundle)) {
2135+
setSourceFile(sourceFileOrBundle as SourceFile);
2136+
emitPrologueDirectives((sourceFileOrBundle as SourceFile).statements);
2137+
}
2138+
else {
2139+
const seenPrologueDirectives = createMap<String>();
2140+
for (const sourceFile of (sourceFileOrBundle as Bundle).sourceFiles) {
2141+
setSourceFile(sourceFile);
2142+
emitPrologueDirectives(sourceFile.statements, /*startWithNewLine*/ true, seenPrologueDirectives);
2143+
}
2144+
}
2145+
}
21182146

2119-
function emitShebang() {
2120-
const shebang = getShebang(currentSourceFile.text);
2121-
if (shebang) {
2122-
write(shebang);
2123-
writeLine();
2147+
function emitShebangIfNeeded(sourceFileOrBundle: Bundle | SourceFile) {
2148+
if (isSourceFile(sourceFileOrBundle)) {
2149+
const shebang = getShebang(sourceFileOrBundle.text);
2150+
if (shebang) {
2151+
write(shebang);
2152+
writeLine();
2153+
return true;
2154+
}
2155+
}
2156+
else {
2157+
for (const sourceFile of sourceFileOrBundle.sourceFiles) {
2158+
// Emit only the first encountered shebang
2159+
if (emitShebangIfNeeded(sourceFile)) {
2160+
break;
2161+
}
2162+
}
21242163
}
21252164
}
21262165

2166+
//
2167+
// Helpers
2168+
//
2169+
21272170
function emitModifiers(node: Node, modifiers: NodeArray<Modifier>) {
21282171
if (modifiers && modifiers.length) {
21292172
emitList(node, modifiers, ListFormat.Modifiers);

src/harness/harness.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1797,7 +1797,7 @@ namespace Harness {
17971797
if (currentFileContent === undefined) {
17981798
currentFileContent = "";
17991799
}
1800-
else {
1800+
else if (currentFileContent !== "") {
18011801
// End-of-line
18021802
currentFileContent = currentFileContent + "\n";
18031803
}

tests/baselines/reference/APISample_compile.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//// [APISample_compile.ts]
2-
32
/*
43
* Note: This test is a public API sample. The sample sources can be found
54
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler
@@ -35,12 +34,12 @@ compile(process.argv.slice(2), {
3534
});
3635

3736
//// [APISample_compile.js]
37+
"use strict";
3838
/*
3939
* Note: This test is a public API sample. The sample sources can be found
4040
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler
4141
* Please log a "breaking change" issue for any API breaking change affecting this issue
4242
*/
43-
"use strict";
4443
exports.__esModule = true;
4544
var ts = require("typescript");
4645
function compile(fileNames, options) {

tests/baselines/reference/APISample_linter.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//// [APISample_linter.ts]
2-
32
/*
43
* Note: This test is a public API sample. The sample sources can be found
54
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#traversing-the-ast-with-a-little-linter
@@ -65,12 +64,12 @@ fileNames.forEach(fileName => {
6564
});
6665

6766
//// [APISample_linter.js]
67+
"use strict";
6868
/*
6969
* Note: This test is a public API sample. The sample sources can be found
7070
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#traversing-the-ast-with-a-little-linter
7171
* Please log a "breaking change" issue for any API breaking change affecting this issue
7272
*/
73-
"use strict";
7473
exports.__esModule = true;
7574
var ts = require("typescript");
7675
function delint(sourceFile) {

tests/baselines/reference/APISample_parseConfig.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//// [APISample_parseConfig.ts]
2-
32
/*
43
* Note: This test is a public API sample. The sample sources can be found
54
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler
@@ -37,12 +36,12 @@ export function createProgram(rootFiles: string[], compilerOptionsJson: string):
3736
}
3837

3938
//// [APISample_parseConfig.js]
39+
"use strict";
4040
/*
4141
* Note: This test is a public API sample. The sample sources can be found
4242
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler
4343
* Please log a "breaking change" issue for any API breaking change affecting this issue
4444
*/
45-
"use strict";
4645
exports.__esModule = true;
4746
var ts = require("typescript");
4847
function printError(error) {

tests/baselines/reference/APISample_transform.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//// [APISample_transform.ts]
2-
32
/*
43
* Note: This test is a public API sample. The sample sources can be found
54
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-simple-transform-function
@@ -17,12 +16,12 @@ let result = ts.transpile(source, { module: ts.ModuleKind.CommonJS });
1716
console.log(JSON.stringify(result));
1817

1918
//// [APISample_transform.js]
19+
"use strict";
2020
/*
2121
* Note: This test is a public API sample. The sample sources can be found
2222
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-simple-transform-function
2323
* Please log a "breaking change" issue for any API breaking change affecting this issue
2424
*/
25-
"use strict";
2625
exports.__esModule = true;
2726
var ts = require("typescript");
2827
var source = "let x: string = 'string'";

tests/baselines/reference/APISample_watcher.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//// [APISample_watcher.ts]
2-
32
/*
43
* Note: This test is a public API sample. The sample sources can be found
54
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#incremental-build-support-using-the-language-services
@@ -110,12 +109,12 @@ const currentDirectoryFiles = fs.readdirSync(process.cwd()).
110109
watch(currentDirectoryFiles, { module: ts.ModuleKind.CommonJS });
111110

112111
//// [APISample_watcher.js]
112+
"use strict";
113113
/*
114114
* Note: This test is a public API sample. The sample sources can be found
115115
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#incremental-build-support-using-the-language-services
116116
* Please log a "breaking change" issue for any API breaking change affecting this issue
117117
*/
118-
"use strict";
119118
exports.__esModule = true;
120119
var ts = require("typescript");
121120
function watch(rootFileNames, options) {

tests/baselines/reference/DeclarationErrorsNoEmitOnError.errors.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
tests/cases/compiler/DeclarationErrorsNoEmitOnError.ts(4,8): error TS4033: Property 'f' of exported interface has or is using private name 'T'.
1+
tests/cases/compiler/DeclarationErrorsNoEmitOnError.ts(3,8): error TS4033: Property 'f' of exported interface has or is using private name 'T'.
22

33

44
==== tests/cases/compiler/DeclarationErrorsNoEmitOnError.ts (1 errors) ====
5-
65
type T = { x : number }
76
export interface I {
87
f: T;

tests/baselines/reference/ES5For-ofTypeCheck10.errors.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck10.ts(10,6): error TS2304: Cannot find name 'Symbol'.
2-
tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck10.ts(15,15): error TS2495: Type 'StringIterator' is not an array type or a string type.
1+
tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck10.ts(9,6): error TS2304: Cannot find name 'Symbol'.
2+
tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck10.ts(14,15): error TS2495: Type 'StringIterator' is not an array type or a string type.
33

44

55
==== tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck10.ts (2 errors) ====
6-
76
// In ES3/5, you cannot for...of over an arbitrary iterable.
87
class StringIterator {
98
next() {

tests/baselines/reference/ES5For-ofTypeCheck10.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//// [ES5For-ofTypeCheck10.ts]
2-
32
// In ES3/5, you cannot for...of over an arbitrary iterable.
43
class StringIterator {
54
next() {

0 commit comments

Comments
 (0)