diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index b91399e8ee1..ecea3a76673 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -509,7 +509,6 @@ namespace ts { 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'." }, Option_noEmit_cannot_be_specified_with_option_declaration: { code: 5041, category: DiagnosticCategory.Error, key: "Option 'noEmit' cannot be specified with option 'declaration'." }, 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." }, - Option_sourceMap_cannot_be_specified_with_option_isolatedModules: { code: 5043, category: DiagnosticCategory.Error, key: "Option 'sourceMap' cannot be specified with option 'isolatedModules'." }, Option_declaration_cannot_be_specified_with_option_isolatedModules: { code: 5044, category: DiagnosticCategory.Error, key: "Option 'declaration' cannot be specified with option 'isolatedModules'." }, Option_noEmitOnError_cannot_be_specified_with_option_isolatedModules: { code: 5045, category: DiagnosticCategory.Error, key: "Option 'noEmitOnError' cannot be specified with option 'isolatedModules'." }, Option_out_cannot_be_specified_with_option_isolatedModules: { code: 5046, category: DiagnosticCategory.Error, key: "Option 'out' cannot be specified with option 'isolatedModules'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 9fda73740ba..2765d04f400 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2025,10 +2025,6 @@ "category": "Error", "code": 5042 }, - "Option 'sourceMap' cannot be specified with option 'isolatedModules'.": { - "category": "Error", - "code": 5043 - }, "Option 'declaration' cannot be specified with option 'isolatedModules'.": { "category": "Error", "code": 5044 diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 5ce4846b43b..b8f48a0fd63 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -341,7 +341,7 @@ namespace ts { }); } - function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { + function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { return runWithCancellationToken(() => { if (!isDeclarationFile(sourceFile)) { let resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken); @@ -350,7 +350,7 @@ namespace ts { return ts.getDeclarationDiagnostics(getEmitHost(writeFile), resolver, sourceFile); } }); - } + } function getOptionsDiagnostics(): Diagnostic[] { let allDiagnostics: Diagnostic[] = []; @@ -602,10 +602,6 @@ namespace ts { function verifyCompilerOptions() { if (options.isolatedModules) { - if (options.sourceMap) { - diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_sourceMap_cannot_be_specified_with_option_isolatedModules)); - } - if (options.declaration) { diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_declaration_cannot_be_specified_with_option_isolatedModules)); } diff --git a/src/services/services.ts b/src/services/services.ts index 16f5b0b91fe..de4004000d4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1754,18 +1754,31 @@ namespace ts { sourceFile.version = version; sourceFile.scriptSnapshot = scriptSnapshot; } - + + export interface TranspileOptions { + compilerOptions?: CompilerOptions; + fileName?: string; + reportDiagnostics?: boolean; + moduleName?: string; + } + + export interface TranspileOutput { + outputText: string; + diagnostics?: Diagnostic[]; + sourceMapText?: string; + } + /* * This function will compile source text from 'input' argument using specified compiler options. * If not options are provided - it will use a set of default compiler options. - * Extra compiler options that will unconditionally be used bu this function are: + * Extra compiler options that will unconditionally be used by this function are: * - isolatedModules = true * - allowNonTsExtensions = true * - noLib = true * - noResolve = true - */ - export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string { - let options = compilerOptions ? clone(compilerOptions) : getDefaultCompilerOptions(); + */ + export function transpileModule(input: string, transpileOptions?: TranspileOptions): TranspileOutput { + let options = transpileOptions.compilerOptions ? clone(transpileOptions.compilerOptions) : getDefaultCompilerOptions(); options.isolatedModules = true; @@ -1781,23 +1794,30 @@ namespace ts { options.noResolve = true; // Parse - let inputFileName = fileName || "module.ts"; + let inputFileName = transpileOptions.fileName || "module.ts"; let sourceFile = createSourceFile(inputFileName, input, options.target); - if (moduleName) { - sourceFile.moduleName = moduleName; + if (transpileOptions.moduleName) { + sourceFile.moduleName = transpileOptions.moduleName; } let newLine = getNewLineCharacter(options); // Output let outputText: string; + let sourceMapText: string; // Create a compilerHost object to allow the compiler to read and write files let compilerHost: CompilerHost = { getSourceFile: (fileName, target) => fileName === inputFileName ? sourceFile : undefined, writeFile: (name, text, writeByteOrderMark) => { - Debug.assert(outputText === undefined, "Unexpected multiple outputs for the file: " + name); - outputText = text; + if (fileExtensionIs(name, ".map")) { + Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`); + sourceMapText = text; + } + else { + Debug.assert(outputText === undefined, "Unexpected multiple outputs for the file: " + name); + outputText = text; + } }, getDefaultLibFileName: () => "lib.d.ts", useCaseSensitiveFileNames: () => false, @@ -1807,16 +1827,29 @@ namespace ts { }; let program = createProgram([inputFileName], options, compilerHost); - - addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); - addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); - + + let diagnostics: Diagnostic[]; + if (transpileOptions.reportDiagnostics) { + diagnostics = []; + addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); + addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); + } // Emit program.emit(); Debug.assert(outputText !== undefined, "Output generation failed"); - return outputText; + return { outputText, diagnostics, sourceMapText }; + } + + /* + * This is a shortcut function for transpileModule - it accepts transpileOptions as parameters and returns only outputText part of the result. + */ + export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string { + let output = transpileModule(input, { compilerOptions, fileName, reportDiagnostics: !!diagnostics, moduleName }); + // addRange correctly handles cases when wither 'from' or 'to' argument is missing + addRange(diagnostics, output.diagnostics); + return output.outputText; } export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile { diff --git a/tests/baselines/reference/isolatedModulesSourceMap.errors.txt b/tests/baselines/reference/isolatedModulesSourceMap.errors.txt deleted file mode 100644 index 6383e85ecd5..00000000000 --- a/tests/baselines/reference/isolatedModulesSourceMap.errors.txt +++ /dev/null @@ -1,7 +0,0 @@ -error TS5043: Option 'sourceMap' cannot be specified with option 'isolatedModules'. - - -!!! error TS5043: Option 'sourceMap' cannot be specified with option 'isolatedModules'. -==== tests/cases/compiler/isolatedModulesSourceMap.ts (0 errors) ==== - - export var x; \ No newline at end of file diff --git a/tests/baselines/reference/isolatedModulesSourceMap.js b/tests/baselines/reference/isolatedModulesSourceMap.js index ca6f4b4190e..2722a3ce7ab 100644 --- a/tests/baselines/reference/isolatedModulesSourceMap.js +++ b/tests/baselines/reference/isolatedModulesSourceMap.js @@ -1,7 +1,7 @@ //// [isolatedModulesSourceMap.ts] -export var x; +export var x = 1; //// [isolatedModulesSourceMap.js] -export var x; +export var x = 1; //# sourceMappingURL=isolatedModulesSourceMap.js.map \ No newline at end of file diff --git a/tests/baselines/reference/isolatedModulesSourceMap.js.map b/tests/baselines/reference/isolatedModulesSourceMap.js.map index 8e505dcda7a..9fa3e0da0d6 100644 --- a/tests/baselines/reference/isolatedModulesSourceMap.js.map +++ b/tests/baselines/reference/isolatedModulesSourceMap.js.map @@ -1,2 +1,2 @@ //// [isolatedModulesSourceMap.js.map] -{"version":3,"file":"isolatedModulesSourceMap.js","sourceRoot":"","sources":["isolatedModulesSourceMap.ts"],"names":[],"mappings":"AACA,WAAW,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"isolatedModulesSourceMap.js","sourceRoot":"","sources":["isolatedModulesSourceMap.ts"],"names":[],"mappings":"AACA,WAAW,CAAC,GAAG,CAAC,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/isolatedModulesSourceMap.sourcemap.txt b/tests/baselines/reference/isolatedModulesSourceMap.sourcemap.txt index 5c6b7659bc6..d31445505f7 100644 --- a/tests/baselines/reference/isolatedModulesSourceMap.sourcemap.txt +++ b/tests/baselines/reference/isolatedModulesSourceMap.sourcemap.txt @@ -8,20 +8,26 @@ sources: isolatedModulesSourceMap.ts emittedFile:tests/cases/compiler/isolatedModulesSourceMap.js sourceFile:isolatedModulesSourceMap.ts ------------------------------------------------------------------- ->>>export var x; +>>>export var x = 1; 1 > 2 >^^^^^^^^^^^ 3 > ^ -4 > ^ -5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> +4 > ^^^ +5 > ^ +6 > ^ +7 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-> 1 > > 2 >export var 3 > x -4 > ; +4 > = +5 > 1 +6 > ; 1 >Emitted(1, 1) Source(2, 1) + SourceIndex(0) 2 >Emitted(1, 12) Source(2, 12) + SourceIndex(0) 3 >Emitted(1, 13) Source(2, 13) + SourceIndex(0) -4 >Emitted(1, 14) Source(2, 14) + SourceIndex(0) +4 >Emitted(1, 16) Source(2, 16) + SourceIndex(0) +5 >Emitted(1, 17) Source(2, 17) + SourceIndex(0) +6 >Emitted(1, 18) Source(2, 18) + SourceIndex(0) --- >>>//# sourceMappingURL=isolatedModulesSourceMap.js.map \ No newline at end of file diff --git a/tests/baselines/reference/isolatedModulesSourceMap.symbols b/tests/baselines/reference/isolatedModulesSourceMap.symbols new file mode 100644 index 00000000000..d4ae3c34cf1 --- /dev/null +++ b/tests/baselines/reference/isolatedModulesSourceMap.symbols @@ -0,0 +1,5 @@ +=== tests/cases/compiler/isolatedModulesSourceMap.ts === + +export var x = 1; +>x : Symbol(x, Decl(isolatedModulesSourceMap.ts, 1, 10)) + diff --git a/tests/baselines/reference/isolatedModulesSourceMap.types b/tests/baselines/reference/isolatedModulesSourceMap.types new file mode 100644 index 00000000000..1955fe5da6e --- /dev/null +++ b/tests/baselines/reference/isolatedModulesSourceMap.types @@ -0,0 +1,6 @@ +=== tests/cases/compiler/isolatedModulesSourceMap.ts === + +export var x = 1; +>x : number +>1 : number + diff --git a/tests/cases/compiler/isolatedModulesSourceMap.ts b/tests/cases/compiler/isolatedModulesSourceMap.ts index 31dd4d9f0cf..465941d9a8b 100644 --- a/tests/cases/compiler/isolatedModulesSourceMap.ts +++ b/tests/cases/compiler/isolatedModulesSourceMap.ts @@ -3,4 +3,4 @@ // @target: es6 // @filename: file1.ts -export var x; \ No newline at end of file +export var x = 1; \ No newline at end of file diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index fcce034b227..1fd8c860d01 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -2,58 +2,120 @@ module ts { describe("Transpile", () => { - - function runTest(input: string, compilerOptions: ts.CompilerOptions = {}, fileName?: string, moduleName?: string, expectedOutput?: string, expectedDiagnosticCodes: number[] = []): void { - let diagnostics: Diagnostic[] = []; - let result = transpile(input, compilerOptions, fileName || "file.ts", diagnostics, moduleName); - + + interface TranspileTestSettings { + options?: TranspileOptions; + expectedOutput?: string; + expectedDiagnosticCodes?: number[]; + } + + function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) { + if(!expectedDiagnosticCodes) { + return; + } + for (let i = 0; i < expectedDiagnosticCodes.length; i++) { assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`); } - assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected"); - - if (expectedOutput !== undefined) { - assert.equal(result, expectedOutput); - } + assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected"); } + + function test(input: string, testSettings: TranspileTestSettings): void { + let diagnostics: Diagnostic[] = []; + + let transpileOptions: TranspileOptions = testSettings.options || {}; + let transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName); + + transpileOptions.reportDiagnostics = true; + let transpileModuleResult = transpileModule(input, transpileOptions); + + checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes); + checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes); + + if (testSettings.expectedOutput !== undefined) { + assert.equal(transpileResult, testSettings.expectedOutput); + assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput); + } + + // check source maps + if (!transpileOptions.compilerOptions) { + transpileOptions.compilerOptions = {}; + } + + if (!transpileOptions.fileName) { + transpileOptions.fileName = "file.ts"; + } + + transpileOptions.compilerOptions.sourceMap = true; + let transpileModuleResultWithSourceMap = transpileModule(input, transpileOptions); + assert.isTrue(transpileModuleResultWithSourceMap.sourceMapText !== undefined); + + let expectedSourceMapFileName = removeFileExtension(transpileOptions.fileName) + ".js.map"; + let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`; + + if (testSettings.expectedOutput !== undefined) { + assert.equal(transpileModuleResultWithSourceMap.outputText, testSettings.expectedOutput + expectedSourceMappingUrlLine); + } + else { + // expected output is not set, just verify that output text has sourceMappingURL as a last line + let output = transpileModuleResultWithSourceMap.outputText; + assert.isTrue(output.length >= expectedSourceMappingUrlLine.length); + if (output.length === expectedSourceMappingUrlLine.length) { + assert.equal(output, expectedSourceMappingUrlLine); + } + else { + let suffix = getNewLineCharacter(transpileOptions.compilerOptions) + expectedSourceMappingUrlLine + assert.isTrue(output.indexOf(suffix, output.length - suffix.length) !== -1); + } + } + } + it("Generates correct compilerOptions diagnostics", () => { // Expecting 5047: "Option 'isolatedModules' can only be used when either option'--module' is provided or option 'target' is 'ES6' or higher." - runTest(`var x = 0;`, {}, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ [5047]); + test(`var x = 0;`, { expectedDiagnosticCodes: [5047] }); }); it("Generates no diagnostics with valid inputs", () => { // No errors - runTest(`var x = 0;`, { module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []); + test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS } } }); }); it("Generates no diagnostics for missing file references", () => { - runTest(`/// -var x = 0;`, - { module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []); + test(`/// +var x = 0;`, + { options: { compilerOptions: { module: ModuleKind.CommonJS } } }); }); it("Generates no diagnostics for missing module imports", () => { - runTest(`import {a} from "module2";`, - { module: ModuleKind.CommonJS }, /*fileName*/ undefined,/*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []); + test(`import {a} from "module2";`, + { options: { compilerOptions: { module: ModuleKind.CommonJS } } }); }); it("Generates expected syntactic diagnostics", () => { - runTest(`a b`, - { module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ [1005]); /// 1005: ';' Expected + test(`a b`, + { options: { compilerOptions: { module: ModuleKind.CommonJS } }, expectedDiagnosticCodes: [1005] }); /// 1005: ';' Expected }); it("Does not generate semantic diagnostics", () => { - runTest(`var x: string = 0;`, - { module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []); + test(`var x: string = 0;`, + { options: { compilerOptions: { module: ModuleKind.CommonJS } } }); }); it("Generates module output", () => { - 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`); + test(`var x = 0;`, + { + options: { compilerOptions: { module: ModuleKind.AMD } }, + expectedOutput: `define(["require", "exports"], function (require, exports) {\r\n var x = 0;\r\n});\r\n` + }); }); it("Uses correct newLine character", () => { - runTest(`var x = 0;`, { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed }, /*fileName*/ undefined, /*moduleName*/undefined, `var x = 0;\n`, /*expectedDiagnosticCodes*/ []); + test(`var x = 0;`, + { + options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } }, + expectedOutput: `var x = 0;\n` + }); }); it("Sets module name", () => { @@ -66,12 +128,15 @@ var x = 0;`, ` }\n` + ` }\n` + `});\n`; - runTest("var x = 1;", { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, /*fileName*/ undefined, "NamedModule", output) + test("var x = 1;", + { + options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" }, + expectedOutput: output + }) }); it("No extra errors for file without extension", () => { - runTest(`var x = 0;`, { module: ModuleKind.CommonJS }, "file", /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/[]); + test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS }, fileName: "file" } }); }); - }); }