Skip to content

Commit cd34199

Browse files
tsc --init update (#61813)
1 parent ac03ba4 commit cd34199

File tree

15 files changed

+608
-1354
lines changed

15 files changed

+608
-1354
lines changed

src/compiler/commandLineParser.ts

Lines changed: 122 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import {
4444
filterMutate,
4545
find,
4646
findIndex,
47-
firstOrUndefinedIterator,
4847
flatten,
4948
forEach,
5049
forEachEntry,
@@ -2789,140 +2788,146 @@ function serializeOptionBaseObject(
27892788
return result;
27902789
}
27912790

2792-
/**
2793-
* Generate a list of the compiler options whose value is not the default.
2794-
* @param options compilerOptions to be evaluated.
2795-
/** @internal */
2796-
export function getCompilerOptionsDiffValue(options: CompilerOptions, newLine: string): string {
2797-
const compilerOptionsMap = getSerializedCompilerOption(options);
2798-
return getOverwrittenDefaultOptions();
2799-
2800-
function makePadding(paddingLength: number): string {
2801-
return Array(paddingLength + 1).join(" ");
2802-
}
2803-
2804-
function getOverwrittenDefaultOptions() {
2805-
const result: string[] = [];
2806-
const tab = makePadding(2);
2807-
commandOptionsWithoutBuild.forEach(cmd => {
2808-
if (!compilerOptionsMap.has(cmd.name)) {
2809-
return;
2810-
}
2811-
2812-
const newValue = compilerOptionsMap.get(cmd.name);
2813-
const defaultValue = getDefaultValueForOption(cmd);
2814-
if (newValue !== defaultValue) {
2815-
result.push(`${tab}${cmd.name}: ${newValue}`);
2816-
}
2817-
else if (hasProperty(defaultInitCompilerOptions, cmd.name)) {
2818-
result.push(`${tab}${cmd.name}: ${defaultValue}`);
2819-
}
2820-
});
2821-
return result.join(newLine) + newLine;
2822-
}
2823-
}
2824-
2825-
/**
2826-
* Get the compiler options to be written into the tsconfig.json.
2827-
* @param options commandlineOptions to be included in the compileOptions.
2828-
*/
2829-
function getSerializedCompilerOption(options: CompilerOptions): Map<string, CompilerOptionsValue> {
2830-
const compilerOptions = extend(options, defaultInitCompilerOptions);
2831-
return serializeCompilerOptions(compilerOptions);
2832-
}
28332791
/**
28342792
* Generate tsconfig configuration when running command line "--init"
28352793
* @param options commandlineOptions to be generated into tsconfig.json
2836-
* @param fileNames array of filenames to be generated into tsconfig.json
2837-
*
28382794
* @internal
28392795
*/
2840-
export function generateTSConfig(options: CompilerOptions, fileNames: readonly string[], newLine: string): string {
2841-
const compilerOptionsMap = getSerializedCompilerOption(options);
2842-
return writeConfigurations();
2796+
export function generateTSConfig(options: CompilerOptions, newLine: string): string {
2797+
type PresetValue = string | number | boolean | (string | number | boolean)[];
2798+
2799+
const tab = " ";
2800+
const result: string[] = [];
2801+
const allSetOptions = Object.keys(options).filter(k => k !== "init" && k !== "help" && k !== "watch");
2802+
2803+
result.push(`{`);
2804+
result.push(`${tab}// ${getLocaleSpecificMessage(Diagnostics.Visit_https_Colon_Slash_Slashaka_ms_Slashtsconfig_to_read_more_about_this_file)}`);
2805+
result.push(`${tab}"compilerOptions": {`);
2806+
2807+
emitHeader(Diagnostics.File_Layout);
2808+
emitOption("rootDir", "./src", "optional");
2809+
emitOption("outDir", "./dist", "optional");
2810+
2811+
newline();
2812+
2813+
emitHeader(Diagnostics.Environment_Settings);
2814+
emitHeader(Diagnostics.See_also_https_Colon_Slash_Slashaka_ms_Slashtsconfig_Slashmodule);
2815+
emitOption("module", ModuleKind.NodeNext);
2816+
emitOption("target", ScriptTarget.ESNext);
2817+
emitOption("types", []);
2818+
if (options.lib) {
2819+
emitOption("lib", options.lib);
2820+
}
2821+
emitHeader(Diagnostics.For_nodejs_Colon);
2822+
result.push(`${tab}${tab}// "lib": ["esnext"],`);
2823+
result.push(`${tab}${tab}// "types": ["node"],`);
2824+
emitHeader(Diagnostics.and_npm_install_D_types_Slashnode);
2825+
2826+
newline();
2827+
2828+
emitHeader(Diagnostics.Other_Outputs);
2829+
emitOption("sourceMap", /*defaultValue*/ true);
2830+
emitOption("declaration", /*defaultValue*/ true);
2831+
emitOption("declarationMap", /*defaultValue*/ true);
2832+
2833+
newline();
2834+
2835+
emitHeader(Diagnostics.Stricter_Typechecking_Options);
2836+
emitOption("noUncheckedIndexedAccess", /*defaultValue*/ true);
2837+
emitOption("exactOptionalPropertyTypes", /*defaultValue*/ true);
2838+
2839+
newline();
2840+
2841+
emitHeader(Diagnostics.Style_Options);
2842+
emitOption("noImplicitReturns", /*defaultValue*/ true, "optional");
2843+
emitOption("noImplicitOverride", /*defaultValue*/ true, "optional");
2844+
emitOption("noUnusedLocals", /*defaultValue*/ true, "optional");
2845+
emitOption("noUnusedParameters", /*defaultValue*/ true, "optional");
2846+
emitOption("noFallthroughCasesInSwitch", /*defaultValue*/ true, "optional");
2847+
emitOption("noPropertyAccessFromIndexSignature", /*defaultValue*/ true, "optional");
2848+
2849+
newline();
2850+
2851+
emitHeader(Diagnostics.Recommended_Options);
2852+
emitOption("strict", /*defaultValue*/ true);
2853+
emitOption("jsx", JsxEmit.ReactJSX);
2854+
emitOption("verbatimModuleSyntax", /*defaultValue*/ true);
2855+
emitOption("isolatedModules", /*defaultValue*/ true);
2856+
emitOption("noUncheckedSideEffectImports", /*defaultValue*/ true);
2857+
emitOption("moduleDetection", ModuleDetectionKind.Force);
2858+
emitOption("skipLibCheck", /*defaultValue*/ true);
2859+
2860+
// Write any user-provided options we haven't already
2861+
if (allSetOptions.length > 0) {
2862+
newline();
2863+
while (allSetOptions.length > 0) {
2864+
emitOption(allSetOptions[0], options[allSetOptions[0]]);
2865+
}
2866+
}
28432867

2844-
function makePadding(paddingLength: number): string {
2845-
return Array(paddingLength + 1).join(" ");
2868+
function newline() {
2869+
result.push("");
28462870
}
28472871

2848-
function isAllowedOptionForOutput({ category, name, isCommandLineOnly }: CommandLineOption): boolean {
2849-
// Skip options which do not have a category or have categories which are more niche
2850-
const categoriesToSkip = [Diagnostics.Command_line_Options, Diagnostics.Editor_Support, Diagnostics.Compiler_Diagnostics, Diagnostics.Backwards_Compatibility, Diagnostics.Watch_and_Build_Modes, Diagnostics.Output_Formatting];
2851-
return !isCommandLineOnly && category !== undefined && (!categoriesToSkip.includes(category) || compilerOptionsMap.has(name));
2872+
function emitHeader(header: DiagnosticMessage) {
2873+
result.push(`${tab}${tab}// ${getLocaleSpecificMessage(header)}`);
28522874
}
28532875

2854-
function writeConfigurations() {
2855-
// Filter applicable options to place in the file
2856-
const categorizedOptions = new Map<DiagnosticMessage, CommandLineOption[]>();
2857-
// Set allowed categories in order
2858-
categorizedOptions.set(Diagnostics.Projects, []);
2859-
categorizedOptions.set(Diagnostics.Language_and_Environment, []);
2860-
categorizedOptions.set(Diagnostics.Modules, []);
2861-
categorizedOptions.set(Diagnostics.JavaScript_Support, []);
2862-
categorizedOptions.set(Diagnostics.Emit, []);
2863-
categorizedOptions.set(Diagnostics.Interop_Constraints, []);
2864-
categorizedOptions.set(Diagnostics.Type_Checking, []);
2865-
categorizedOptions.set(Diagnostics.Completeness, []);
2866-
for (const option of optionDeclarations) {
2867-
if (isAllowedOptionForOutput(option)) {
2868-
let listForCategory = categorizedOptions.get(option.category!);
2869-
if (!listForCategory) categorizedOptions.set(option.category!, listForCategory = []);
2870-
listForCategory.push(option);
2871-
}
2876+
// commented = 'always': Always comment this out, even if it's on commandline
2877+
// commented = 'optional': Comment out unless it's on commandline
2878+
// commented = 'never': Never comment this out
2879+
function emitOption<K extends string & keyof CompilerOptions>(setting: K, defaultValue: CompilerOptions[K], commented: "always" | "optional" | "never" = "never") {
2880+
const existingOptionIndex = allSetOptions.indexOf(setting);
2881+
if (existingOptionIndex >= 0) {
2882+
allSetOptions.splice(existingOptionIndex, 1);
28722883
}
28732884

2874-
// Serialize all options and their descriptions
2875-
let marginLength = 0;
2876-
let seenKnownKeys = 0;
2877-
const entries: { value: string; description?: string; }[] = [];
2878-
categorizedOptions.forEach((options, category) => {
2879-
if (entries.length !== 0) {
2880-
entries.push({ value: "" });
2881-
}
2882-
entries.push({ value: `/* ${getLocaleSpecificMessage(category)} */` });
2883-
for (const option of options) {
2884-
let optionName;
2885-
if (compilerOptionsMap.has(option.name)) {
2886-
optionName = `"${option.name}": ${JSON.stringify(compilerOptionsMap.get(option.name))}${(seenKnownKeys += 1) === compilerOptionsMap.size ? "" : ","}`;
2887-
}
2888-
else {
2889-
optionName = `// "${option.name}": ${JSON.stringify(getDefaultValueForOption(option))},`;
2890-
}
2891-
entries.push({
2892-
value: optionName,
2893-
description: `/* ${option.description && getLocaleSpecificMessage(option.description) || option.name} */`,
2894-
});
2895-
marginLength = Math.max(optionName.length, marginLength);
2896-
}
2897-
});
2898-
2899-
// Write the output
2900-
const tab = makePadding(2);
2901-
const result: string[] = [];
2902-
result.push(`{`);
2903-
result.push(`${tab}"compilerOptions": {`);
2904-
result.push(`${tab}${tab}/* ${getLocaleSpecificMessage(Diagnostics.Visit_https_Colon_Slash_Slashaka_ms_Slashtsconfig_to_read_more_about_this_file)} */`);
2905-
result.push("");
2906-
// Print out each row, aligning all the descriptions on the same column.
2907-
for (const entry of entries) {
2908-
const { value, description = "" } = entry;
2909-
result.push(value && `${tab}${tab}${value}${description && (makePadding(marginLength - value.length + 2) + description)}`);
2885+
let comment: boolean;
2886+
if (commented === "always") {
2887+
comment = true;
29102888
}
2911-
if (fileNames.length) {
2912-
result.push(`${tab}},`);
2913-
result.push(`${tab}"files": [`);
2914-
for (let i = 0; i < fileNames.length; i++) {
2915-
result.push(`${tab}${tab}${JSON.stringify(fileNames[i])}${i === fileNames.length - 1 ? "" : ","}`);
2916-
}
2917-
result.push(`${tab}]`);
2889+
else if (commented === "never") {
2890+
comment = false;
29182891
}
29192892
else {
2920-
result.push(`${tab}}`);
2893+
comment = !hasProperty(options, setting);
29212894
}
2922-
result.push(`}`);
29232895

2924-
return result.join(newLine) + newLine;
2896+
const value = (options[setting] ?? defaultValue) as PresetValue;
2897+
if (comment) {
2898+
result.push(`${tab}${tab}// "${setting}": ${formatValueOrArray(setting, value)},`);
2899+
}
2900+
else {
2901+
result.push(`${tab}${tab}"${setting}": ${formatValueOrArray(setting, value)},`);
2902+
}
29252903
}
2904+
2905+
function formatValueOrArray(settingName: string, value: PresetValue): string {
2906+
const option = optionDeclarations.filter(c => c.name === settingName)[0];
2907+
if (!option) Debug.fail(`No option named ${settingName}?`);
2908+
const map = (option.type instanceof Map) ? option.type : undefined;
2909+
if (isArray(value)) {
2910+
// eslint-disable-next-line local/no-in-operator
2911+
const map = ("element" in option && (option.element.type instanceof Map)) ? option.element.type : undefined;
2912+
return `[${value.map(v => formatSingleValue(v, map)).join(", ")}]`;
2913+
}
2914+
else {
2915+
return formatSingleValue(value, map);
2916+
}
2917+
}
2918+
2919+
function formatSingleValue(value: PresetValue, map: Map<string, string | number> | undefined) {
2920+
if (map) {
2921+
value = getNameOfCompilerOptionValue(value as string | number, map) ?? Debug.fail(`No matching value of ${value}`);
2922+
}
2923+
return JSON.stringify(value);
2924+
}
2925+
2926+
result.push(`${tab}}`);
2927+
result.push(`}`);
2928+
result.push(``);
2929+
2930+
return result.join(newLine);
29262931
}
29272932

29282933
/** @internal */
@@ -4256,25 +4261,3 @@ function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption):
42564261
});
42574262
}
42584263
}
4259-
4260-
function getDefaultValueForOption(option: CommandLineOption): {} {
4261-
switch (option.type) {
4262-
case "number":
4263-
return 1;
4264-
case "boolean":
4265-
return true;
4266-
case "string":
4267-
const defaultValue = option.defaultValueDescription;
4268-
return option.isFilePath ? `./${defaultValue && typeof defaultValue === "string" ? defaultValue : ""}` : "";
4269-
case "list":
4270-
return [];
4271-
case "listOrElement":
4272-
return getDefaultValueForOption(option.element);
4273-
case "object":
4274-
return {};
4275-
default:
4276-
const value = firstOrUndefinedIterator(option.type.keys());
4277-
if (value !== undefined) return value;
4278-
return Debug.fail("Expected 'option.type' to have entries.");
4279-
}
4280-
}

src/compiler/diagnosticMessages.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5702,6 +5702,42 @@
57025702
"category": "Message",
57035703
"code": 6283
57045704
},
5705+
"File Layout": {
5706+
"category": "Message",
5707+
"code": 6284
5708+
},
5709+
"Environment Settings": {
5710+
"category": "Message",
5711+
"code": 6285
5712+
},
5713+
"See also https://aka.ms/tsconfig/module": {
5714+
"category": "Message",
5715+
"code": 6286
5716+
},
5717+
"For nodejs:": {
5718+
"category": "Message",
5719+
"code": 6287
5720+
},
5721+
"and npm install -D @types/node": {
5722+
"category": "Message",
5723+
"code": 6290
5724+
},
5725+
"Other Outputs": {
5726+
"category": "Message",
5727+
"code": 6291
5728+
},
5729+
"Stricter Typechecking Options": {
5730+
"category": "Message",
5731+
"code": 6292
5732+
},
5733+
"Style Options": {
5734+
"category": "Message",
5735+
"code": 6293
5736+
},
5737+
"Recommended Options": {
5738+
"category": "Message",
5739+
"code": 6294
5740+
},
57055741

57065742
"Enable project compilation": {
57075743
"category": "Message",

src/compiler/executeCommandLine.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import {
4848
formatMessage,
4949
generateTSConfig,
5050
getBuildOrderFromAnyBuildOrder,
51-
getCompilerOptionsDiffValue,
5251
getConfigFileParsingDiagnostics,
5352
getDiagnosticText,
5453
getErrorSummaryText,
@@ -574,7 +573,7 @@ function executeCommandLineWorker(
574573
}
575574

576575
if (commandLine.options.init) {
577-
writeConfigFile(sys, reportDiagnostic, commandLine.options, commandLine.fileNames);
576+
writeConfigFile(sys, reportDiagnostic, commandLine.options);
578577
return sys.exit(ExitStatus.Success);
579578
}
580579

@@ -1277,17 +1276,15 @@ function writeConfigFile(
12771276
sys: System,
12781277
reportDiagnostic: DiagnosticReporter,
12791278
options: CompilerOptions,
1280-
fileNames: string[],
12811279
) {
12821280
const currentDirectory = sys.getCurrentDirectory();
12831281
const file = normalizePath(combinePaths(currentDirectory, "tsconfig.json"));
12841282
if (sys.fileExists(file)) {
12851283
reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file));
12861284
}
12871285
else {
1288-
sys.writeFile(file, generateTSConfig(options, fileNames, sys.newLine));
1289-
const output: string[] = [sys.newLine, ...getHeader(sys, "Created a new tsconfig.json with:")];
1290-
output.push(getCompilerOptionsDiffValue(options, sys.newLine) + sys.newLine + sys.newLine);
1286+
sys.writeFile(file, generateTSConfig(options, sys.newLine));
1287+
const output: string[] = [sys.newLine, ...getHeader(sys, "Created a new tsconfig.json")];
12911288
output.push(`You can learn more at https://aka.ms/tsconfig` + sys.newLine);
12921289
for (const line of output) {
12931290
sys.write(line);

src/testRunner/unittests/config/initializeTSConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ describe("unittests:: config:: initTSConfig", () => {
55
function initTSConfigCorrectly(name: string, commandLinesArgs: string[]) {
66
describe(name, () => {
77
const commandLine = ts.parseCommandLine(commandLinesArgs);
8-
const initResult = ts.generateTSConfig(commandLine.options, commandLine.fileNames, "\n");
8+
const initResult = ts.generateTSConfig(commandLine.options, "\n");
99
const outputFileName = `config/initTSConfig/${name.replace(/[^a-z0-9\-. ]/gi, "")}/tsconfig.json`;
1010

1111
it(`Correct output for ${outputFileName}`, () => {

0 commit comments

Comments
 (0)