internal/execute/tsc/init.go (180 lines of code) (raw):

package tsc import ( "fmt" "reflect" "slices" "strings" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/diagnostics" "github.com/microsoft/typescript-go/internal/jsonutil" "github.com/microsoft/typescript-go/internal/locale" "github.com/microsoft/typescript-go/internal/tsoptions" "github.com/microsoft/typescript-go/internal/tspath" ) func WriteConfigFile(sys System, locale locale.Locale, reportDiagnostic DiagnosticReporter, options *collections.OrderedMap[string, any]) { getCurrentDirectory := sys.GetCurrentDirectory() file := tspath.NormalizePath(tspath.CombinePaths(getCurrentDirectory, "tsconfig.json")) if sys.FS().FileExists(file) { reportDiagnostic(ast.NewCompilerDiagnostic(diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file)) } else { _ = sys.FS().WriteFile(file, generateTSConfig(options, locale), false) output := []string{"\n"} output = append(output, getHeader(sys, "Created a new tsconfig.json")...) output = append(output, "You can learn more at https://aka.ms/tsconfig", "\n") fmt.Fprint(sys.Writer(), strings.Join(output, "")) } } func generateTSConfig(options *collections.OrderedMap[string, any], locale locale.Locale) string { const tab = " " var result []string allSetOptions := make([]string, 0, options.Size()) for k := range options.Keys() { if k != "init" && k != "help" && k != "watch" { allSetOptions = append(allSetOptions, k) } } emitHeader := func(header *diagnostics.Message) { result = append(result, tab+tab+"// "+header.Localize(locale)) } newline := func() { result = append(result, "") } push := func(args ...string) { result = append(result, args...) } formatSingleValue := func(value any, enumMap *collections.OrderedMap[string, any]) string { if enumMap != nil { var found bool for k, v := range enumMap.Entries() { if value == v { value = k found = true break } } if !found { panic(fmt.Sprintf("No matching value of %v", value)) } } b, err := jsonutil.MarshalIndent(value, "", "") if err != nil { panic(fmt.Sprintf("should not happen: %v", err)) } return string(b) } formatValueOrArray := func(settingName string, value any) string { var option *tsoptions.CommandLineOption for _, decl := range tsoptions.OptionsDeclarations { if decl.Name == settingName { option = decl } } if option == nil { panic(`No option named ` + settingName) } rval := reflect.ValueOf(value) if rval.Kind() == reflect.Slice { var enumMap *collections.OrderedMap[string, any] if elemOption := option.Elements(); elemOption != nil { enumMap = elemOption.EnumMap() } var elems []string for i := range rval.Len() { elems = append(elems, formatSingleValue(rval.Index(i).Interface(), enumMap)) } return `[` + strings.Join(elems, ", ") + `]` } else { return formatSingleValue(value, option.EnumMap()) } } // commentedNever': Never comment this out // commentedAlways': Always comment this out, even if it's on commandline // commentedOptional': Comment out unless it's on commandline type commented int const ( commentedNever commented = iota commentedAlways commentedOptional ) emitOption := func(setting string, defaultValue any, commented commented) { if commented > 2 { panic("should not happen: invalid `commented`, must be a bug.") } existingOptionIndex := slices.Index(allSetOptions, setting) if existingOptionIndex >= 0 { allSetOptions = slices.Delete(allSetOptions, existingOptionIndex, existingOptionIndex+1) } var comment bool switch commented { case commentedAlways: comment = true case commentedNever: comment = false default: comment = !options.Has(setting) } value, ok := options.Get(setting) if !ok { value = defaultValue } if comment { push(tab + tab + `// "` + setting + `": ` + formatValueOrArray(setting, value) + `,`) } else { push(tab + tab + `"` + setting + `": ` + formatValueOrArray(setting, value) + `,`) } } push("{") push(tab + `// ` + diagnostics.Visit_https_Colon_Slash_Slashaka_ms_Slashtsconfig_to_read_more_about_this_file.Localize(locale)) push(tab + `"compilerOptions": {`) emitHeader(diagnostics.File_Layout) emitOption("rootDir", "./src", commentedOptional) emitOption("outDir", "./dist", commentedOptional) newline() emitHeader(diagnostics.Environment_Settings) emitHeader(diagnostics.See_also_https_Colon_Slash_Slashaka_ms_Slashtsconfig_Slashmodule) emitOption("module", core.ModuleKindNodeNext, commentedNever) emitOption("target", core.ScriptTargetESNext, commentedNever) emitOption("types", []any{}, commentedNever) if lib, ok := options.Get("lib"); ok { emitOption("lib", lib, commentedNever) } emitHeader(diagnostics.For_nodejs_Colon) push(tab + tab + `// "lib": ["esnext"],`) push(tab + tab + `// "types": ["node"],`) emitHeader(diagnostics.X_and_npm_install_D_types_Slashnode) newline() emitHeader(diagnostics.Other_Outputs) emitOption("sourceMap" /*defaultValue*/, true, commentedNever) emitOption("declaration" /*defaultValue*/, true, commentedNever) emitOption("declarationMap" /*defaultValue*/, true, commentedNever) newline() emitHeader(diagnostics.Stricter_Typechecking_Options) emitOption("noUncheckedIndexedAccess" /*defaultValue*/, true, commentedNever) emitOption("exactOptionalPropertyTypes" /*defaultValue*/, true, commentedNever) newline() emitHeader(diagnostics.Style_Options) emitOption("noImplicitReturns" /*defaultValue*/, true, commentedOptional) emitOption("noImplicitOverride" /*defaultValue*/, true, commentedOptional) emitOption("noUnusedLocals" /*defaultValue*/, true, commentedOptional) emitOption("noUnusedParameters" /*defaultValue*/, true, commentedOptional) emitOption("noFallthroughCasesInSwitch" /*defaultValue*/, true, commentedOptional) emitOption("noPropertyAccessFromIndexSignature" /*defaultValue*/, true, commentedOptional) newline() emitHeader(diagnostics.Recommended_Options) emitOption("strict" /*defaultValue*/, true, commentedNever) emitOption("jsx", core.JsxEmitReactJSX, commentedNever) emitOption("verbatimModuleSyntax" /*defaultValue*/, true, commentedNever) emitOption("isolatedModules" /*defaultValue*/, true, commentedNever) emitOption("noUncheckedSideEffectImports" /*defaultValue*/, true, commentedNever) emitOption("moduleDetection", core.ModuleDetectionKindForce, commentedNever) emitOption("skipLibCheck" /*defaultValue*/, true, commentedNever) // Write any user-provided options we haven't already if len(allSetOptions) > 0 { newline() for len(allSetOptions) > 0 { emitOption(allSetOptions[0], options.GetOrZero(allSetOptions[0]), commentedNever) } } push(tab + "}") push(`}`) push(``) return strings.Join(result, "\n") }