diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 506a2d5bee42d..55e7a2a3a2fb3 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -6,7 +6,7 @@ namespace ts { /* @internal */ - export let optionDeclarations: CommandLineOption[] = [ + export const optionDeclarations: CommandLineOption[] = [ { name: "charset", type: "string", diff --git a/src/services/services.ts b/src/services/services.ts index 34b521b34961a..0002bb1bacb1d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1920,6 +1920,40 @@ namespace ts { sourceMapText?: string; } + + + let commandLineOptions_stringToEnum: CommandLineOptionOfCustomType[]; + + /** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */ + function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { + // Lazily create this value to fix module loading errors. + commandLineOptions_stringToEnum = commandLineOptions_stringToEnum || filter(optionDeclarations, o => + typeof o.type === "object" && !forEachValue(> o.type, v => typeof v !== "number")); + + options = clone(options); + + for (const opt of commandLineOptions_stringToEnum) { + if (!hasProperty(options, opt.name)) { + continue; + } + + const value = options[opt.name]; + // Value should be a key of opt.type + if (typeof value === "string") { + // If value is not a string, this will fail + options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); + } + else { + if (!forEachValue(opt.type, v => v === value)) { + // Supplied value isn't a valid enum value. + diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); + } + } + } + + return options; + } + /* * 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. @@ -1930,7 +1964,9 @@ namespace ts { * - noResolve = true */ export function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput { - const options = transpileOptions.compilerOptions ? clone(transpileOptions.compilerOptions) : getDefaultCompilerOptions(); + const diagnostics: Diagnostic[] = []; + + const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : getDefaultCompilerOptions(); options.isolatedModules = true; @@ -1988,9 +2024,7 @@ namespace ts { const program = createProgram([inputFileName], options, compilerHost); - let diagnostics: Diagnostic[]; if (transpileOptions.reportDiagnostics) { - diagnostics = []; addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); } diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index 904cc435c440d..a4b1416ed291a 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -7,17 +7,21 @@ namespace ts { options?: TranspileOptions; expectedOutput?: string; expectedDiagnosticCodes?: number[]; + expectedDiagnosticTexts?: string[]; } - function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) { - if (!expectedDiagnosticCodes) { - return; + function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes: number[] = [], expectedDiagnosticTexts?: string[]) { + const n = expectedDiagnosticCodes.length; + if (expectedDiagnosticTexts) { + assert.equal(n, expectedDiagnosticTexts.length); } - - 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"); + for (let i = 0; i < n; i++) { + assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expected diagnostic.`); + if (expectedDiagnosticTexts) { + assert.equal(expectedDiagnosticTexts[i], diagnostics[i] && diagnostics[i].messageText); + } + }; + assert.equal(diagnostics.length, n, "Resuting diagnostics count does not match expected"); } function test(input: string, testSettings: TranspileTestSettings): void { @@ -26,7 +30,7 @@ namespace ts { if (!transpileOptions.compilerOptions) { transpileOptions.compilerOptions = {}; } - if (transpileOptions.compilerOptions.newLine === undefined) { // + if (transpileOptions.compilerOptions.newLine === undefined) { // use \r\n as default new line transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed; } @@ -36,7 +40,7 @@ namespace ts { transpileOptions.reportDiagnostics = true; const transpileModuleResult = transpileModule(input, transpileOptions); - checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes); + checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes, testSettings.expectedDiagnosticTexts); if (testSettings.expectedOutput !== undefined) { assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput); @@ -45,7 +49,7 @@ namespace ts { if (canUseOldTranspile) { const diagnostics: Diagnostic[] = []; const transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName); - checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes); + checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes, testSettings.expectedDiagnosticTexts); if (testSettings.expectedOutput) { assert.equal(transpileResult, testSettings.expectedOutput); } @@ -292,13 +296,37 @@ var x = 0;`, const output = `"use strict";\nvar a = 10;\n`; test(input, { expectedOutput: output, - options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true }, - expectedDiagnosticCodes: [] + options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } }); }); it("Supports urls in file name", () => { test("var x", { expectedOutput: `"use strict";\r\nvar x;\r\n`, options: { fileName: "http://somewhere/directory//directory2/file.ts" } }); }); + + describe("String values for enums", () => { + it("Accepts strings instead of enum values", () => { + test(`export const x = 0`, { + options: { + compilerOptions: { + module: "es6", + // Capitalization and spaces ignored + target: " Es6 " + } + }, + expectedOutput: "export const x = 0;\r\n" + }); + }); + + it("Fails on bad value", () => { + for (const value in [123, {}, ""]) { + test(``, { + options: { compilerOptions: { module: value } }, + expectedDiagnosticCodes: [6046], + expectedDiagnosticTexts: ["Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'"] + }); + } + }); + }); }); }