From fa52f36c4f3eead562a50e6910d8f875bb3f3d99 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 11 Aug 2016 16:33:34 -0700 Subject: [PATCH 1/5] wip --- src/compiler/tsc.ts | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 15c52c508cd48..cf7cac95b78cd 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -778,6 +778,19 @@ namespace ts { return; + function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { + if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") { + // this is of a type CommandLineOptionOfPrimitiveType + return undefined; + } + else if (optionDefinition.type === "list") { + return getCustomTypeMapOfCommandLineOption((optionDefinition).element); + } + else { + return (optionDefinition).type + } + } + function serializeCompilerOptions(options: CompilerOptions): Map { const result: Map = {}; const optionsNameMap = getOptionNameMap().optionNameMap; @@ -786,7 +799,6 @@ namespace ts { if (hasProperty(options, name)) { // tsconfig only options cannot be specified via command line, // so we can assume that only types that can appear here string | number | boolean - const value = options[name]; switch (name) { case "init": case "watch": @@ -795,21 +807,27 @@ namespace ts { case "project": break; default: + const value = options[name]; let optionDefinition = optionsNameMap[name.toLowerCase()]; if (optionDefinition) { - if (typeof optionDefinition.type === "string") { - // string, number or boolean - result[name] = value; + const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); + if (!customTypeMap) { + // optionDefinition.type is "string" | "number" | "boolean" then use the value as-is + result[name] = value; } else { - // Enum - const typeMap = >optionDefinition.type; - for (const key in typeMap) { - if (hasProperty(typeMap, key)) { - if (typeMap[key] === value) - result[name] = key; + if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { + // There is a typeMap associated with this command-line option so use it to map value back to its name + for (const key in customTypeMap) { + if (hasProperty(customTypeMap, key)) { + if (customTypeMap[key] === value) + result[name] = key; + } } } + else { + // The value is of type list + } } } break; From f28c1b99d316760469c207fa63290e70c2d29959 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Fri, 12 Aug 2016 16:06:49 -0700 Subject: [PATCH 2/5] Separate generate tsconfig into its own function and implement init with --lib --- src/compiler/commandLineParser.ts | 104 ++++++++++++++++++++++++++++++ src/compiler/program.ts | 8 --- src/compiler/tsc.ts | 59 +---------------- 3 files changed, 105 insertions(+), 66 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 72fd5ff6fd7a7..00d627caaf236 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -458,6 +458,14 @@ namespace ts { shortOptionNames: Map; } + /* @internal */ + export const defaultInitCompilerOptions: CompilerOptions = { + module: ModuleKind.CommonJS, + target: ScriptTarget.ES5, + noImplicitAny: false, + sourceMap: false, + }; + let optionNameMapCache: OptionNameMap; /* @internal */ @@ -664,6 +672,102 @@ namespace ts { } } + /** + * Generate tsconfig configuration when running command line "--init" + * @param options commandlineOptions to be generated into tsconfig.json + * @param fileNames array of filenames to be generated into tsconfig.json + */ + /* @internal */ + export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map } { + const compilerOptions = extend(options, defaultInitCompilerOptions); + const configurations: any = { + compilerOptions: serializeCompilerOptions(compilerOptions) + }; + if (fileNames && fileNames.length) { + // only set the files property if we have at least one file + configurations.files = fileNames; + } + else { + configurations.exclude = ["node_modules"]; + if (compilerOptions.outDir) { + configurations.exclude.push(compilerOptions.outDir); + } + } + + return configurations; + + function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { + if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") { + // this is of a type CommandLineOptionOfPrimitiveType + return undefined; + } + else if (optionDefinition.type === "list") { + return getCustomTypeMapOfCommandLineOption((optionDefinition).element); + } + else { + return (optionDefinition).type; + } + } + + function getKeyOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { + // There is a typeMap associated with this command-line option so use it to map value back to its name + for (const key in customTypeMap) { + if (hasProperty(customTypeMap, key)) { + if (customTypeMap[key] === value) { + return key; + } + } + } + return undefined; + } + + function serializeCompilerOptions(options: CompilerOptions): Map { + const result: Map = {}; + const optionsNameMap = getOptionNameMap().optionNameMap; + + for (const name in options) { + if (hasProperty(options, name)) { + // tsconfig only options cannot be specified via command line, + // so we can assume that only types that can appear here string | number | boolean + switch (name) { + case "init": + case "watch": + case "version": + case "help": + case "project": + break; + default: + const value = options[name]; + let optionDefinition = optionsNameMap[name.toLowerCase()]; + if (optionDefinition) { + const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); + if (!customTypeMap) { + // There is no map associated with this compiler option then use the value as-is + // This is the case if the value is expect to be string, number, boolean or list of string + result[name] = value; + } + else { + if (optionDefinition.type === "list") { + const convertedValue: string[] = []; + for (const element of value as (string | number)[]) { + convertedValue.push(getKeyOfCompilerOptionValue(element, customTypeMap)); + } + result[name] = convertedValue; + } + else { + // There is a typeMap associated with this command-line option so use it to map value back to its name + result[name] = getKeyOfCompilerOptionValue(value, customTypeMap); + } + } + } + break; + } + } + } + return result; + } + } + /** * Remove the comments from a json like text. * Comments can be single line comments (starting with # or //) or multiline comments using / * * / diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a254b04d17410..ecbe125c55dbe 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -842,14 +842,6 @@ namespace ts { : { resolvedModule: undefined, failedLookupLocations }; } - /* @internal */ - export const defaultInitCompilerOptions: CompilerOptions = { - module: ModuleKind.CommonJS, - target: ScriptTarget.ES5, - noImplicitAny: false, - sourceMap: false, - }; - interface OutputFingerprint { hash: string; byteOrderMark: boolean; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 15c52c508cd48..c7eef1fcae22f 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -756,68 +756,11 @@ namespace ts { reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file), /* host */ undefined); } else { - const compilerOptions = extend(options, defaultInitCompilerOptions); - const configurations: any = { - compilerOptions: serializeCompilerOptions(compilerOptions) - }; - - if (fileNames && fileNames.length) { - // only set the files property if we have at least one file - configurations.files = fileNames; - } - else { - configurations.exclude = ["node_modules"]; - if (compilerOptions.outDir) { - configurations.exclude.push(compilerOptions.outDir); - } - } - - sys.writeFile(file, JSON.stringify(configurations, undefined, 4)); + sys.writeFile(file, JSON.stringify(generateTSConfig(options, fileNames), undefined, 4)); reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file), /* host */ undefined); } return; - - function serializeCompilerOptions(options: CompilerOptions): Map { - const result: Map = {}; - const optionsNameMap = getOptionNameMap().optionNameMap; - - for (const name in options) { - if (hasProperty(options, name)) { - // tsconfig only options cannot be specified via command line, - // so we can assume that only types that can appear here string | number | boolean - const value = options[name]; - switch (name) { - case "init": - case "watch": - case "version": - case "help": - case "project": - break; - default: - let optionDefinition = optionsNameMap[name.toLowerCase()]; - if (optionDefinition) { - if (typeof optionDefinition.type === "string") { - // string, number or boolean - result[name] = value; - } - else { - // Enum - const typeMap = >optionDefinition.type; - for (const key in typeMap) { - if (hasProperty(typeMap, key)) { - if (typeMap[key] === value) - result[name] = key; - } - } - } - } - break; - } - } - } - return result; - } } } From c649c9da6be915158f86fe656a5addb3cb493cad Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Fri, 12 Aug 2016 16:07:56 -0700 Subject: [PATCH 3/5] Add unittests and baselines for generating tsconfig --- .../tsconfig.json | 11 +++++ .../tsconfig.json | 12 ++++++ .../tsconfig.json | 12 ++++++ .../tsconfig.json | 13 ++++++ .../tsconfig.json | 15 +++++++ .../tsconfig.json | 15 +++++++ tests/cases/unittests/initializeTSConfig.ts | 40 +++++++++++++++++++ 7 files changed, 118 insertions(+) create mode 100644 tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json create mode 100644 tests/cases/unittests/initializeTSConfig.ts diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json new file mode 100644 index 0000000000000..8e1a28308cf20 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json new file mode 100644 index 0000000000000..74d76539a5e4b --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "noUnusedLocals": true, + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json new file mode 100644 index 0000000000000..f52e59032ff23 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es5", + "jsx": "react", + "module": "commonjs", + "noImplicitAny": false, + "sourceMap": false + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json new file mode 100644 index 0000000000000..5273b3cb7c8ed --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + }, + "files": [ + "file0.st", + "file1.ts", + "file2.ts" + ] +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json new file mode 100644 index 0000000000000..ac18f2a3f3266 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "lib": [ + "es5", + "es2015.core" + ], + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json new file mode 100644 index 0000000000000..caeb572e1b726 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "types": [ + "jquery", + "mocha" + ], + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/tests/cases/unittests/initializeTSConfig.ts b/tests/cases/unittests/initializeTSConfig.ts new file mode 100644 index 0000000000000..2d742313f27a0 --- /dev/null +++ b/tests/cases/unittests/initializeTSConfig.ts @@ -0,0 +1,40 @@ +/// +/// + +namespace ts { + describe("initTSConfig", () => { + function initTSConfigCorrectly(name: string, commandLinesArgs: string[]) { + describe(name, () => { + const commandLine = parseCommandLine(commandLinesArgs); + const initResult = generateTSConfig(commandLine.options, commandLine.fileNames); + const outputFileName = `tsConfig/${name.replace(/[^a-z0-9\-. ]/ig, "")}/tsconfig.json`; + + it(`Correct output for ${outputFileName}`, () => { + Harness.Baseline.runBaseline("Correct output", outputFileName, () => { + if (initResult) { + return JSON.stringify(initResult, undefined, 4); + } + else { + // This can happen if compiler recieve invalid compiler-options + /* tslint:disable:no-null-keyword */ + return null; + /* tslint:enable:no-null-keyword */ + } + }); + }); + }); + } + + initTSConfigCorrectly("Default initialized TSConfig", ["--init"]); + + initTSConfigCorrectly("Initialized TSConfig with files options", ["--init", "file0.st", "file1.ts", "file2.ts"]); + + initTSConfigCorrectly("Initialized TSConfig with boolean value compiler options", ["--init", "--noUnusedLocals"]); + + initTSConfigCorrectly("Initialized TSConfig with enum value compiler options", ["--init", "--target", "es5", "--jsx", "react"]); + + initTSConfigCorrectly("Initialized TSConfig with list compiler options", ["--init", "--types", "jquery,mocha"]); + + initTSConfigCorrectly("Initialized TSConfig with list compiler options with enum value", ["--init", "--lib", "es5,es2015.core"]); + }); +} \ No newline at end of file From 996fc080f61771bf0c6ca4373de2b208b906918c Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Fri, 12 Aug 2016 16:08:16 -0700 Subject: [PATCH 4/5] Update jake and gulp files --- Gulpfile.ts | 1 + Jakefile.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 304c5ff876862..fa7409eabb38d 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -159,6 +159,7 @@ const harnessSources = harnessCoreSources.concat([ "convertTypingOptionsFromJson.ts", "tsserverProjectSystem.ts", "matchFiles.ts", + "initializeTSConfig.ts", ].map(function (f) { return path.join(unittestsDirectory, f); })).concat([ diff --git a/Jakefile.js b/Jakefile.js index 828f4b084d1fa..bc344e72dd80f 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -157,7 +157,8 @@ var harnessSources = harnessCoreSources.concat([ "convertCompilerOptionsFromJson.ts", "convertTypingOptionsFromJson.ts", "tsserverProjectSystem.ts", - "matchFiles.ts" + "matchFiles.ts", + "initializeTSConfig.ts", ].map(function (f) { return path.join(unittestsDirectory, f); })).concat([ From 4dd2102bd8b4cd281108f8be9d0e9fd771ec8d85 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Mon, 15 Aug 2016 08:59:04 -0700 Subject: [PATCH 5/5] Address PR --- src/compiler/commandLineParser.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 00d627caaf236..f95914ad5cbc4 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -709,16 +709,15 @@ namespace ts { } } - function getKeyOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { - // There is a typeMap associated with this command-line option so use it to map value back to its name - for (const key in customTypeMap) { - if (hasProperty(customTypeMap, key)) { - if (customTypeMap[key] === value) { - return key; - } + function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { + // There is a typeMap associated with this command-line option so use it to map value back to its name + const name = forEachKey(customTypeMap, (key) => { + if (customTypeMap[key] === value) { + return key; } - } - return undefined; + }); + + return name ? name : undefined; } function serializeCompilerOptions(options: CompilerOptions): Map { @@ -750,13 +749,13 @@ namespace ts { if (optionDefinition.type === "list") { const convertedValue: string[] = []; for (const element of value as (string | number)[]) { - convertedValue.push(getKeyOfCompilerOptionValue(element, customTypeMap)); + convertedValue.push(getNameOfCompilerOptionValue(element, customTypeMap)); } result[name] = convertedValue; } else { // There is a typeMap associated with this command-line option so use it to map value back to its name - result[name] = getKeyOfCompilerOptionValue(value, customTypeMap); + result[name] = getNameOfCompilerOptionValue(value, customTypeMap); } } }