Skip to content

Commit e1fa3c5

Browse files
committed
Merge pull request microsoft#3982 from Microsoft/sourceMapsInTranspile
add transpileModule function that can return emitted source map
2 parents c5c7e25 + 500cada commit e1fa3c5

12 files changed

+165
-66
lines changed

src/compiler/diagnosticInformationMap.generated.ts

-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,6 @@ namespace ts {
509509
Option_noEmit_cannot_be_specified_with_option_out_or_outDir: { code: 5040, category: DiagnosticCategory.Error, key: "Option 'noEmit' cannot be specified with option 'out' or 'outDir'." },
510510
Option_noEmit_cannot_be_specified_with_option_declaration: { code: 5041, category: DiagnosticCategory.Error, key: "Option 'noEmit' cannot be specified with option 'declaration'." },
511511
Option_project_cannot_be_mixed_with_source_files_on_a_command_line: { code: 5042, category: DiagnosticCategory.Error, key: "Option 'project' cannot be mixed with source files on a command line." },
512-
Option_sourceMap_cannot_be_specified_with_option_isolatedModules: { code: 5043, category: DiagnosticCategory.Error, key: "Option 'sourceMap' cannot be specified with option 'isolatedModules'." },
513512
Option_declaration_cannot_be_specified_with_option_isolatedModules: { code: 5044, category: DiagnosticCategory.Error, key: "Option 'declaration' cannot be specified with option 'isolatedModules'." },
514513
Option_noEmitOnError_cannot_be_specified_with_option_isolatedModules: { code: 5045, category: DiagnosticCategory.Error, key: "Option 'noEmitOnError' cannot be specified with option 'isolatedModules'." },
515514
Option_out_cannot_be_specified_with_option_isolatedModules: { code: 5046, category: DiagnosticCategory.Error, key: "Option 'out' cannot be specified with option 'isolatedModules'." },

src/compiler/diagnosticMessages.json

-4
Original file line numberDiff line numberDiff line change
@@ -2025,10 +2025,6 @@
20252025
"category": "Error",
20262026
"code": 5042
20272027
},
2028-
"Option 'sourceMap' cannot be specified with option 'isolatedModules'.": {
2029-
"category": "Error",
2030-
"code": 5043
2031-
},
20322028
"Option 'declaration' cannot be specified with option 'isolatedModules'.": {
20332029
"category": "Error",
20342030
"code": 5044

src/compiler/program.ts

-4
Original file line numberDiff line numberDiff line change
@@ -602,10 +602,6 @@ namespace ts {
602602

603603
function verifyCompilerOptions() {
604604
if (options.isolatedModules) {
605-
if (options.sourceMap) {
606-
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_sourceMap_cannot_be_specified_with_option_isolatedModules));
607-
}
608-
609605
if (options.declaration) {
610606
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_declaration_cannot_be_specified_with_option_isolatedModules));
611607
}

src/services/services.ts

+48-15
Original file line numberDiff line numberDiff line change
@@ -1760,18 +1760,31 @@ namespace ts {
17601760
sourceFile.version = version;
17611761
sourceFile.scriptSnapshot = scriptSnapshot;
17621762
}
1763-
1763+
1764+
export interface TranspileOptions {
1765+
compilerOptions?: CompilerOptions;
1766+
fileName?: string;
1767+
reportDiagnostics?: boolean;
1768+
moduleName?: string;
1769+
}
1770+
1771+
export interface TranspileOutput {
1772+
outputText: string;
1773+
diagnostics?: Diagnostic[];
1774+
sourceMapText?: string;
1775+
}
1776+
17641777
/*
17651778
* This function will compile source text from 'input' argument using specified compiler options.
17661779
* If not options are provided - it will use a set of default compiler options.
1767-
* Extra compiler options that will unconditionally be used bu this function are:
1780+
* Extra compiler options that will unconditionally be used by this function are:
17681781
* - isolatedModules = true
17691782
* - allowNonTsExtensions = true
17701783
* - noLib = true
17711784
* - noResolve = true
1772-
*/
1773-
export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string {
1774-
let options = compilerOptions ? clone(compilerOptions) : getDefaultCompilerOptions();
1785+
*/
1786+
export function transpileModule(input: string, transpileOptions?: TranspileOptions): TranspileOutput {
1787+
let options = transpileOptions.compilerOptions ? clone(transpileOptions.compilerOptions) : getDefaultCompilerOptions();
17751788

17761789
options.isolatedModules = true;
17771790

@@ -1787,23 +1800,30 @@ namespace ts {
17871800
options.noResolve = true;
17881801

17891802
// Parse
1790-
let inputFileName = fileName || "module.ts";
1803+
let inputFileName = transpileOptions.fileName || "module.ts";
17911804
let sourceFile = createSourceFile(inputFileName, input, options.target);
1792-
if (moduleName) {
1793-
sourceFile.moduleName = moduleName;
1805+
if (transpileOptions.moduleName) {
1806+
sourceFile.moduleName = transpileOptions.moduleName;
17941807
}
17951808

17961809
let newLine = getNewLineCharacter(options);
17971810

17981811
// Output
17991812
let outputText: string;
1813+
let sourceMapText: string;
18001814

18011815
// Create a compilerHost object to allow the compiler to read and write files
18021816
let compilerHost: CompilerHost = {
18031817
getSourceFile: (fileName, target) => fileName === inputFileName ? sourceFile : undefined,
18041818
writeFile: (name, text, writeByteOrderMark) => {
1805-
Debug.assert(outputText === undefined, "Unexpected multiple outputs for the file: " + name);
1806-
outputText = text;
1819+
if (fileExtensionIs(name, ".map")) {
1820+
Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`);
1821+
sourceMapText = text;
1822+
}
1823+
else {
1824+
Debug.assert(outputText === undefined, "Unexpected multiple outputs for the file: " + name);
1825+
outputText = text;
1826+
}
18071827
},
18081828
getDefaultLibFileName: () => "lib.d.ts",
18091829
useCaseSensitiveFileNames: () => false,
@@ -1813,16 +1833,29 @@ namespace ts {
18131833
};
18141834

18151835
let program = createProgram([inputFileName], options, compilerHost);
1816-
1817-
addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile));
1818-
addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics());
1819-
1836+
1837+
let diagnostics: Diagnostic[];
1838+
if (transpileOptions.reportDiagnostics) {
1839+
diagnostics = [];
1840+
addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile));
1841+
addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics());
1842+
}
18201843
// Emit
18211844
program.emit();
18221845

18231846
Debug.assert(outputText !== undefined, "Output generation failed");
18241847

1825-
return outputText;
1848+
return { outputText, diagnostics, sourceMapText };
1849+
}
1850+
1851+
/*
1852+
* This is a shortcut function for transpileModule - it accepts transpileOptions as parameters and returns only outputText part of the result.
1853+
*/
1854+
export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string {
1855+
let output = transpileModule(input, { compilerOptions, fileName, reportDiagnostics: !!diagnostics, moduleName });
1856+
// addRange correctly handles cases when wither 'from' or 'to' argument is missing
1857+
addRange(diagnostics, output.diagnostics);
1858+
return output.outputText;
18261859
}
18271860

18281861
export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile {

tests/baselines/reference/isolatedModulesSourceMap.errors.txt

-7
This file was deleted.

tests/baselines/reference/isolatedModulesSourceMap.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/baselines/reference/isolatedModulesSourceMap.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/baselines/reference/isolatedModulesSourceMap.sourcemap.txt

+11-5
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,26 @@ sources: isolatedModulesSourceMap.ts
88
emittedFile:tests/cases/compiler/isolatedModulesSourceMap.js
99
sourceFile:isolatedModulesSourceMap.ts
1010
-------------------------------------------------------------------
11-
>>>export var x;
11+
>>>export var x = 1;
1212
1 >
1313
2 >^^^^^^^^^^^
1414
3 > ^
15-
4 > ^
16-
5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^->
15+
4 > ^^^
16+
5 > ^
17+
6 > ^
18+
7 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^->
1719
1 >
1820
>
1921
2 >export var
2022
3 > x
21-
4 > ;
23+
4 > =
24+
5 > 1
25+
6 > ;
2226
1 >Emitted(1, 1) Source(2, 1) + SourceIndex(0)
2327
2 >Emitted(1, 12) Source(2, 12) + SourceIndex(0)
2428
3 >Emitted(1, 13) Source(2, 13) + SourceIndex(0)
25-
4 >Emitted(1, 14) Source(2, 14) + SourceIndex(0)
29+
4 >Emitted(1, 16) Source(2, 16) + SourceIndex(0)
30+
5 >Emitted(1, 17) Source(2, 17) + SourceIndex(0)
31+
6 >Emitted(1, 18) Source(2, 18) + SourceIndex(0)
2632
---
2733
>>>//# sourceMappingURL=isolatedModulesSourceMap.js.map
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
=== tests/cases/compiler/isolatedModulesSourceMap.ts ===
2+
3+
export var x = 1;
4+
>x : Symbol(x, Decl(isolatedModulesSourceMap.ts, 1, 10))
5+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
=== tests/cases/compiler/isolatedModulesSourceMap.ts ===
2+
3+
export var x = 1;
4+
>x : number
5+
>1 : number
6+

tests/cases/compiler/isolatedModulesSourceMap.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
// @target: es6
44

55
// @filename: file1.ts
6-
export var x;
6+
export var x = 1;

tests/cases/unittests/transpile.ts

+91-26
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,120 @@
22

33
module ts {
44
describe("Transpile", () => {
5-
6-
function runTest(input: string, compilerOptions: ts.CompilerOptions = {}, fileName?: string, moduleName?: string, expectedOutput?: string, expectedDiagnosticCodes: number[] = []): void {
7-
let diagnostics: Diagnostic[] = [];
8-
let result = transpile(input, compilerOptions, fileName || "file.ts", diagnostics, moduleName);
9-
5+
6+
interface TranspileTestSettings {
7+
options?: TranspileOptions;
8+
expectedOutput?: string;
9+
expectedDiagnosticCodes?: number[];
10+
}
11+
12+
function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) {
13+
if(!expectedDiagnosticCodes) {
14+
return;
15+
}
16+
1017
for (let i = 0; i < expectedDiagnosticCodes.length; i++) {
1118
assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`);
1219
}
13-
assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected");
14-
15-
if (expectedOutput !== undefined) {
16-
assert.equal(result, expectedOutput);
17-
}
20+
assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected");
1821
}
22+
23+
function test(input: string, testSettings: TranspileTestSettings): void {
24+
let diagnostics: Diagnostic[] = [];
25+
26+
let transpileOptions: TranspileOptions = testSettings.options || {};
27+
let transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName);
28+
29+
transpileOptions.reportDiagnostics = true;
30+
let transpileModuleResult = transpileModule(input, transpileOptions);
31+
32+
checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes);
33+
checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes);
34+
35+
if (testSettings.expectedOutput !== undefined) {
36+
assert.equal(transpileResult, testSettings.expectedOutput);
37+
assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput);
38+
}
39+
40+
// check source maps
41+
if (!transpileOptions.compilerOptions) {
42+
transpileOptions.compilerOptions = {};
43+
}
44+
45+
if (!transpileOptions.fileName) {
46+
transpileOptions.fileName = "file.ts";
47+
}
48+
49+
transpileOptions.compilerOptions.sourceMap = true;
50+
let transpileModuleResultWithSourceMap = transpileModule(input, transpileOptions);
51+
assert.isTrue(transpileModuleResultWithSourceMap.sourceMapText !== undefined);
52+
53+
let expectedSourceMapFileName = removeFileExtension(transpileOptions.fileName) + ".js.map";
54+
let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`;
55+
56+
if (testSettings.expectedOutput !== undefined) {
57+
assert.equal(transpileModuleResultWithSourceMap.outputText, testSettings.expectedOutput + expectedSourceMappingUrlLine);
58+
}
59+
else {
60+
// expected output is not set, just verify that output text has sourceMappingURL as a last line
61+
let output = transpileModuleResultWithSourceMap.outputText;
62+
assert.isTrue(output.length >= expectedSourceMappingUrlLine.length);
63+
if (output.length === expectedSourceMappingUrlLine.length) {
64+
assert.equal(output, expectedSourceMappingUrlLine);
65+
}
66+
else {
67+
let suffix = getNewLineCharacter(transpileOptions.compilerOptions) + expectedSourceMappingUrlLine
68+
assert.isTrue(output.indexOf(suffix, output.length - suffix.length) !== -1);
69+
}
70+
}
1971

72+
}
73+
2074
it("Generates correct compilerOptions diagnostics", () => {
2175
// Expecting 5047: "Option 'isolatedModules' can only be used when either option'--module' is provided or option 'target' is 'ES6' or higher."
22-
runTest(`var x = 0;`, {}, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ [5047]);
76+
test(`var x = 0;`, { expectedDiagnosticCodes: [5047] });
2377
});
2478

2579
it("Generates no diagnostics with valid inputs", () => {
2680
// No errors
27-
runTest(`var x = 0;`, { module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []);
81+
test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS } } });
2882
});
2983

3084
it("Generates no diagnostics for missing file references", () => {
31-
runTest(`/// <reference path="file2.ts" />
32-
var x = 0;`,
33-
{ module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []);
85+
test(`/// <reference path="file2.ts" />
86+
var x = 0;`,
87+
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
3488
});
3589

3690
it("Generates no diagnostics for missing module imports", () => {
37-
runTest(`import {a} from "module2";`,
38-
{ module: ModuleKind.CommonJS }, /*fileName*/ undefined,/*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []);
91+
test(`import {a} from "module2";`,
92+
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
3993
});
4094

4195
it("Generates expected syntactic diagnostics", () => {
42-
runTest(`a b`,
43-
{ module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ [1005]); /// 1005: ';' Expected
96+
test(`a b`,
97+
{ options: { compilerOptions: { module: ModuleKind.CommonJS } }, expectedDiagnosticCodes: [1005] }); /// 1005: ';' Expected
4498
});
4599

46100
it("Does not generate semantic diagnostics", () => {
47-
runTest(`var x: string = 0;`,
48-
{ module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []);
101+
test(`var x: string = 0;`,
102+
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
49103
});
50104

51105
it("Generates module output", () => {
52-
runTest(`var x = 0;`, { module: ModuleKind.AMD }, /*fileName*/ undefined, /*moduleName*/undefined, `define(["require", "exports"], function (require, exports) {\r\n var x = 0;\r\n});\r\n`);
106+
test(`var x = 0;`,
107+
{
108+
options: { compilerOptions: { module: ModuleKind.AMD } },
109+
expectedOutput: `define(["require", "exports"], function (require, exports) {\r\n var x = 0;\r\n});\r\n`
110+
});
53111
});
54112

55113
it("Uses correct newLine character", () => {
56-
runTest(`var x = 0;`, { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed }, /*fileName*/ undefined, /*moduleName*/undefined, `var x = 0;\n`, /*expectedDiagnosticCodes*/ []);
114+
test(`var x = 0;`,
115+
{
116+
options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } },
117+
expectedOutput: `var x = 0;\n`
118+
});
57119
});
58120

59121
it("Sets module name", () => {
@@ -66,12 +128,15 @@ var x = 0;`,
66128
` }\n` +
67129
` }\n` +
68130
`});\n`;
69-
runTest("var x = 1;", { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, /*fileName*/ undefined, "NamedModule", output)
131+
test("var x = 1;",
132+
{
133+
options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" },
134+
expectedOutput: output
135+
})
70136
});
71137

72138
it("No extra errors for file without extension", () => {
73-
runTest(`var x = 0;`, { module: ModuleKind.CommonJS }, "file", /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/[]);
139+
test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS }, fileName: "file" } });
74140
});
75-
76141
});
77142
}

0 commit comments

Comments
 (0)