From da8fc5d5a90dc18e83a137ba7e04d38854fc1c7d Mon Sep 17 00:00:00 2001 From: Yui Date: Wed, 17 Aug 2016 15:23:28 -0700 Subject: [PATCH] Fix 10289: correctly generate tsconfig.json with --lib (#10355) * Separate generate tsconfig into its own function and implement init with --lib # Conflicts: # src/compiler/tsc.ts * Add tests and baselines; Update function name Add unittests and baselines Add unittests and baselines for generating tsconfig Move unittest into harness folder Update harness tsconfig.json USe correct function name * Use new MapLike interstead. Update unittest # Conflicts: # src/compiler/commandLineParser.ts * Update JakeFile * Add tests for incorrect cases * Address PR : remove explicity write node_modules --- Jakefile.js | 3 +- src/compiler/commandLineParser.ts | 96 +++++++++++++++++++ src/compiler/program.ts | 8 -- src/compiler/tsc.ts | 58 +---------- src/compiler/types.ts | 2 +- src/harness/tsconfig.json | 3 +- src/harness/unittests/initializeTSConfig.ts | 44 +++++++++ .../tsconfig.json | 8 ++ .../tsconfig.json | 9 ++ .../tsconfig.json | 9 ++ .../tsconfig.json | 13 +++ .../tsconfig.json | 12 +++ .../tsconfig.json | 8 ++ .../tsconfig.json | 12 +++ .../tsconfig.json | 12 +++ 15 files changed, 229 insertions(+), 68 deletions(-) create mode 100644 src/harness/unittests/initializeTSConfig.ts 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 incorrect compiler option value/tsconfig.json create mode 100644 tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/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 diff --git a/Jakefile.js b/Jakefile.js index 992839a8661..441f6aef4f9 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -180,7 +180,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([ diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 51dc2b498f1..6406455d713 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -466,6 +466,14 @@ namespace ts { shortOptionNames: Map; } + /* @internal */ + export const defaultInitCompilerOptions: CompilerOptions = { + module: ModuleKind.CommonJS, + target: ScriptTarget.ES5, + noImplicitAny: false, + sourceMap: false, + }; + let optionNameMapCache: OptionNameMap; /* @internal */ @@ -671,6 +679,94 @@ 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; + } + + 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 getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike): 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 (customTypeMap[key] === value) { + return key; + } + } + return undefined; + } + + function serializeCompilerOptions(options: CompilerOptions): Map { + const result = createMap(); + 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(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] = getNameOfCompilerOptionValue(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 1c491aeeaac..e732fe21872 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -828,14 +828,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 8b445bcb626..62b58609086 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -763,67 +763,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 = createMap(); - 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 (typeMap[key] === value) { - result[name] = key; - } - } - } - } - break; - } - } - } - return result; - } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0bbbd99bdd5..7594d2c3fb8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2766,7 +2766,7 @@ namespace ts { /* @internal */ export interface CommandLineOptionOfCustomType extends CommandLineOptionBase { - type: Map; // an object literal mapping named values to actual values + type: Map; // an object literal mapping named values to actual values } /* @internal */ diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 3b9025c27c3..a21faf9dd07 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -89,6 +89,7 @@ "./unittests/convertCompilerOptionsFromJson.ts", "./unittests/convertTypingOptionsFromJson.ts", "./unittests/tsserverProjectSystem.ts", - "./unittests/matchFiles.ts" + "./unittests/matchFiles.ts", + "./unittests/initializeTSConfig.ts" ] } diff --git a/src/harness/unittests/initializeTSConfig.ts b/src/harness/unittests/initializeTSConfig.ts new file mode 100644 index 00000000000..059f07e0115 --- /dev/null +++ b/src/harness/unittests/initializeTSConfig.ts @@ -0,0 +1,44 @@ +/// +/// + +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"]); + + initTSConfigCorrectly("Initialized TSConfig with incorrect compiler option", ["--init", "--someNonExistOption"]); + + initTSConfigCorrectly("Initialized TSConfig with incorrect compiler option value", ["--init", "--lib", "nonExistLib,es5,es2015.promise"]); + }); +} \ No newline at end of file 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 00000000000..ea891967cb5 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + } +} \ 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 00000000000..abe135b4f16 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "noUnusedLocals": true + } +} \ 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 00000000000..e28b66c8c2b --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "jsx": "react" + } +} \ 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 00000000000..5273b3cb7c8 --- /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 incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json new file mode 100644 index 00000000000..fa9cb6cad84 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "lib": [ + "es5", + "es2015.promise" + ] + } +} \ No newline at end of file diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json new file mode 100644 index 00000000000..ea891967cb5 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false + } +} \ 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 00000000000..3ff8208d9d6 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "lib": [ + "es5", + "es2015.core" + ] + } +} \ 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 00000000000..b1740ac4c12 --- /dev/null +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": false, + "types": [ + "jquery", + "mocha" + ] + } +} \ No newline at end of file