216 lines
6.8 KiB
Go
216 lines
6.8 KiB
Go
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/json"
|
|
"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))
|
|
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 := json.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")
|
|
}
|