From 0735f465f0e30444cda2771db6e3ab063abdd1fa Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Thu, 10 Mar 2016 11:14:29 -0800 Subject: [PATCH] Add support for list types consolidate typings options and commmandline option parsing from json files Fix --- src/compiler/commandLineParser.ts | 271 ++++++++++++++++-------------- src/compiler/types.ts | 13 +- 2 files changed, 149 insertions(+), 135 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index c0912394491..36aef619888 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -186,8 +186,8 @@ namespace ts { name: "rootDir", type: "string", isFilePath: true, - description: Diagnostics.Specifies_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir, paramType: Diagnostics.LOCATION, + description: Diagnostics.Specifies_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir, }, { name: "isolatedModules", @@ -268,14 +268,17 @@ namespace ts { error: Diagnostics.Argument_for_moduleResolution_option_must_be_node_or_classic, }, { - name: "list", - elementType: { - "node": ModuleResolutionKind.NodeJs, - "classic": ModuleResolutionKind.Classic, - }, + name: "lib", type: "list", + element: { + name: "lib", + type: { + "node": ModuleResolutionKind.NodeJs, + "classic": ModuleResolutionKind.Classic, + }, + error: Diagnostics.Argument_for_moduleResolution_option_must_be_node_or_classic, + }, description: Diagnostics.Specifies_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6, - error: Diagnostics.Argument_for_moduleResolution_option_must_be_node_or_classic, }, { name: "allowUnusedLabels", @@ -319,9 +322,13 @@ namespace ts { // this option can only be specified in tsconfig.json // use type = object to copy the value as-is name: "rootDirs", - type: "object", + type: "list", isTSConfigOnly: true, - isFilePath: true + element: { + name: "rootDirs", + type: "string", + isFilePath: true + } }, { name: "traceModuleResolution", @@ -345,6 +352,30 @@ namespace ts { } ]; + /* @internal */ + export let typingOptionDeclarations: CommandLineOption[] = [ + { + name: "enableAutoDiscovery", + type: "boolean", + }, + { + name: "include", + type: "list", + element: { + name: "include", + type: "string" + } + }, + { + name: "exclude", + type: "list", + element: { + name: "include", + type: "string" + } + } + ]; + /* @internal */ export interface OptionNameMap { optionNameMap: Map; @@ -401,7 +432,40 @@ namespace ts { } if (hasProperty(optionNameMap, s)) { - parseString(optionNameMap[s], args[i]); + const opt = optionNameMap[s]; + + if (opt.isTSConfigOnly) { + errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name)); + } + else { + // 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)); + } + + switch (opt.type) { + case "number": + options[opt.name] = parseInt(args[i]); + i++; + break; + case "boolean": + options[opt.name] = true; + break; + case "string": + options[opt.name] = args[i] || ""; + i++; + break; + case "list": + options[opt.name] = parseListTypeOption(opt, args[i]); + i++; + break; + // If not a primitive, the possible types are specified in what is effectively a map of options. + default: + options[opt.name] = parseCustomTypeOption(opt, args[i]); + i++; + break; + } + } } else { errors.push(createCompilerDiagnostic(Diagnostics.Unknown_compiler_option_0, s)); @@ -410,40 +474,25 @@ namespace ts { else { fileNames.push(s); } - } - function parseString(opt: CommandLineOption, value: string) { - // Check to see if no argument was provided (e.g. "--locale" is the last command-line argument). - if (!value && opt.type !== "boolean") { - errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_expects_an_argument, opt.name)); + function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string) { + const map = >opt.type; + const key = (value || "").toLowerCase(); + if (hasProperty(map, key)) { + return map[key]; + } + else { + errors.push(createCompilerDiagnostic(opt.error)); + } } - switch (opt.type) { - case "number": - options[opt.name] = parseInt(value); - i++; - break; - case "boolean": - options[opt.name] = true; - break; - case "string": - options[opt.name] = value || ""; - i++; - break; - case "list": - forEach((value || "").split(","), s => parseString(opt.name, opti ); - 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 = (value || "").toLowerCase(); - i++; - if (hasProperty(map, key)) { - options[opt.name] = map[key]; - } - else { - errors.push(createCompilerDiagnostic((opt).error)); - } + function parseListTypeOption(opt: CommandLineOptionOfListType, value: string): number[] | string[] { + const values = (value || "").split(","); + switch (opt.element.type) { + case "number": return ts.map(values, parseInt); + case "string": return ts.map(values, v => v || ""); + default: return ts.map(values, v => parseCustomTypeOption(opt.element, v)); + } } } } @@ -512,7 +561,6 @@ namespace ts { } } - /** * Remove the comments from a json like text. * Comments can be single line comments (starting with # or //) or multiline comments using / * * / @@ -546,18 +594,20 @@ namespace ts { * file to. e.g. outDir */ export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string): ParsedCommandLine { - const { options: optionsFromJsonConfigFile, errors } = convertCompilerOptionsFromJson(json["compilerOptions"], basePath, configFileName); - + const errors: Diagnostic[] = []; + const optionsFromJsonConfigFile = convertOptionsFromJson(optionDeclarations, json["compilerOptions"], basePath, configFileName, errors); const options = extend(existingOptions, optionsFromJsonConfigFile); + const typingOptions = convertOptionsFromJson(typingOptionDeclarations, json["typingOptions"], basePath, configFileName, errors); + const fileNames = getFileNames(errors); return { options, - fileNames: getFileNames(), - typingOptions: getTypingOptions(), + fileNames, + typingOptions, errors }; - function getFileNames(): string[] { + function getFileNames(errors: Diagnostic[]): string[] { let fileNames: string[] = []; if (hasProperty(json, "files")) { if (json["files"] instanceof Array) { @@ -618,47 +668,24 @@ namespace ts { } return fileNames; } - - function getTypingOptions(): TypingOptions { - const options: TypingOptions = getBaseFileName(configFileName) === "jsconfig.json" - ? { enableAutoDiscovery: true, include: [], exclude: [] } - : { enableAutoDiscovery: false, include: [], exclude: [] }; - const jsonTypingOptions = json["typingOptions"]; - if (jsonTypingOptions) { - for (const id in jsonTypingOptions) { - if (id === "enableAutoDiscovery") { - if (typeof jsonTypingOptions[id] === "boolean") { - options.enableAutoDiscovery = jsonTypingOptions[id]; - } - else { - errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id)); - } - } - else if (id === "include") { - options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); - } - else if (id === "exclude") { - options.exclude = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); - } - else { - errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id)); - } - } - } - return options; - } } export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } { - const options: CompilerOptions = {}; const errors: Diagnostic[] = []; + const options = convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, configFileName, errors); - if (configFileName && getBaseFileName(configFileName) === "jsconfig.json") { + if (configFileName && getBaseFileName(configFileName) === "jsconfig.json" && typeof options.allowJs === "undefined") { options.allowJs = true; } + return { options, errors }; + } + + function convertOptionsFromJson(optionDeclarations: CommandLineOption[], jsonOptions: any, basePath: string, configFileName: string, errors: Diagnostic[]): T { + const options = {} as T; + if (!jsonOptions) { - return { options, errors }; + return options; } const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name); @@ -666,69 +693,53 @@ namespace ts { for (const id in jsonOptions) { if (hasProperty(optionNameMap, id)) { const opt = optionNameMap[id]; - const optType = opt.type; - let value = jsonOptions[id]; - const expectedType = typeof optType === "string" ? optType : "string"; - if (typeof value === expectedType) { - if (typeof optType !== "string") { - const key = value.toLowerCase(); - if (hasProperty(optType, key)) { - value = optType[key]; - } - else { - errors.push(createCompilerDiagnostic((opt).error)); - value = 0; - } - } - if (opt.isFilePath) { - switch (typeof value) { - case "string": - value = normalizePath(combinePaths(basePath, value)); - break; - case "object": - // "object" options with 'isFilePath' = true expected to be string arrays - value = convertJsonOptionToStringArray(opt.name, value, errors, (element) => normalizePath(combinePaths(basePath, element))); - break; - } - if (value === "") { - value = "."; - } - } - options[opt.name] = value; - } - else { - errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, id, expectedType)); - } + options[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); } else { errors.push(createCompilerDiagnostic(Diagnostics.Unknown_compiler_option_0, id)); } } - return { options, errors }; + return options; } - function convertJsonOptionToStringArray(optionName: string, optionJson: any, errors: Diagnostic[], func?: (element: string) => string): string[] { - const items: string[] = []; - let invalidOptionType = false; - if (!isArray(optionJson)) { - invalidOptionType = true; + function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Diagnostic[]): number | string | number[] | string[] { + const optType = opt.type; + const expectedType = typeof optType === "string" ? optType : "string"; + if (optType === "list" && isArray(value)) { + return convertJsonOptionOfListType(opt, value, basePath, errors); } - else { - for (const element of optionJson) { - if (typeof element === "string") { - const item = func ? func(element) : element; - items.push(item); - } - else { - invalidOptionType = true; - break; + else if (typeof value === expectedType) { + if (typeof optType !== "string") { + return convertJsonOptionOfCustomType(opt, value, errors); + } + else { + if (opt.isFilePath) { + value = normalizePath(combinePaths(basePath, value)); + if (value === "") { + value = "."; + } } } + return value; } - if (invalidOptionType) { - errors.push(createCompilerDiagnostic(Diagnostics.Option_0_should_have_array_of_strings_as_a_value, optionName)); + else { + errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.name, expectedType)); } - return items; + } + + function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { + const key = value.toLowerCase(); + if (hasProperty(opt.type, key)) { + return opt.type[key]; + } + else { + errors.push(createCompilerDiagnostic(opt.error)); + return 0; + } + } + + function convertJsonOptionOfListType(option: CommandLineOptionOfListType, values: any[], basePath: string, errors: Diagnostic[]): any[] { + return ts.map(values, v => convertJsonOption(option.element, v, basePath, errors)); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6273ea990df..2945fac0b09 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2444,7 +2444,9 @@ namespace ts { // Do not perform validation of output file name in transpile scenarios /* @internal */ suppressOutputPathCheck?: boolean; - [option: string]: string | number | boolean | TsConfigOnlyOptions; + list?: string[]; + + [option: string]: string | number | boolean | TsConfigOnlyOptions | string[] | number[]; } export interface TypingOptions { @@ -2554,13 +2556,14 @@ namespace ts { type: "object"; } - export interface CommandlineOptionOfListType extends CommandLineOptionBase { - type: "list", - elementType: Map | "string" | "number"; + /* @internal */ + export interface CommandLineOptionOfListType extends CommandLineOptionBase { + type: "list"; + element: CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType; } /* @internal */ - export type CommandLineOption = CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType | TsConfigOnlyOption | CommandlineOptionOfListType; + export type CommandLineOption = CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType | TsConfigOnlyOption | CommandLineOptionOfListType; /* @internal */ export const enum CharacterCodes {