diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 8bc95d4e31b..d1a6e56d818 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -312,23 +312,31 @@ namespace ts { if (hasProperty(optionNameMap, s)) { let opt = optionNameMap[s]; - if (opt.type === "boolean") { - // This needs to be treated specially since it doesnt accept argument - options[opt.name] = true; + // Check to see if no argument was provided (e.g. "--locale" is the last command-line argument). + if (!args[i] && opt.type !== "boolean") { + errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_expects_an_argument, opt.name)); } - else { - // Check to see if no argument was provided (e.g. "--locale" is the last command-line argument). - if (!args[i]) { - errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_expects_an_argument, opt.name)); - } - let { hasError, value} = parseOption(opt, args[i++], options[opt.name]); - if (hasError) { - errors.push(createCompilerDiagnostic((opt).error)); - } - else { - options[opt.name] = value; - } + switch (opt.type) { + case "number": + options[opt.name] = parseInt(args[i++]); + break; + case "boolean": + options[opt.name] = true; + break; + case "string": + options[opt.name] = args[i++] || ""; + break; + // If not a primitive, the possible types are specified in what is effectively a map of options. + default: + let map = >opt.type; + let key = (args[i++] || "").toLowerCase(); + if (hasProperty(map, key)) { + options[opt.name] = map[key]; + } + else { + errors.push(createCompilerDiagnostic((opt).error)); + } } } else { @@ -375,68 +383,6 @@ namespace ts { } } - /** - * Parses non quoted strings separated by comma e.g. "a,b" would result in string array ["a", "b"] - * @param s - * @param existingValue - */ - function parseMultiValueStringArray(s: string, existingValue: string[]) { - let value: string[] = existingValue || []; - let hasError = false; - let currentString = ""; - if (s) { - for (let i = 0; i < s.length; i++) { - let ch = s.charCodeAt(i); - if (ch === CharacterCodes.comma) { - pushCurrentStringToResult(); - } - else { - currentString += s.charAt(i); - } - } - // push last string - pushCurrentStringToResult(); - } - return { value, hasError }; - - function pushCurrentStringToResult() { - if (currentString) { - value.push(currentString); - currentString = ""; - } - else { - hasError = true; - } - } - } - - /* @internal */ - export function parseOption(option: CommandLineOption, stringValue: string, existingValue: CompilerOptionsValueType) { - let hasError: boolean; - let value: CompilerOptionsValueType; - switch (option.type) { - case "number": - value = parseInt(stringValue); - break; - case "string": - value = stringValue || ""; - break; - case "string[]": - return parseMultiValueStringArray(stringValue, existingValue); - // If not a primitive, the possible types are specified in what is effectively a map of options. - default: - let map = >option.type; - let key = (stringValue || "").toLowerCase(); - if (hasProperty(map, key)) { - value = map[key]; - } - else { - hasError = true; - } - } - return { hasError, value }; - } - /** * Read tsconfig.json file * @param fileName The path to the config file @@ -466,44 +412,11 @@ namespace ts { } } - /* @internal */ - export function parseJsonCompilerOption(opt: CommandLineOption, jsonValue: any, errors: Diagnostic[]) { - let optType = opt.type; - let expectedType = typeof optType === "string" ? optType : "string"; - let hasValidValue = true; - if (typeof jsonValue === expectedType) { - if (typeof optType !== "string") { - let key = jsonValue.toLowerCase(); - if (hasProperty(optType, key)) { - jsonValue = optType[key]; - } - else { - errors.push(createCompilerDiagnostic((opt).error)); - jsonValue = 0; - } - } - } - // Check if the value asked was string[] and value provided was not string[] - else if (expectedType !== "string[]" || - !(jsonValue instanceof Array) || - forEach(jsonValue, individualValue => typeof individualValue !== "string")) { - // Not expectedType - errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.name, expectedType)); - hasValidValue = false; - } - - return { - value: jsonValue, - hasValidValue - }; - } - /** * Parse the contents of a config file (tsconfig.json). * @param json The contents of the config file to parse * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir - * @param existingOptions optional existing options to extend into */ export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}): ParsedCommandLine { let errors: Diagnostic[] = []; @@ -526,16 +439,31 @@ namespace ts { for (let id in jsonOptions) { if (hasProperty(optionNameMap, id)) { let opt = optionNameMap[id]; - let { hasValidValue, value } = parseJsonCompilerOption(opt, jsonOptions[id], errors); - if (hasValidValue) { + let optType = opt.type; + let value = jsonOptions[id]; + let expectedType = typeof optType === "string" ? optType : "string"; + if (typeof value === expectedType) { + if (typeof optType !== "string") { + let key = value.toLowerCase(); + if (hasProperty(optType, key)) { + value = optType[key]; + } + else { + errors.push(createCompilerDiagnostic((opt).error)); + value = 0; + } + } if (opt.isFilePath) { - value = normalizePath(combinePaths(basePath, value)); + value = normalizePath(combinePaths(basePath, value)); if (value === "") { value = "."; } } options[opt.name] = value; } + else { + errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, id, expectedType)); + } } else { errors.push(createCompilerDiagnostic(Diagnostics.Unknown_compiler_option_0, id)); diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index e05f4f3f5db..90e15d61c46 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -588,8 +588,8 @@ namespace ts { return; - function serializeCompilerOptions(options: CompilerOptions): Map { - let result: Map = {}; + function serializeCompilerOptions(options: CompilerOptions): Map { + let result: Map = {}; let optionsNameMap = getOptionNameMap().optionNameMap; for (let name in options) { @@ -606,8 +606,8 @@ namespace ts { let optionDefinition = optionsNameMap[name.toLowerCase()]; if (optionDefinition) { if (typeof optionDefinition.type === "string") { - // string, number, boolean or string[] - result[name] = value; + // string, number or boolean + result[name] = value; } else { // Enum diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 15caa8b529c..5d41a60526a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2095,11 +2095,9 @@ namespace ts { // Skip checking lib.d.ts to help speed up tests. /* @internal */ skipDefaultLibCheck?: boolean; - [option: string]: CompilerOptionsValueType; + [option: string]: string | number | boolean; } - export type CompilerOptionsValueType = string | number | boolean | string[]; - export const enum ModuleKind { None = 0, CommonJS = 1, @@ -2166,7 +2164,7 @@ namespace ts { /* @internal */ export interface CommandLineOptionOfCustomType extends CommandLineOptionBase { - type: Map | string; // an object literal mapping named values to actual values | string if it is string[] + type: Map; // an object literal mapping named values to actual values error: DiagnosticMessage; // The error given when the argument does not fit a customized 'type' } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index fab3f649f2a..7fac374c09c 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1006,17 +1006,23 @@ namespace Harness { } let option = getCommandLineOption(name); if (option) { - if (option.type === "boolean") { - options[option.name] = value.toLowerCase() === "true"; - } - else { - let { hasError, value: parsedValue } = ts.parseOption(option, value, options[option.name]); - if (hasError) { - throw new Error(`Unknown value '${value}' for compiler option '${name}'.`); - } - else { - options[option.name] = parsedValue; - } + switch (option.type) { + case "boolean": + options[option.name] = value.toLowerCase() === "true"; + break; + case "string": + options[option.name] = value; + break; + // If not a primitive, the possible types are specified in what is effectively a map of options. + default: + let map = >option.type; + let key = value.toLowerCase(); + if (ts.hasProperty(map, key)) { + options[option.name] = map[key]; + } + else { + throw new Error(`Unknown value '${value}' for compiler option '${name}'.`); + } } } else { diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 51b8ddbf86e..f596b421e18 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -3,7 +3,7 @@ /* tslint:disable:no-null */ // Test case is json of below type in tests/cases/project/ -interface ProjectRunnerTestCase extends ts.CompilerOptions { +interface ProjectRunnerTestCase { scenario: string; projectRoot: string; // project where it lives - this also is the current directory when compiling inputFiles: string[]; // list of input files to be given to program @@ -51,7 +51,7 @@ class ProjectRunner extends RunnerBase { } private runProjectTestCase(testCaseFileName: string) { - let testCase: ProjectRunnerTestCase; + let testCase: ProjectRunnerTestCase & ts.CompilerOptions; let testFileText: string = null; try { @@ -62,7 +62,7 @@ class ProjectRunner extends RunnerBase { } try { - testCase = JSON.parse(testFileText); + testCase = JSON.parse(testFileText); } catch (e) { assert(false, "Testcase: " + testCaseFileName + " does not contain valid json format: " + e.message); @@ -183,13 +183,7 @@ class ProjectRunner extends RunnerBase { let outputFiles: BatchCompileProjectTestCaseEmittedFile[] = []; let inputFiles = testCase.inputFiles; - let { errors, compilerOptions } = createCompilerOptions(); - if (errors.length) { - return { - moduleKind, - errors - }; - } + let compilerOptions = createCompilerOptions(); let configFileName: string; if (compilerOptions.project) { @@ -240,7 +234,6 @@ class ProjectRunner extends RunnerBase { module: moduleKind, moduleResolution: ts.ModuleResolutionKind.Classic, // currently all tests use classic module resolution kind, this will change in the future }; - let errors: ts.Diagnostic[] = []; // Set the values specified using json let optionNameMap: ts.Map = {}; ts.forEach(ts.optionDeclarations, option => { @@ -249,14 +242,19 @@ class ProjectRunner extends RunnerBase { for (let name in testCase) { if (name !== "mapRoot" && name !== "sourceRoot" && ts.hasProperty(optionNameMap, name)) { let option = optionNameMap[name]; - let { hasValidValue, value } = ts.parseJsonCompilerOption(option, testCase[name], errors); - if (hasValidValue) { - compilerOptions[option.name] = value; + let optType = option.type; + let value = testCase[name]; + if (typeof optType !== "string") { + let key = value.toLowerCase(); + if (ts.hasProperty(optType, key)) { + value = optType[key]; + } } + compilerOptions[option.name] = value; } } - return { errors, compilerOptions }; + return compilerOptions; } function getFileNameInTheProjectTest(fileName: string): string {