diff --git a/kitcom/internal/tsgo/tsoptions/commandlineoption.go b/kitcom/internal/tsgo/tsoptions/commandlineoption.go deleted file mode 100644 index c86f503..0000000 --- a/kitcom/internal/tsgo/tsoptions/commandlineoption.go +++ /dev/null @@ -1,188 +0,0 @@ -package tsoptions - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -type CommandLineOptionKind string - -const ( - CommandLineOptionTypeString CommandLineOptionKind = "string" - CommandLineOptionTypeNumber CommandLineOptionKind = "number" - CommandLineOptionTypeBoolean CommandLineOptionKind = "boolean" - CommandLineOptionTypeObject CommandLineOptionKind = "object" - CommandLineOptionTypeList CommandLineOptionKind = "list" - CommandLineOptionTypeListOrElement CommandLineOptionKind = "listOrElement" - CommandLineOptionTypeEnum CommandLineOptionKind = "enum" // map -) - -type CommandLineOption struct { - Name, ShortName string - Kind CommandLineOptionKind - - // used in parsing - IsFilePath bool - IsTSConfigOnly bool - IsCommandLineOnly bool - - // used in output - Description *diagnostics.Message - DefaultValueDescription any - ShowInSimplifiedHelpView bool - - // used in output in serializing and generate tsconfig - Category *diagnostics.Message - - // a flag indicating whether `validateJsonOptionValue` should perform extra checks - extraValidation bool - - // true or undefined - // used for configDirTemplateSubstitutionOptions - allowConfigDirTemplateSubstitution bool - - // used for filter in compilerrunner - AffectsDeclarationPath bool - AffectsProgramStructure bool - AffectsSemanticDiagnostics bool - AffectsBuildInfo bool - AffectsBindDiagnostics bool - AffectsSourceFile bool - AffectsModuleResolution bool - AffectsEmit bool - - allowJsFlag bool - strictFlag bool - - // used in transpileoptions worker - // todo: revisit to see if this can be reduced to boolean - transpileOptionValue core.Tristate - - // used for CommandLineOptionTypeList - listPreserveFalsyValues bool - // used for compilerOptionsDeclaration - ElementOptions CommandLineOptionNameMap -} - -func (o *CommandLineOption) DeprecatedKeys() *collections.Set[string] { - if o.Kind != CommandLineOptionTypeEnum { - return nil - } - return commandLineOptionDeprecated[o.Name] -} - -func (o *CommandLineOption) EnumMap() *collections.OrderedMap[string, any] { - if o.Kind != CommandLineOptionTypeEnum { - return nil - } - return commandLineOptionEnumMap[o.Name] -} - -func (o *CommandLineOption) Elements() *CommandLineOption { - if o.Kind != CommandLineOptionTypeList && o.Kind != CommandLineOptionTypeListOrElement { - return nil - } - return commandLineOptionElements[o.Name] -} - -func (o *CommandLineOption) DisallowNullOrUndefined() bool { - return o.Name == "extends" -} - -// CommandLineOption.Elements() -var commandLineOptionElements = map[string]*CommandLineOption{ - "lib": { - Name: "lib", - Kind: CommandLineOptionTypeEnum, // libMap, - DefaultValueDescription: core.TSUnknown, - }, - "rootDirs": { - Name: "rootDirs", - Kind: CommandLineOptionTypeString, - IsFilePath: true, - }, - "typeRoots": { - Name: "typeRoots", - Kind: CommandLineOptionTypeString, - IsFilePath: true, - }, - "types": { - Name: "types", - Kind: CommandLineOptionTypeString, - }, - "moduleSuffixes": { - Name: "moduleSuffixes", - Kind: CommandLineOptionTypeString, - }, - "customConditions": { - Name: "condition", - Kind: CommandLineOptionTypeString, - }, - "plugins": { - Name: "plugin", - Kind: CommandLineOptionTypeObject, - }, - // For tsconfig root options - "references": { - Name: "references", - Kind: CommandLineOptionTypeObject, - }, - "files": { - Name: "files", - Kind: CommandLineOptionTypeString, - }, - "include": { - Name: "include", - Kind: CommandLineOptionTypeString, - }, - "exclude": { - Name: "exclude", - Kind: CommandLineOptionTypeString, - }, - "extends": { - Name: "extends", - Kind: CommandLineOptionTypeString, - }, - // For Watch options - "excludeDirectories": { - Name: "excludeDirectory", - Kind: CommandLineOptionTypeString, - IsFilePath: true, - extraValidation: true, - }, - "excludeFiles": { - Name: "excludeFile", - Kind: CommandLineOptionTypeString, - IsFilePath: true, - extraValidation: true, - }, - // Test infra options - "libFiles": { - Name: "libFiles", - Kind: CommandLineOptionTypeString, - }, -} - -// CommandLineOption.EnumMap() -var commandLineOptionEnumMap = map[string]*collections.OrderedMap[string, any]{ - "lib": LibMap, - "moduleResolution": moduleResolutionOptionMap, - "module": moduleOptionMap, - "target": targetOptionMap, - "moduleDetection": moduleDetectionOptionMap, - "jsx": jsxOptionMap, - "newLine": newLineOptionMap, - "watchFile": watchFileEnumMap, - "watchDirectory": watchDirectoryEnumMap, - "fallbackPolling": fallbackEnumMap, -} - -// CommandLineOption.DeprecatedKeys() -var commandLineOptionDeprecated = map[string]*collections.Set[string]{ - "moduleResolution": collections.NewSetFromItems("node", "classic", "node10"), - "target": collections.NewSetFromItems("es3"), -} - -// todo: revisit to see if this can be improved -type CompilerOptionsValue any diff --git a/kitcom/internal/tsgo/tsoptions/commandlineparser.go b/kitcom/internal/tsgo/tsoptions/commandlineparser.go deleted file mode 100644 index 4a4b91f..0000000 --- a/kitcom/internal/tsgo/tsoptions/commandlineparser.go +++ /dev/null @@ -1,395 +0,0 @@ -package tsoptions - -import ( - "strconv" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" -) - -func (p *commandLineParser) AlternateMode() *AlternateModeDiagnostics { - return p.workerDiagnostics.didYouMean.alternateMode -} - -func (p *commandLineParser) OptionsDeclarations() []*CommandLineOption { - return p.workerDiagnostics.didYouMean.OptionDeclarations -} - -func (p *commandLineParser) UnknownOptionDiagnostic() *diagnostics.Message { - return p.workerDiagnostics.didYouMean.UnknownOptionDiagnostic -} - -func (p *commandLineParser) UnknownDidYouMeanDiagnostic() *diagnostics.Message { - return p.workerDiagnostics.didYouMean.UnknownDidYouMeanDiagnostic -} - -type commandLineParser struct { - workerDiagnostics *ParseCommandLineWorkerDiagnostics - optionsMap *NameMap - fs vfs.FS - options *collections.OrderedMap[string, any] - fileNames []string - errors []*ast.Diagnostic -} - -func ParseCommandLine( - commandLine []string, - host ParseConfigHost, -) *ParsedCommandLine { - if commandLine == nil { - commandLine = []string{} - } - parser := parseCommandLineWorker(CompilerOptionsDidYouMeanDiagnostics, commandLine, host.FS()) - optionsWithAbsolutePaths := convertToOptionsWithAbsolutePaths(parser.options, CommandLineCompilerOptionsMap, host.GetCurrentDirectory()) - compilerOptions := convertMapToOptions(optionsWithAbsolutePaths, &compilerOptionsParser{&core.CompilerOptions{}}).CompilerOptions - watchOptions := convertMapToOptions(optionsWithAbsolutePaths, &watchOptionsParser{&core.WatchOptions{}}).WatchOptions - result := NewParsedCommandLine(compilerOptions, parser.fileNames, tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: host.GetCurrentDirectory(), - }) - result.ParsedConfig.WatchOptions = watchOptions - result.Errors = parser.errors - result.Raw = parser.options - return result -} - -func ParseBuildCommandLine( - commandLine []string, - host ParseConfigHost, -) *ParsedBuildCommandLine { - if commandLine == nil { - commandLine = []string{} - } - parser := parseCommandLineWorker(buildOptionsDidYouMeanDiagnostics, commandLine, host.FS()) - compilerOptions := &core.CompilerOptions{} - for key, value := range parser.options.Entries() { - buildOption := BuildNameMap.Get(key) - if buildOption == &TscBuildOption || buildOption == CompilerNameMap.Get(key) { - ParseCompilerOptions(key, value, compilerOptions) - } - } - result := &ParsedBuildCommandLine{ - BuildOptions: convertMapToOptions(parser.options, &buildOptionsParser{&core.BuildOptions{}}).BuildOptions, - CompilerOptions: compilerOptions, - WatchOptions: convertMapToOptions(parser.options, &watchOptionsParser{&core.WatchOptions{}}).WatchOptions, - Projects: parser.fileNames, - Errors: parser.errors, - - comparePathsOptions: tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: host.GetCurrentDirectory(), - }, - } - - if len(result.Projects) == 0 { - // tsc -b invoked with no extra arguments; act as if invoked with "tsc -b ." - result.Projects = append(result.Projects, ".") - } - - // Nonsensical combinations - if result.BuildOptions.Clean.IsTrue() && result.BuildOptions.Force.IsTrue() { - result.Errors = append(result.Errors, ast.NewCompilerDiagnostic(diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force")) - } - if result.BuildOptions.Clean.IsTrue() && result.BuildOptions.Verbose.IsTrue() { - result.Errors = append(result.Errors, ast.NewCompilerDiagnostic(diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose")) - } - if result.BuildOptions.Clean.IsTrue() && result.CompilerOptions.Watch.IsTrue() { - result.Errors = append(result.Errors, ast.NewCompilerDiagnostic(diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch")) - } - if result.CompilerOptions.Watch.IsTrue() && result.BuildOptions.Dry.IsTrue() { - result.Errors = append(result.Errors, ast.NewCompilerDiagnostic(diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry")) - } - - return result -} - -func parseCommandLineWorker( - parseCommandLineWithDiagnostics *ParseCommandLineWorkerDiagnostics, - commandLine []string, - fs vfs.FS, -) *commandLineParser { - parser := &commandLineParser{ - fs: fs, - workerDiagnostics: parseCommandLineWithDiagnostics, - fileNames: []string{}, - options: &collections.OrderedMap[string, any]{}, - errors: []*ast.Diagnostic{}, - } - parser.optionsMap = GetNameMapFromList(parser.OptionsDeclarations()) - parser.parseStrings(commandLine) - return parser -} - -func (p *commandLineParser) parseStrings(args []string) { - i := 0 - for i < len(args) { - s := args[i] - i++ - if s == "" { - continue - } - switch s[0] { - case '@': - p.parseResponseFile(s[1:]) - case '-': - inputOptionName := getInputOptionName(s) - opt := p.optionsMap.GetOptionDeclarationFromName(inputOptionName, true /*allowShort*/) - if opt != nil { - i = p.parseOptionValue(args, i, opt, nil) - } else { - watchOpt := WatchNameMap.GetOptionDeclarationFromName(inputOptionName, true /*allowShort*/) - if watchOpt != nil { - i = p.parseOptionValue(args, i, watchOpt, watchOptionsDidYouMeanDiagnostics.OptionTypeMismatchDiagnostic) - } else { - p.errors = append(p.errors, p.createUnknownOptionError(inputOptionName, s, nil, nil)) - } - } - default: - p.fileNames = append(p.fileNames, s) - } - } -} - -func getInputOptionName(input string) string { - // removes at most two leading '-' from the input string - return strings.TrimPrefix(strings.TrimPrefix(input, "-"), "-") -} - -func (p *commandLineParser) parseResponseFile(fileName string) { - fileContents, errors := tryReadFile(fileName, func(fileName string) (string, bool) { - if p.fs == nil { - return "", false - } - read, err := p.fs.ReadFile(fileName) - return read, err - }, p.errors) - p.errors = errors - - if fileContents == "" { - return - } - - var args []string - text := []rune(fileContents) - textLength := len(text) - pos := 0 - for pos < textLength { - for pos < textLength && text[pos] <= ' ' { - pos++ - } - if pos >= textLength { - break - } - start := pos - if text[pos] == '"' { - pos++ - for pos < textLength && text[pos] != '"' { - pos++ - } - if pos < textLength { - args = append(args, string(text[start+1:pos])) - pos++ - } else { - p.errors = append(p.errors, ast.NewCompilerDiagnostic(diagnostics.Unterminated_quoted_string_in_response_file_0, fileName)) - } - } else { - for text[pos] > ' ' { - pos++ - } - args = append(args, string(text[start:pos])) - } - } - p.parseStrings(args) -} - -func tryReadFile(fileName string, readFile func(string) (string, bool), errors []*ast.Diagnostic) (string, []*ast.Diagnostic) { - // this function adds a compiler diagnostic if the file cannot be read - text, e := readFile(fileName) - - if !e || text == "" { - // !!! Divergence: the returned error will not give a useful message - // errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Cannot_read_file_0_Colon_1, *e)); - text = "" - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Cannot_read_file_0, fileName)) - } - return text, errors -} - -func (p *commandLineParser) parseOptionValue( - args []string, - i int, - opt *CommandLineOption, - diag *diagnostics.Message, -) int { - if opt.IsTSConfigOnly && i <= len(args) { - optValue := "" - if i < len(args) { - optValue = args[i] - } - if optValue == "null" { - p.options.Set(opt.Name, nil) - i++ - } else if opt.Kind == "boolean" { - if optValue == "false" { - p.options.Set(opt.Name, false) - i++ - } else { - if optValue == "true" { - i++ - } - p.errors = append(p.errors, ast.NewCompilerDiagnostic(diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_null_on_command_line, opt.Name)) - } - } else { - p.errors = append(p.errors, ast.NewCompilerDiagnostic(diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_null_on_command_line, opt.Name)) - if len(optValue) != 0 && !strings.HasPrefix(optValue, "-") { - i++ - } - } - } else { - // Check to see if no argument was provided (e.g. "--locale" is the last command-line argument). - if i >= len(args) { - if opt.Kind != "boolean" { - if diag == nil { - diag = p.workerDiagnostics.OptionTypeMismatchDiagnostic - } - p.errors = append(p.errors, ast.NewCompilerDiagnostic(diag, opt.Name, getCompilerOptionValueTypeString(opt))) - if opt.Kind == "list" { - p.options.Set(opt.Name, []string{}) - } else if opt.Kind == "enum" { - p.errors = append(p.errors, createDiagnosticForInvalidEnumType(opt, nil, nil)) - } - } else { - p.options.Set(opt.Name, true) - } - return i - } - if args[i] != "null" { - switch opt.Kind { - case "number": - // !!! Make sure this parseInt matches JS parseInt - num, e := strconv.Atoi(args[i]) - if e == nil { - p.options.Set(opt.Name, num) - } - i++ - case "boolean": - // boolean flag has optional value true, false, others - optValue := args[i] - - // check next argument as boolean flag value - if optValue == "false" { - p.options.Set(opt.Name, false) - } else { - p.options.Set(opt.Name, true) - } - // try to consume next argument as value for boolean flag; do not consume argument if it is not "true" or "false" - if optValue == "false" || optValue == "true" { - i++ - } - case "string": - val, err := validateJsonOptionValue(opt, args[i], nil, nil) - if err == nil { - p.options.Set(opt.Name, val) - } else { - p.errors = append(p.errors, err...) - } - i++ - case "list": - result, err := p.parseListTypeOption(opt, args[i]) - p.options.Set(opt.Name, result) - p.errors = append(p.errors, err...) - if len(result) > 0 || len(err) > 0 { - i++ - } - case "listOrElement": - // If not a primitive, the possible types are specified in what is effectively a map of options. - panic("listOrElement not supported here") - default: - val, err := convertJsonOptionOfEnumType(opt, strings.TrimFunc(args[i], stringutil.IsWhiteSpaceLike), nil, nil) - p.options.Set(opt.Name, val) - p.errors = append(p.errors, err...) - i++ - } - } else { - p.options.Set(opt.Name, nil) - i++ - } - } - return i -} - -func (p *commandLineParser) parseListTypeOption(opt *CommandLineOption, value string) ([]any, []*ast.Diagnostic) { - return ParseListTypeOption(opt, value) -} - -func ParseListTypeOption(opt *CommandLineOption, value string) ([]any, []*ast.Diagnostic) { - value = strings.TrimSpace(value) - var errors []*ast.Diagnostic - if strings.HasPrefix(value, "-") { - return []any{}, errors - } - if opt.Kind == "listOrElement" && !strings.ContainsRune(value, ',') { - val, err := validateJsonOptionValue(opt, value, nil, nil) - if err != nil { - return []any{}, err - } - return []any{val.(string)}, errors - } - if value == "" { - return []any{}, errors - } - values := strings.Split(value, ",") - switch opt.Elements().Kind { - case "string": - elements := core.MapFiltered(values, func(v string) (any, bool) { - val, err := validateJsonOptionValue(opt.Elements(), v, nil, nil) - if s, ok := val.(string); ok && len(err) == 0 && s != "" { - return s, true - } - errors = append(errors, err...) - return "", false - }) - return elements, errors - case "boolean", "object", "number": - // do nothing: only string and enum/object types currently allowed as list entries - // !!! we don't actually have number list options, so I didn't implement number list parsing - panic("List of " + opt.Elements().Kind + " is not yet supported.") - default: - result := core.MapFiltered(values, func(v string) (any, bool) { - val, err := convertJsonOptionOfEnumType(opt.Elements(), strings.TrimFunc(v, stringutil.IsWhiteSpaceLike), nil, nil) - if s, ok := val.(string); ok && len(err) == 0 && s != "" { - return s, true - } - errors = append(errors, err...) - return "", false - }) - return result, errors - } -} - -func convertJsonOptionOfEnumType( - opt *CommandLineOption, - value string, - valueExpression *ast.Expression, - sourceFile *ast.SourceFile, -) (any, []*ast.Diagnostic) { - if value == "" { - return nil, nil - } - key := strings.ToLower(value) - typeMap := opt.EnumMap() - if typeMap == nil { - return nil, nil - } - val, ok := typeMap.Get(key) - if ok { - return validateJsonOptionValue(opt, val, valueExpression, sourceFile) - } - return nil, []*ast.Diagnostic{createDiagnosticForInvalidEnumType(opt, sourceFile, valueExpression)} -} diff --git a/kitcom/internal/tsgo/tsoptions/commandlineparser_test.go b/kitcom/internal/tsgo/tsoptions/commandlineparser_test.go deleted file mode 100644 index 87ce526..0000000 --- a/kitcom/internal/tsgo/tsoptions/commandlineparser_test.go +++ /dev/null @@ -1,448 +0,0 @@ -package tsoptions_test - -import ( - "path/filepath" - "slices" - "strings" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnosticwriter" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/baseline" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/filefixture" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions/tsoptionstest" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/osvfs" - "github.com/go-json-experiment/json" - "github.com/google/go-cmp/cmp/cmpopts" - "gotest.tools/v3/assert" -) - -func TestCommandLineParseResult(t *testing.T) { - t.Parallel() - repo.SkipIfNoTypeScriptSubmodule(t) - - parseCommandLineSubScenarios := []*subScenarioInput{ - // --lib es6 0.ts - {"Parse single option of library flag", []string{"--lib", "es6", "0.ts"}}, - {"Handles may only be used with --build flags", []string{"--build", "--clean", "--dry", "--force", "--verbose"}}, - // --declarations --allowTS - {"Handles did you mean for misspelt flags", []string{"--declarations", "--allowTS"}}, - // --lib es5,es2015.symbol.wellknown 0.ts - {"Parse multiple options of library flags", []string{"--lib", "es5,es2015.symbol.wellknown", "0.ts"}}, - // --lib es5,invalidOption 0.ts - {"Parse invalid option of library flags", []string{"--lib", "es5,invalidOption", "0.ts"}}, - // 0.ts --jsx - {"Parse empty options of --jsx", []string{"0.ts", "--jsx"}}, - // 0.ts -- - {"Parse empty options of --module", []string{"0.ts", "--module"}}, - // 0.ts --newLine - {"Parse empty options of --newLine", []string{"0.ts", "--newLine"}}, - // 0.ts --target - {"Parse empty options of --target", []string{"0.ts", "--target"}}, - // 0.ts --moduleResolution - {"Parse empty options of --moduleResolution", []string{"0.ts", "--moduleResolution"}}, - // 0.ts --lib - {"Parse empty options of --lib", []string{"0.ts", "--lib"}}, - // 0.ts --lib - // This test is an error because the empty string is falsey - {"Parse empty string of --lib", []string{"0.ts", "--lib", ""}}, - // 0.ts --lib - {"Parse immediately following command line argument of --lib", []string{"0.ts", "--lib", "--sourcemap"}}, - // --lib es5, es7 0.ts - {"Parse --lib option with extra comma", []string{"--lib", "es5,", "es7", "0.ts"}}, - // --lib es5, es7 0.ts - {"Parse --lib option with trailing white-space", []string{"--lib", "es5, ", "es7", "0.ts"}}, - // --lib es5,es2015.symbol.wellknown --target es5 0.ts - {"Parse multiple compiler flags with input files at the end", []string{"--lib", "es5,es2015.symbol.wellknown", "--target", "es5", "0.ts"}}, - // --module commonjs --target es5 0.ts --lib es5,es2015.symbol.wellknown - {"Parse multiple compiler flags with input files in the middle", []string{"--module", "commonjs", "--target", "es5", "0.ts", "--lib", "es5,es2015.symbol.wellknown"}}, - // --module commonjs --target es5 --lib es5 0.ts --library es2015.array,es2015.symbol.wellknown - {"Parse multiple library compiler flags ", []string{"--module", "commonjs", "--target", "es5", "--lib", "es5", "0.ts", "--lib", "es2015.core, es2015.symbol.wellknown "}}, - {"Parse explicit boolean flag value", []string{"--strictNullChecks", "false", "0.ts"}}, - {"Parse non boolean argument after boolean flag", []string{"--noImplicitAny", "t", "0.ts"}}, - {"Parse implicit boolean flag value", []string{"--strictNullChecks"}}, - {"parse --incremental", []string{"--incremental", "0.ts"}}, - {"parse --tsBuildInfoFile", []string{"--tsBuildInfoFile", "build.tsbuildinfo", "0.ts"}}, - {"allows tsconfig only option to be set to null", []string{"--composite", "null", "-tsBuildInfoFile", "null", "0.ts"}}, - - // ****** Watch Options ****** - {"parse --watchFile", []string{"--watchFile", "UseFsEvents", "0.ts"}}, - {"parse --watchDirectory", []string{"--watchDirectory", "FixedPollingInterval", "0.ts"}}, - {"parse --fallbackPolling", []string{"--fallbackPolling", "PriorityInterval", "0.ts"}}, - {"parse --synchronousWatchDirectory", []string{"--synchronousWatchDirectory", "0.ts"}}, - {"errors on missing argument to --fallbackPolling", []string{"0.ts", "--fallbackPolling"}}, - {"parse --excludeDirectories", []string{"--excludeDirectories", "**/temp", "0.ts"}}, - {"errors on invalid excludeDirectories", []string{"--excludeDirectories", "**/../*", "0.ts"}}, - {"parse --excludeFiles", []string{"--excludeFiles", "**/temp/*.ts", "0.ts"}}, - {"errors on invalid excludeFiles", []string{"--excludeFiles", "**/../*", "0.ts"}}, - } - - for _, testCase := range parseCommandLineSubScenarios { - testCase.createSubScenario("parseCommandLine").assertParseResult(t) - } -} - -func TestParseCommandLineVerifyNull(t *testing.T) { - t.Parallel() - repo.SkipIfNoTypeScriptSubmodule(t) - - // run test for boolean - subScenarioInput{"allows setting option type boolean to false", []string{"--composite", "false", "0.ts"}}.createSubScenario("parseCommandLine").assertParseResult(t) - - verifyNullSubScenarios := []verifyNull{ - { - subScenario: "option of type boolean", - optionName: "composite", - nonNullValue: "true", - }, - { - subScenario: "option of type object", - optionName: "paths", - }, - { - subScenario: "option of type list", - optionName: "rootDirs", - nonNullValue: "abc,xyz", - }, - createVerifyNullForNonNullIncluded("option of type string", tsoptions.CommandLineOptionTypeString, "hello"), - createVerifyNullForNonNullIncluded("option of type number", tsoptions.CommandLineOptionTypeNumber, "10"), - // todo: make the following work for tests -- currently it is difficult to do extra options of enum type - // createVerifyNullForNonNullIncluded("option of type custom map", CommandLineOptionTypeEnum, "node"), - } - - for _, verifyNullCase := range verifyNullSubScenarios { - createSubScenario( - "parseCommandLine", - verifyNullCase.subScenario+" allows setting it to null", - []string{"--" + verifyNullCase.optionName, "null", "0.ts"}, - verifyNullCase.optDecls, - ).assertParseResult(t) - - if verifyNullCase.nonNullValue != "" { - createSubScenario( - "parseCommandLine", - verifyNullCase.subScenario+" errors if non null value is passed", - []string{"--" + verifyNullCase.optionName, verifyNullCase.nonNullValue, "0.ts"}, - verifyNullCase.optDecls, - ).assertParseResult(t) - } - - createSubScenario( - "parseCommandLine", - verifyNullCase.subScenario+" errors if its followed by another option", - []string{"0.ts", "--strictNullChecks", "--" + verifyNullCase.optionName}, - verifyNullCase.optDecls, - ).assertParseResult(t) - - createSubScenario( - "parseCommandLine", - verifyNullCase.subScenario+" errors if its last option", - []string{"0.ts", "--" + verifyNullCase.optionName}, - verifyNullCase.optDecls, - ).assertParseResult(t) - } -} - -func createVerifyNullForNonNullIncluded(subScenario string, kind tsoptions.CommandLineOptionKind, nonNullValue string) verifyNull { - return verifyNull{ - subScenario: subScenario, - optionName: "optionName", - nonNullValue: nonNullValue, - optDecls: slices.Concat(tsoptions.OptionsDeclarations, []*tsoptions.CommandLineOption{{ - Name: "optionName", - Kind: kind, - IsTSConfigOnly: true, - Category: diagnostics.Backwards_Compatibility, - Description: diagnostics.Enable_project_compilation, - DefaultValueDescription: nil, - }}), - } -} - -func (f commandLineSubScenario) assertParseResult(t *testing.T) { - t.Helper() - t.Run(f.testName, func(t *testing.T) { - t.Parallel() - originalBaseline := f.baseline.ReadFile(t) - tsBaseline := parseExistingCompilerBaseline(t, originalBaseline) - - // f.workerDiagnostic is either defined or set to default pointer in `createSubScenario` - parsed := tsoptions.ParseCommandLineTestWorker(f.optDecls, f.commandLine, osvfs.FS()) - - newBaselineFileNames := strings.Join(parsed.FileNames, ",") - assert.Equal(t, tsBaseline.fileNames, newBaselineFileNames) - - o, _ := json.Marshal(parsed.Options) - newParsedCompilerOptions := &core.CompilerOptions{} - e := json.Unmarshal(o, newParsedCompilerOptions) - assert.NilError(t, e) - assert.DeepEqual(t, tsBaseline.options, newParsedCompilerOptions, cmpopts.IgnoreUnexported(core.CompilerOptions{})) - - newParsedWatchOptions := core.WatchOptions{} - e = json.Unmarshal(o, &newParsedWatchOptions) - assert.NilError(t, e) - - // !!! useful for debugging but will not pass due to `none` as enum options - // assert.DeepEqual(t, tsBaseline.watchoptions, newParsedWatchOptions) - - var formattedErrors strings.Builder - diagnosticwriter.WriteFormatDiagnostics(&formattedErrors, parsed.Errors, &diagnosticwriter.FormattingOptions{NewLine: "\n"}) - newBaselineErrors := formattedErrors.String() - - // !!! - // useful for debugging--compares the new errors with the old errors. currently will NOT pass because of unimplemented options, not completely identical enum options, etc - // assert.Equal(t, tsBaseline.errors, newBaselineErrors) - - baseline.Run(t, f.testName+".js", formatNewBaseline(f.commandLine, o, newBaselineFileNames, newBaselineErrors), baseline.Options{Subfolder: "tsoptions/commandLineParsing"}) - }) -} - -func parseExistingCompilerBaseline(t *testing.T, baseline string) *TestCommandLineParser { - _, rest, _ := strings.Cut(baseline, "CompilerOptions::\n") - compilerOptions, rest, watchFound := strings.Cut(rest, "\nWatchOptions::\n") - watchOptions, rest, _ := strings.Cut(rest, "\nFileNames::\n") - fileNames, errors, _ := strings.Cut(rest, "\nErrors::\n") - - baselineCompilerOptions := &core.CompilerOptions{} - e := json.Unmarshal([]byte(compilerOptions), &baselineCompilerOptions) - assert.NilError(t, e) - - baselineWatchOptions := &core.WatchOptions{} - if watchFound && watchOptions != "" { - e2 := json.Unmarshal([]byte(watchOptions), &baselineWatchOptions) - assert.NilError(t, e2) - } - - return &TestCommandLineParser{ - options: baselineCompilerOptions, - watchoptions: baselineWatchOptions, - fileNames: fileNames, - errors: errors, - } -} - -func formatNewBaseline( - commandLine []string, - opts []byte, - fileNames string, - errors string, -) string { - var formatted strings.Builder - formatted.WriteString("Args::\n") - formatted.WriteString("[\"" + strings.Join(commandLine, "\", \"") + "\"]") - formatted.WriteString("\n\nCompilerOptions::\n") - formatted.Write(opts) - // todo: watch options not implemented - // formatted.WriteString("WatchOptions::\n") - formatted.WriteString("\n\nFileNames::\n") - formatted.WriteString(fileNames) - formatted.WriteString("\n\nErrors::\n") - formatted.WriteString(errors) - return formatted.String() -} - -func (f commandLineSubScenario) assertBuildParseResult(t *testing.T) { - t.Helper() - t.Run(f.testName, func(t *testing.T) { - t.Parallel() - originalBaseline := f.baseline.ReadFile(t) - tsBaseline := parseExistingCompilerBaselineBuild(t, originalBaseline) - - // f.workerDiagnostic is either defined or set to default pointer in `createSubScenario` - parsed := tsoptions.ParseBuildCommandLine(f.commandLine, &tsoptionstest.VfsParseConfigHost{ - Vfs: osvfs.FS(), - CurrentDirectory: tspath.NormalizeSlashes(repo.TypeScriptSubmodulePath), - }) - - newBaselineProjects := strings.Join(parsed.Projects, ",") - assert.Equal(t, tsBaseline.projects, newBaselineProjects) - - o, _ := json.Marshal(parsed.BuildOptions) - newParsedBuildOptions := &core.BuildOptions{} - e := json.Unmarshal(o, newParsedBuildOptions) - assert.NilError(t, e) - assert.DeepEqual(t, tsBaseline.options, newParsedBuildOptions, cmpopts.IgnoreUnexported(core.BuildOptions{})) - - compilerOpts, _ := json.Marshal(parsed.CompilerOptions) - newParsedCompilerOptions := &core.CompilerOptions{} - e = json.Unmarshal(compilerOpts, newParsedCompilerOptions) - assert.NilError(t, e) - assert.DeepEqual(t, tsBaseline.compilerOptions, newParsedCompilerOptions, cmpopts.IgnoreUnexported(core.CompilerOptions{})) - - newParsedWatchOptions := core.WatchOptions{} - e = json.Unmarshal(o, &newParsedWatchOptions) - assert.NilError(t, e) - - // !!! useful for debugging but will not pass due to `none` as enum options - // assert.DeepEqual(t, tsBaseline.watchoptions, newParsedWatchOptions) - - var formattedErrors strings.Builder - diagnosticwriter.WriteFormatDiagnostics(&formattedErrors, parsed.Errors, &diagnosticwriter.FormattingOptions{NewLine: "\n"}) - newBaselineErrors := formattedErrors.String() - - // !!! - // useful for debugging--compares the new errors with the old errors. currently will NOT pass because of unimplemented options, not completely identical enum options, etc - // assert.Equal(t, tsBaseline.errors, newBaselineErrors) - - baseline.Run(t, f.testName+".js", formatNewBaselineBuild(f.commandLine, o, compilerOpts, newBaselineProjects, newBaselineErrors), baseline.Options{Subfolder: "tsoptions/commandLineParsing"}) - }) -} - -func parseExistingCompilerBaselineBuild(t *testing.T, baseline string) *TestCommandLineParserBuild { - _, rest, _ := strings.Cut(baseline, "buildOptions::\n") - buildOptions, rest, watchFound := strings.Cut(rest, "\nWatchOptions::\n") - watchOptions, rest, _ := strings.Cut(rest, "\nProjects::\n") - projects, errors, _ := strings.Cut(rest, "\nErrors::\n") - - baselineBuildOptions := &core.BuildOptions{} - e := json.Unmarshal([]byte(buildOptions), &baselineBuildOptions) - assert.NilError(t, e) - - baselineCompilerOptions := &core.CompilerOptions{} - e = json.Unmarshal([]byte(buildOptions), &baselineCompilerOptions) - assert.NilError(t, e) - - baselineWatchOptions := &core.WatchOptions{} - if watchFound && watchOptions != "" { - e2 := json.Unmarshal([]byte(watchOptions), &baselineWatchOptions) - assert.NilError(t, e2) - } - - return &TestCommandLineParserBuild{ - options: baselineBuildOptions, - compilerOptions: baselineCompilerOptions, - watchoptions: baselineWatchOptions, - projects: projects, - errors: errors, - } -} - -func formatNewBaselineBuild( - commandLine []string, - opts []byte, - compilerOpts []byte, - projects string, - errors string, -) string { - var formatted strings.Builder - formatted.WriteString("Args::\n") - if len(commandLine) == 0 { - formatted.WriteString("[]") - } else { - formatted.WriteString("[\"" + strings.Join(commandLine, "\", \"") + "\"]") - } - formatted.WriteString("\n\nbuildOptions::\n") - formatted.Write(opts) - formatted.WriteString("\n\ncompilerOptions::\n") - formatted.Write(compilerOpts) - // todo: watch options not implemented - // formatted.WriteString("WatchOptions::\n") - formatted.WriteString("\n\nProjects::\n") - formatted.WriteString(projects) - formatted.WriteString("\n\nErrors::\n") - formatted.WriteString(errors) - return formatted.String() -} - -func createSubScenario(scenarioKind string, subScenarioName string, commandline []string, opts ...[]*tsoptions.CommandLineOption) *commandLineSubScenario { - subScenarioName = scenarioKind + "/" + subScenarioName - baselineFileName := "tests/baselines/reference/config/commandLineParsing/" + subScenarioName + ".js" - - result := &commandLineSubScenario{ - filefixture.FromFile(subScenarioName, filepath.Join(repo.TypeScriptSubmodulePath, baselineFileName)), - subScenarioName, - commandline, - nil, - } - if len(opts) > 0 { - result.optDecls = opts[0] - } - return result -} - -type subScenarioInput struct { - name string - commandLineArgs []string -} - -func (f subScenarioInput) createSubScenario(scenarioKind string) *commandLineSubScenario { - return createSubScenario(scenarioKind, f.name, f.commandLineArgs) -} - -type commandLineSubScenario struct { - baseline filefixture.Fixture - testName string - commandLine []string - optDecls []*tsoptions.CommandLineOption -} - -type verifyNull struct { - subScenario string - optionName string - nonNullValue string - optDecls []*tsoptions.CommandLineOption -} - -type TestCommandLineParser struct { - options *core.CompilerOptions - watchoptions *core.WatchOptions - fileNames, errors string -} - -type TestCommandLineParserBuild struct { - options *core.BuildOptions - compilerOptions *core.CompilerOptions - watchoptions *core.WatchOptions - projects, errors string -} - -func TestParseBuildCommandLine(t *testing.T) { - t.Parallel() - repo.SkipIfNoTypeScriptSubmodule(t) - - parseCommandLineSubScenarios := []*subScenarioInput{ - {"parse build without any options ", []string{}}, - {"Parse multiple options", []string{"--verbose", "--force", "tests"}}, - {"Parse option with invalid option", []string{"--verbose", "--invalidOption"}}, - {"Parse multiple flags with input projects at the end", []string{"--force", "--verbose", "src", "tests"}}, - {"Parse multiple flags with input projects in the middle", []string{"--force", "src", "tests", "--verbose"}}, - {"Parse multiple flags with input projects in the beginning", []string{"src", "tests", "--force", "--verbose"}}, - {"parse build with --incremental", []string{"--incremental", "tests"}}, - {"parse build with --locale en-us", []string{"--locale", "en-us", "src"}}, - {"parse build with --tsBuildInfoFile", []string{"--tsBuildInfoFile", "build.tsbuildinfo", "tests"}}, - {"reports other common may not be used with --build flags", []string{"--strict"}}, - {`--clean and --force together is invalid`, []string{"--clean", "--force"}}, - {`--clean and --verbose together is invalid`, []string{"--clean", "--verbose"}}, - {`--clean and --watch together is invalid`, []string{"--clean", "--watch"}}, - {`--watch and --dry together is invalid`, []string{"--watch", "--dry"}}, - {"parse --watchFile", []string{"--watchFile", "UseFsEvents", "--verbose"}}, - {"parse --watchDirectory", []string{"--watchDirectory", "FixedPollingInterval", "--verbose"}}, - {"parse --fallbackPolling", []string{"--fallbackPolling", "PriorityInterval", "--verbose"}}, - {"parse --synchronousWatchDirectory", []string{"--synchronousWatchDirectory", "--verbose"}}, - {"errors on missing argument", []string{"--verbose", "--fallbackPolling"}}, - {"errors on invalid excludeDirectories", []string{"--excludeDirectories", "**/../*"}}, - {"parse --excludeFiles", []string{"--excludeFiles", "**/temp/*.ts"}}, - {"errors on invalid excludeFiles", []string{"--excludeFiles", "**/../*"}}, - } - - for _, testCase := range parseCommandLineSubScenarios { - testCase.createSubScenario("parseBuildOptions").assertBuildParseResult(t) - } -} - -func TestAffectsBuildInfo(t *testing.T) { - t.Parallel() - t.Run("should have affectsBuildInfo true for every option with affectsSemanticDiagnostics", func(t *testing.T) { - t.Parallel() - for _, option := range tsoptions.OptionsDeclarations { - if option.AffectsSemanticDiagnostics { - // semantic diagnostics affect the build info, so ensure they're included - assert.Assert(t, option.AffectsBuildInfo) - } - } - }) -} diff --git a/kitcom/internal/tsgo/tsoptions/decls_test.go b/kitcom/internal/tsgo/tsoptions/decls_test.go deleted file mode 100644 index 597b395..0000000 --- a/kitcom/internal/tsgo/tsoptions/decls_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package tsoptions_test - -import ( - "reflect" - "strings" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" -) - -func TestCompilerOptionsDeclaration(t *testing.T) { - t.Parallel() - - decls := make(map[string]*tsoptions.CommandLineOption) - - for _, decl := range tsoptions.OptionsDeclarations { - decls[strings.ToLower(decl.Name)] = decl - } - - internalOptions := []string{ - "allowNonTsExtensions", - "build", - "configFilePath", - "noDtsResolution", - "noEmitForJsFiles", - "pathsBasePath", - "suppressOutputPathCheck", - "build", - } - - internalOptionsMap := make(map[string]string) - for _, opt := range internalOptions { - internalOptionsMap[strings.ToLower(opt)] = opt - } - - compilerOptionsType := reflect.TypeFor[core.CompilerOptions]() - for i := range compilerOptionsType.NumField() { - field := compilerOptionsType.Field(i) - if !field.IsExported() { - continue - } - - lowerName := strings.ToLower(field.Name) - - decl := decls[lowerName] - if decl == nil { - if name, ok := internalOptionsMap[lowerName]; ok { - checkCompilerOptionJsonTagName(t, field, name) - continue - } - t.Errorf("CompilerOptions.%s has no options declaration", field.Name) - continue - } - delete(decls, lowerName) - - checkCompilerOptionJsonTagName(t, field, decl.Name) - } - - skippedOptions := []string{ - "charset", - "noImplicitUseStrict", - "noStrictGenericChecks", - "plugins", - "preserveValueImports", - "suppressExcessPropertyErrors", - "suppressImplicitAnyIndexErrors", - } - - for _, opt := range skippedOptions { - delete(decls, strings.ToLower(opt)) - } - - for _, decl := range decls { - t.Errorf("Option declaration %s is not present in CompilerOptions", decl.Name) - } -} - -func checkCompilerOptionJsonTagName(t *testing.T, field reflect.StructField, name string) { - t.Helper() - want := name + ",omitzero" - got := field.Tag.Get("json") - if got != want { - t.Errorf("Field %s has json tag %s, but the option declaration has name %s", field.Name, got, want) - } -} diff --git a/kitcom/internal/tsgo/tsoptions/declsbuild.go b/kitcom/internal/tsgo/tsoptions/declsbuild.go deleted file mode 100644 index bfd93ad..0000000 --- a/kitcom/internal/tsgo/tsoptions/declsbuild.go +++ /dev/null @@ -1,61 +0,0 @@ -package tsoptions - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -var BuildOpts = slices.Concat(commonOptionsWithBuild, optionsForBuild) - -var TscBuildOption = CommandLineOption{ - Name: "build", - Kind: "boolean", - ShortName: "b", - ShowInSimplifiedHelpView: true, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date, - DefaultValueDescription: false, -} - -var optionsForBuild = []*CommandLineOption{ - &TscBuildOption, - { - Name: "verbose", - ShortName: "v", - Category: diagnostics.Command_line_Options, - Description: diagnostics.Enable_verbose_logging, - Kind: "boolean", - DefaultValueDescription: false, - }, - { - Name: "dry", - ShortName: "d", - Category: diagnostics.Command_line_Options, - Description: diagnostics.Show_what_would_be_built_or_deleted_if_specified_with_clean, - Kind: "boolean", - DefaultValueDescription: false, - }, - { - Name: "force", - ShortName: "f", - Category: diagnostics.Command_line_Options, - Description: diagnostics.Build_all_projects_including_those_that_appear_to_be_up_to_date, - Kind: "boolean", - DefaultValueDescription: false, - }, - { - Name: "clean", - Category: diagnostics.Command_line_Options, - Description: diagnostics.Delete_the_outputs_of_all_projects, - Kind: "boolean", - DefaultValueDescription: false, - }, - { - Name: "stopBuildOnErrors", - Category: diagnostics.Command_line_Options, - Description: diagnostics.Skip_building_downstream_projects_on_error_in_upstream_project, - Kind: "boolean", - DefaultValueDescription: false, - }, -} diff --git a/kitcom/internal/tsgo/tsoptions/declscompiler.go b/kitcom/internal/tsgo/tsoptions/declscompiler.go deleted file mode 100644 index bdb39a0..0000000 --- a/kitcom/internal/tsgo/tsoptions/declscompiler.go +++ /dev/null @@ -1,1240 +0,0 @@ -package tsoptions - -import ( - "reflect" - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -var OptionsDeclarations = slices.Concat(commonOptionsWithBuild, optionsForCompiler) - -var commonOptionsWithBuild = []*CommandLineOption{ - //******* commonOptionsWithBuild ******* - { - Name: "help", - ShortName: "h", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: true, - IsCommandLineOnly: true, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Print_this_message, - DefaultValueDescription: false, - }, - { - Name: "help", - ShortName: "?", - Kind: CommandLineOptionTypeBoolean, - IsCommandLineOnly: true, - Category: diagnostics.Command_line_Options, - DefaultValueDescription: false, - }, - { - Name: "watch", - ShortName: "w", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: true, - IsCommandLineOnly: true, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Watch_input_files, - DefaultValueDescription: false, - }, - { - Name: "preserveWatchOutput", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: false, - Category: diagnostics.Output_Formatting, - Description: diagnostics.Disable_wiping_the_console_in_watch_mode, - DefaultValueDescription: false, - }, - { - Name: "listFiles", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Print_all_of_the_files_read_during_the_compilation, - DefaultValueDescription: false, - }, - { - Name: "explainFiles", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Print_files_read_during_the_compilation_including_why_it_was_included, - DefaultValueDescription: false, - }, - { - Name: "listEmittedFiles", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Print_the_names_of_emitted_files_after_a_compilation, - DefaultValueDescription: false, - }, - { - Name: "pretty", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Output_Formatting, - Description: diagnostics.Enable_color_and_formatting_in_TypeScript_s_output_to_make_compiler_errors_easier_to_read, - DefaultValueDescription: true, - }, - { - Name: "traceResolution", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Log_paths_used_during_the_moduleResolution_process, - DefaultValueDescription: false, - }, - { - Name: "diagnostics", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Output_compiler_performance_information_after_building, - DefaultValueDescription: false, - }, - { - Name: "extendedDiagnostics", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Output_more_detailed_compiler_performance_information_after_building, - DefaultValueDescription: false, - }, - { - Name: "generateCpuProfile", - Kind: CommandLineOptionTypeString, - IsFilePath: true, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Emit_a_v8_CPU_profile_of_the_compiler_run_for_debugging, - DefaultValueDescription: "profile.cpuprofile", - }, - - { - Name: "generateTrace", - Kind: CommandLineOptionTypeString, - IsFilePath: true, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Generates_an_event_trace_and_a_list_of_types, - }, - { - Name: "incremental", - ShortName: "i", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Projects, - Description: diagnostics.Save_tsbuildinfo_files_to_allow_for_incremental_compilation_of_projects, - transpileOptionValue: core.TSUnknown, - DefaultValueDescription: diagnostics.X_false_unless_composite_is_set, - }, - { - Name: "declaration", - ShortName: "d", - Kind: CommandLineOptionTypeBoolean, - // Not setting affectsEmit because we calculate this flag might not affect full emit - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Emit, - transpileOptionValue: core.TSUnknown, - Description: diagnostics.Generate_d_ts_files_from_TypeScript_and_JavaScript_files_in_your_project, - DefaultValueDescription: diagnostics.X_false_unless_composite_is_set, - }, - { - Name: "declarationMap", - Kind: CommandLineOptionTypeBoolean, - // Not setting affectsEmit because we calculate this flag might not affect full emit - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Emit, - DefaultValueDescription: false, - Description: diagnostics.Create_sourcemaps_for_d_ts_files, - }, - { - Name: "emitDeclarationOnly", - Kind: CommandLineOptionTypeBoolean, - // Not setting affectsEmit because we calculate this flag might not affect full emit - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Emit, - Description: diagnostics.Only_output_d_ts_files_and_not_JavaScript_files, - transpileOptionValue: core.TSUnknown, - DefaultValueDescription: false, - }, - { - Name: "sourceMap", - Kind: CommandLineOptionTypeBoolean, - // Not setting affectsEmit because we calculate this flag might not affect full emit - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Emit, - DefaultValueDescription: false, - Description: diagnostics.Create_source_map_files_for_emitted_JavaScript_files, - }, - { - Name: "inlineSourceMap", - Kind: CommandLineOptionTypeBoolean, - // Not setting affectsEmit because we calculate this flag might not affect full emit - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Include_sourcemap_files_inside_the_emitted_JavaScript, - DefaultValueDescription: false, - }, - { - Name: "noCheck", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: false, - Category: diagnostics.Compiler_Diagnostics, - Description: diagnostics.Disable_full_type_checking_only_critical_parse_and_emit_errors_will_be_reported, - transpileOptionValue: core.TSTrue, - DefaultValueDescription: false, - // Not setting affectsSemanticDiagnostics or affectsBuildInfo because we dont want all diagnostics to go away, its handled in builder - }, - { - Name: "noEmit", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Emit, - Description: diagnostics.Disable_emitting_files_from_a_compilation, - transpileOptionValue: core.TSUnknown, - DefaultValueDescription: false, - }, - { - Name: "assumeChangesOnlyAffectDirectDependencies", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Watch_and_Build_Modes, - Description: diagnostics.Have_recompiles_in_projects_that_use_incremental_and_watch_mode_assume_that_changes_within_a_file_will_only_affect_files_directly_depending_on_it, - DefaultValueDescription: false, - }, - { - Name: "locale", - Kind: CommandLineOptionTypeString, - Category: diagnostics.Command_line_Options, - IsCommandLineOnly: true, - Description: diagnostics.Set_the_language_of_the_messaging_from_TypeScript_This_does_not_affect_emit, - DefaultValueDescription: diagnostics.Platform_specific, - }, - - { - Name: "quiet", - ShortName: "q", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Do_not_print_diagnostics, - }, - { - Name: "singleThreaded", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Run_in_single_threaded_mode, - }, - { - Name: "pprofDir", - Kind: CommandLineOptionTypeString, - IsFilePath: true, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Generate_pprof_CPU_Slashmemory_profiles_to_the_given_directory, - }, -} - -var optionsForCompiler = []*CommandLineOption{ - //******* compilerOptions not common with --build ******* - - // CommandLine only options - { - Name: "all", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Show_all_compiler_options, - DefaultValueDescription: false, - }, - { - Name: "version", - ShortName: "v", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Print_the_compiler_s_version, - DefaultValueDescription: false, - }, - { - Name: "init", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Initializes_a_TypeScript_project_and_creates_a_tsconfig_json_file, - DefaultValueDescription: false, - }, - { - Name: "project", - ShortName: "p", - Kind: CommandLineOptionTypeString, - IsFilePath: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Command_line_Options, - Description: diagnostics.Compile_the_project_given_the_path_to_its_configuration_file_or_to_a_folder_with_a_tsconfig_json, - }, - { - Name: "showConfig", - Kind: CommandLineOptionTypeBoolean, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Command_line_Options, - IsCommandLineOnly: true, - Description: diagnostics.Print_the_final_configuration_instead_of_building, - DefaultValueDescription: false, - }, - { - Name: "listFilesOnly", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Command_line_Options, - IsCommandLineOnly: true, - Description: diagnostics.Print_names_of_files_that_are_part_of_the_compilation_and_then_stop_processing, - DefaultValueDescription: false, - }, - - // Basic - // targetOptionDeclaration, - { - Name: "target", - ShortName: "t", - Kind: CommandLineOptionTypeEnum, // targetOptionMap - AffectsSourceFile: true, - AffectsModuleResolution: true, - AffectsEmit: true, - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Set_the_JavaScript_language_version_for_emitted_JavaScript_and_include_compatible_library_declarations, - DefaultValueDescription: core.ScriptTargetES5, - }, - - // moduleOptionDeclaration, - { - Name: "module", - ShortName: "m", - Kind: CommandLineOptionTypeEnum, // moduleOptionMap - AffectsModuleResolution: true, - AffectsEmit: true, - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Modules, - Description: diagnostics.Specify_what_module_code_is_generated, - DefaultValueDescription: core.TSUnknown, - }, - { - Name: "lib", - Kind: CommandLineOptionTypeList, - // elements: &CommandLineOption{ - // name: "lib", - // kind: CommandLineOptionTypeEnum, // libMap, - // defaultValueDescription: core.TSUnknown, - // }, - AffectsProgramStructure: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Specify_a_set_of_bundled_library_declaration_files_that_describe_the_target_runtime_environment, - transpileOptionValue: core.TSUnknown, - }, - { - Name: "allowJs", - Kind: CommandLineOptionTypeBoolean, - allowJsFlag: true, - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.JavaScript_Support, - Description: diagnostics.Allow_JavaScript_files_to_be_a_part_of_your_program_Use_the_checkJs_option_to_get_errors_from_these_files, - DefaultValueDescription: false, - }, - { - Name: "checkJs", - Kind: CommandLineOptionTypeBoolean, - AffectsModuleResolution: true, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.JavaScript_Support, - Description: diagnostics.Enable_error_reporting_in_type_checked_JavaScript_files, - DefaultValueDescription: false, - }, - { - Name: "jsx", - Kind: CommandLineOptionTypeEnum, // jsxOptionMap, - AffectsSourceFile: true, - AffectsEmit: true, - AffectsBuildInfo: true, - AffectsModuleResolution: true, - // The checker emits an error when it sees JSX but this option is not set in compilerOptions. - // This is effectively a semantic error, so mark this option as affecting semantic diagnostics - // so we know to refresh errors when this option is changed. - AffectsSemanticDiagnostics: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Specify_what_JSX_code_is_generated, - DefaultValueDescription: core.TSUnknown, - }, - { - Name: "outFile", - Kind: CommandLineOptionTypeString, - AffectsEmit: true, - AffectsBuildInfo: true, - AffectsDeclarationPath: true, - IsFilePath: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Emit, - Description: diagnostics.Specify_a_file_that_bundles_all_outputs_into_one_JavaScript_file_If_declaration_is_true_also_designates_a_file_that_bundles_all_d_ts_output, - transpileOptionValue: core.TSUnknown, - }, - { - Name: "outDir", - Kind: CommandLineOptionTypeString, - AffectsEmit: true, - AffectsBuildInfo: true, - AffectsDeclarationPath: true, - IsFilePath: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Emit, - Description: diagnostics.Specify_an_output_folder_for_all_emitted_files, - }, - { - Name: "rootDir", - Kind: CommandLineOptionTypeString, - AffectsEmit: true, - AffectsBuildInfo: true, - AffectsDeclarationPath: true, - IsFilePath: true, - Category: diagnostics.Modules, - Description: diagnostics.Specify_the_root_folder_within_your_source_files, - DefaultValueDescription: diagnostics.Computed_from_the_list_of_input_files, - }, - { - Name: "composite", - Kind: CommandLineOptionTypeBoolean, - // Not setting affectsEmit because we calculate this flag might not affect full emit - AffectsBuildInfo: true, - IsTSConfigOnly: true, - Category: diagnostics.Projects, - transpileOptionValue: core.TSUnknown, - DefaultValueDescription: false, - Description: diagnostics.Enable_constraints_that_allow_a_TypeScript_project_to_be_used_with_project_references, - }, - { - Name: "tsBuildInfoFile", - Kind: CommandLineOptionTypeString, - AffectsEmit: true, - AffectsBuildInfo: true, - IsFilePath: true, - Category: diagnostics.Projects, - transpileOptionValue: core.TSUnknown, - DefaultValueDescription: ".tsbuildinfo", - Description: diagnostics.Specify_the_path_to_tsbuildinfo_incremental_compilation_file, - }, - { - Name: "removeComments", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Emit, - DefaultValueDescription: false, - Description: diagnostics.Disable_emitting_comments, - }, - { - Name: "importHelpers", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - AffectsSourceFile: true, - Category: diagnostics.Emit, - Description: diagnostics.Allow_importing_helper_functions_from_tslib_once_per_project_instead_of_including_them_per_file, - DefaultValueDescription: false, - }, - { - Name: "downlevelIteration", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Emit_more_compliant_but_verbose_and_less_performant_JavaScript_for_iteration, - DefaultValueDescription: false, - }, - { - Name: "isolatedModules", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Interop_Constraints, - Description: diagnostics.Ensure_that_each_file_can_be_safely_transpiled_without_relying_on_other_imports, - transpileOptionValue: core.TSTrue, - DefaultValueDescription: false, - }, - { - Name: "verbatimModuleSyntax", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Interop_Constraints, - Description: diagnostics.Do_not_transform_or_elide_any_imports_or_exports_not_marked_as_type_only_ensuring_they_are_written_in_the_output_file_s_format_based_on_the_module_setting, - DefaultValueDescription: false, - }, - { - Name: "isolatedDeclarations", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Interop_Constraints, - Description: diagnostics.Require_sufficient_annotation_on_exports_so_other_tools_can_trivially_generate_declaration_files, - DefaultValueDescription: false, - AffectsBuildInfo: true, - AffectsSemanticDiagnostics: true, - }, - { - Name: "erasableSyntaxOnly", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Interop_Constraints, - Description: diagnostics.Do_not_allow_runtime_constructs_that_are_not_part_of_ECMAScript, - DefaultValueDescription: false, - AffectsBuildInfo: true, - AffectsSemanticDiagnostics: true, - }, - { - Name: "libReplacement", - Kind: CommandLineOptionTypeBoolean, - AffectsProgramStructure: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Enable_lib_replacement, - DefaultValueDescription: false, - }, - - // Strict Type Checks - { - Name: "strict", - Kind: CommandLineOptionTypeBoolean, - // Though this affects semantic diagnostics, affectsSemanticDiagnostics is not set here - // The value of each strictFlag depends on own strictFlag value or this and never accessed directly. - // But we need to store `strict` in builf info, even though it won't be examined directly, so that the - // flags it controls (e.g. `strictNullChecks`) will be retrieved correctly - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Enable_all_strict_type_checking_options, - DefaultValueDescription: false, - }, - { - Name: "noImplicitAny", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Enable_error_reporting_for_expressions_and_declarations_with_an_implied_any_type, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - { - Name: "strictNullChecks", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.When_type_checking_take_into_account_null_and_undefined, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - { - Name: "strictFunctionTypes", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.When_assigning_functions_check_to_ensure_parameters_and_the_return_values_are_subtype_compatible, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - { - Name: "strictBindCallApply", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Check_that_the_arguments_for_bind_call_and_apply_methods_match_the_original_function, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - { - Name: "strictPropertyInitialization", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Check_for_class_properties_that_are_declared_but_not_set_in_the_constructor, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - { - Name: "strictBuiltinIteratorReturn", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Built_in_iterators_are_instantiated_with_a_TReturn_type_of_undefined_instead_of_any, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - { - Name: "noImplicitThis", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Enable_error_reporting_when_this_is_given_the_type_any, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - { - Name: "useUnknownInCatchVariables", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Default_catch_clause_variables_as_unknown_instead_of_any, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - { - Name: "alwaysStrict", - Kind: CommandLineOptionTypeBoolean, - AffectsSourceFile: true, - AffectsEmit: true, - AffectsBuildInfo: true, - strictFlag: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Ensure_use_strict_is_always_emitted, - DefaultValueDescription: diagnostics.X_false_unless_strict_is_set, - }, - - // Additional Checks - { - Name: "noUnusedLocals", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Enable_error_reporting_when_local_variables_aren_t_read, - DefaultValueDescription: false, - }, - { - Name: "noUnusedParameters", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Raise_an_error_when_a_function_parameter_isn_t_read, - DefaultValueDescription: false, - }, - { - Name: "exactOptionalPropertyTypes", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Interpret_optional_property_types_as_written_rather_than_adding_undefined, - DefaultValueDescription: false, - }, - { - Name: "noImplicitReturns", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Enable_error_reporting_for_codepaths_that_do_not_explicitly_return_in_a_function, - DefaultValueDescription: false, - }, - { - Name: "noFallthroughCasesInSwitch", - Kind: CommandLineOptionTypeBoolean, - AffectsBindDiagnostics: true, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Enable_error_reporting_for_fallthrough_cases_in_switch_statements, - DefaultValueDescription: false, - }, - { - Name: "noUncheckedIndexedAccess", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Add_undefined_to_a_type_when_accessed_using_an_index, - DefaultValueDescription: false, - }, - { - Name: "noImplicitOverride", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Ensure_overriding_members_in_derived_classes_are_marked_with_an_override_modifier, - DefaultValueDescription: false, - }, - { - Name: "noPropertyAccessFromIndexSignature", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: false, - Category: diagnostics.Type_Checking, - Description: diagnostics.Enforces_using_indexed_accessors_for_keys_declared_using_an_indexed_type, - DefaultValueDescription: false, - }, - - // Module Resolution - { - Name: "moduleResolution", - Kind: CommandLineOptionTypeEnum, - // new Map(Object.entries({ - // // N.B. The first entry specifies the value shown in `tsc --init` - // node10: ModuleResolutionKind.Node10, - // node: ModuleResolutionKind.Node10, - // classic: ModuleResolutionKind.Classic, - // node16: ModuleResolutionKind.Node16, - // nodenext: ModuleResolutionKind.NodeNext, - // bundler: ModuleResolutionKind.Bundler, - // })), - AffectsModuleResolution: true, - Category: diagnostics.Modules, - Description: diagnostics.Specify_how_TypeScript_looks_up_a_file_from_a_given_module_specifier, - DefaultValueDescription: diagnostics.X_module_AMD_or_UMD_or_System_or_ES6_then_Classic_Otherwise_Node, - }, - { - Name: "baseUrl", - Kind: CommandLineOptionTypeString, - AffectsModuleResolution: true, - IsFilePath: true, - Category: diagnostics.Modules, - Description: diagnostics.Specify_the_base_directory_to_resolve_non_relative_module_names, - }, - { - // this option can only be specified in tsconfig.json - // use type = object to copy the value as-is - Name: "paths", - Kind: CommandLineOptionTypeObject, - AffectsModuleResolution: true, - allowConfigDirTemplateSubstitution: true, - IsTSConfigOnly: true, - Category: diagnostics.Modules, - Description: diagnostics.Specify_a_set_of_entries_that_re_map_imports_to_additional_lookup_locations, - transpileOptionValue: core.TSUnknown, - }, - { - // this option can only be specified in tsconfig.json - // use type = object to copy the value as-is - Name: "rootDirs", - Kind: CommandLineOptionTypeList, - IsTSConfigOnly: true, - AffectsModuleResolution: true, - allowConfigDirTemplateSubstitution: true, - Category: diagnostics.Modules, - Description: diagnostics.Allow_multiple_folders_to_be_treated_as_one_when_resolving_modules, - transpileOptionValue: core.TSUnknown, - DefaultValueDescription: diagnostics.Computed_from_the_list_of_input_files, - }, - { - Name: "typeRoots", - Kind: CommandLineOptionTypeList, - AffectsModuleResolution: true, - allowConfigDirTemplateSubstitution: true, - Category: diagnostics.Modules, - Description: diagnostics.Specify_multiple_folders_that_act_like_Slashnode_modules_Slash_types, - }, - { - Name: "types", - Kind: CommandLineOptionTypeList, - AffectsProgramStructure: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Modules, - Description: diagnostics.Specify_type_package_names_to_be_included_without_being_referenced_in_a_source_file, - transpileOptionValue: core.TSUnknown, - }, - { - Name: "allowSyntheticDefaultImports", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Interop_Constraints, - Description: diagnostics.Allow_import_x_from_y_when_a_module_doesn_t_have_a_default_export, - DefaultValueDescription: diagnostics.X_module_system_or_esModuleInterop, - }, - { - Name: "esModuleInterop", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsEmit: true, - AffectsBuildInfo: true, - ShowInSimplifiedHelpView: true, - Category: diagnostics.Interop_Constraints, - Description: diagnostics.Emit_additional_JavaScript_to_ease_support_for_importing_CommonJS_modules_This_enables_allowSyntheticDefaultImports_for_type_compatibility, - DefaultValueDescription: false, - }, - { - Name: "preserveSymlinks", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Interop_Constraints, - Description: diagnostics.Disable_resolving_symlinks_to_their_realpath_This_correlates_to_the_same_flag_in_node, - DefaultValueDescription: false, - }, - { - Name: "allowUmdGlobalAccess", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Modules, - Description: diagnostics.Allow_accessing_UMD_globals_from_modules, - DefaultValueDescription: false, - }, - { - Name: "moduleSuffixes", - Kind: CommandLineOptionTypeList, - listPreserveFalsyValues: true, - AffectsModuleResolution: true, - Category: diagnostics.Modules, - Description: diagnostics.List_of_file_name_suffixes_to_search_when_resolving_a_module, - }, - { - Name: "allowImportingTsExtensions", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Modules, - Description: diagnostics.Allow_imports_to_include_TypeScript_file_extensions_Requires_moduleResolution_bundler_and_either_noEmit_or_emitDeclarationOnly_to_be_set, - DefaultValueDescription: false, - transpileOptionValue: core.TSUnknown, - }, - { - Name: "rewriteRelativeImportExtensions", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Modules, - Description: diagnostics.Rewrite_ts_tsx_mts_and_cts_file_extensions_in_relative_import_paths_to_their_JavaScript_equivalent_in_output_files, - DefaultValueDescription: false, - }, - { - Name: "resolvePackageJsonExports", - Kind: CommandLineOptionTypeBoolean, - AffectsModuleResolution: true, - Category: diagnostics.Modules, - Description: diagnostics.Use_the_package_json_exports_field_when_resolving_package_imports, - DefaultValueDescription: diagnostics.X_true_when_moduleResolution_is_node16_nodenext_or_bundler_otherwise_false, - }, - { - Name: "resolvePackageJsonImports", - Kind: CommandLineOptionTypeBoolean, - AffectsModuleResolution: true, - Category: diagnostics.Modules, - Description: diagnostics.Use_the_package_json_imports_field_when_resolving_imports, - DefaultValueDescription: diagnostics.X_true_when_moduleResolution_is_node16_nodenext_or_bundler_otherwise_false, - }, - { - Name: "customConditions", - Kind: CommandLineOptionTypeList, - AffectsModuleResolution: true, - Category: diagnostics.Modules, - Description: diagnostics.Conditions_to_set_in_addition_to_the_resolver_specific_defaults_when_resolving_imports, - }, - { - Name: "noUncheckedSideEffectImports", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Modules, - Description: diagnostics.Check_side_effect_imports, - DefaultValueDescription: false, - }, - - // Source Maps - { - Name: "sourceRoot", - Kind: CommandLineOptionTypeString, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Specify_the_root_path_for_debuggers_to_find_the_reference_source_code, - }, - { - Name: "mapRoot", - Kind: CommandLineOptionTypeString, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Specify_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations, - }, - { - Name: "inlineSources", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Include_source_code_in_the_sourcemaps_inside_the_emitted_JavaScript, - DefaultValueDescription: false, - }, - - // Experimental - { - Name: "experimentalDecorators", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Enable_experimental_support_for_legacy_experimental_decorators, - DefaultValueDescription: false, - }, - { - Name: "emitDecoratorMetadata", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Emit_design_type_metadata_for_decorated_declarations_in_source_files, - DefaultValueDescription: false, - }, - - // Advanced - { - Name: "jsxFactory", - Kind: CommandLineOptionTypeString, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Specify_the_JSX_factory_function_used_when_targeting_React_JSX_emit_e_g_React_createElement_or_h, - DefaultValueDescription: "`React.createElement`", - }, - { - Name: "jsxFragmentFactory", - Kind: CommandLineOptionTypeString, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Specify_the_JSX_Fragment_reference_used_for_fragments_when_targeting_React_JSX_emit_e_g_React_Fragment_or_Fragment, - DefaultValueDescription: "React.Fragment", - }, - { - Name: "jsxImportSource", - Kind: CommandLineOptionTypeString, - AffectsSemanticDiagnostics: true, - AffectsEmit: true, - AffectsBuildInfo: true, - AffectsModuleResolution: true, - AffectsSourceFile: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Specify_module_specifier_used_to_import_the_JSX_factory_functions_when_using_jsx_Colon_react_jsx_Asterisk, - DefaultValueDescription: "react", - }, - { - Name: "resolveJsonModule", - Kind: CommandLineOptionTypeBoolean, - AffectsModuleResolution: true, - Category: diagnostics.Modules, - Description: diagnostics.Enable_importing_json_files, - DefaultValueDescription: false, - }, - { - Name: "allowArbitraryExtensions", - Kind: CommandLineOptionTypeBoolean, - AffectsProgramStructure: true, - Category: diagnostics.Modules, - Description: diagnostics.Enable_importing_files_with_any_extension_provided_a_declaration_file_is_present, - DefaultValueDescription: false, - }, - - { - Name: "reactNamespace", - Kind: CommandLineOptionTypeString, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Specify_the_object_invoked_for_createElement_This_only_applies_when_targeting_react_JSX_emit, - DefaultValueDescription: "`React`", - }, - { - Name: "skipDefaultLibCheck", - Kind: CommandLineOptionTypeBoolean, - // We need to store these to determine whether `lib` files need to be rechecked - AffectsBuildInfo: true, - Category: diagnostics.Completeness, - Description: diagnostics.Skip_type_checking_d_ts_files_that_are_included_with_TypeScript, - DefaultValueDescription: false, - }, - { - Name: "emitBOM", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Emit_a_UTF_8_Byte_Order_Mark_BOM_in_the_beginning_of_output_files, - DefaultValueDescription: false, - }, - { - Name: "newLine", - Kind: CommandLineOptionTypeEnum, // newLineOptionMap, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Set_the_newline_character_for_emitting_files, - DefaultValueDescription: "lf", - }, - { - Name: "noErrorTruncation", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Output_Formatting, - Description: diagnostics.Disable_truncating_types_in_error_messages, - DefaultValueDescription: false, - }, - { - Name: "noLib", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Language_and_Environment, - AffectsProgramStructure: true, - Description: diagnostics.Disable_including_any_library_files_including_the_default_lib_d_ts, - // We are not returning a sourceFile for lib file when asked by the program, - // so pass --noLib to avoid reporting a file not found error. - transpileOptionValue: core.TSTrue, - DefaultValueDescription: false, - }, - { - Name: "noResolve", - Kind: CommandLineOptionTypeBoolean, - AffectsModuleResolution: true, - Category: diagnostics.Modules, - Description: diagnostics.Disallow_import_s_require_s_or_reference_s_from_expanding_the_number_of_files_TypeScript_should_add_to_a_project, - // We are not doing a full typecheck, we are not resolving the whole context, - // so pass --noResolve to avoid reporting missing file errors. - transpileOptionValue: core.TSTrue, - DefaultValueDescription: false, - }, - { - Name: "stripInternal", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Disable_emitting_declarations_that_have_internal_in_their_JSDoc_comments, - DefaultValueDescription: false, - }, - { - Name: "disableSizeLimit", - Kind: CommandLineOptionTypeBoolean, - AffectsProgramStructure: true, - Category: diagnostics.Editor_Support, - Description: diagnostics.Remove_the_20mb_cap_on_total_source_code_size_for_JavaScript_files_in_the_TypeScript_language_server, - DefaultValueDescription: false, - }, - { - Name: "disableSourceOfProjectReferenceRedirect", - Kind: CommandLineOptionTypeBoolean, - IsTSConfigOnly: true, - Category: diagnostics.Projects, - Description: diagnostics.Disable_preferring_source_files_instead_of_declaration_files_when_referencing_composite_projects, - DefaultValueDescription: false, - }, - { - Name: "disableSolutionSearching", - Kind: CommandLineOptionTypeBoolean, - IsTSConfigOnly: true, - Category: diagnostics.Projects, - Description: diagnostics.Opt_a_project_out_of_multi_project_reference_checking_when_editing, - DefaultValueDescription: false, - }, - { - Name: "disableReferencedProjectLoad", - Kind: CommandLineOptionTypeBoolean, - IsTSConfigOnly: true, - Category: diagnostics.Projects, - Description: diagnostics.Reduce_the_number_of_projects_loaded_automatically_by_TypeScript, - DefaultValueDescription: false, - }, - { - Name: "noEmitHelpers", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Disable_generating_custom_helper_functions_like_extends_in_compiled_output, - DefaultValueDescription: false, - }, - { - Name: "noEmitOnError", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - transpileOptionValue: core.TSUnknown, - Description: diagnostics.Disable_emitting_files_if_any_type_checking_errors_are_reported, - DefaultValueDescription: false, - }, - { - Name: "preserveConstEnums", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Emit, - Description: diagnostics.Disable_erasing_const_enum_declarations_in_generated_code, - DefaultValueDescription: false, - }, - { - Name: "declarationDir", - Kind: CommandLineOptionTypeString, - AffectsEmit: true, - AffectsBuildInfo: true, - AffectsDeclarationPath: true, - IsFilePath: true, - Category: diagnostics.Emit, - transpileOptionValue: core.TSUnknown, - Description: diagnostics.Specify_the_output_directory_for_generated_declaration_files, - }, - { - Name: "skipLibCheck", - Kind: CommandLineOptionTypeBoolean, - // We need to store these to determine whether `lib` files need to be rechecked - AffectsBuildInfo: true, - Category: diagnostics.Completeness, - Description: diagnostics.Skip_type_checking_all_d_ts_files, - DefaultValueDescription: false, - }, - { - Name: "allowUnusedLabels", - Kind: CommandLineOptionTypeBoolean, - AffectsBindDiagnostics: true, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Disable_error_reporting_for_unused_labels, - DefaultValueDescription: core.TSUnknown, - }, - { - Name: "allowUnreachableCode", - Kind: CommandLineOptionTypeBoolean, - AffectsBindDiagnostics: true, - AffectsSemanticDiagnostics: true, - AffectsBuildInfo: true, - Category: diagnostics.Type_Checking, - Description: diagnostics.Disable_error_reporting_for_unreachable_code, - DefaultValueDescription: core.TSUnknown, - }, - { - Name: "forceConsistentCasingInFileNames", - Kind: CommandLineOptionTypeBoolean, - AffectsModuleResolution: true, - Category: diagnostics.Interop_Constraints, - Description: diagnostics.Ensure_that_casing_is_correct_in_imports, - DefaultValueDescription: true, - }, - { - Name: "maxNodeModuleJsDepth", - Kind: CommandLineOptionTypeNumber, - AffectsModuleResolution: true, - Category: diagnostics.JavaScript_Support, - Description: diagnostics.Specify_the_maximum_folder_depth_used_for_checking_JavaScript_files_from_node_modules_Only_applicable_with_allowJs, - DefaultValueDescription: 0, - }, - { - Name: "useDefineForClassFields", - Kind: CommandLineOptionTypeBoolean, - AffectsSemanticDiagnostics: true, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Language_and_Environment, - Description: diagnostics.Emit_ECMAScript_standard_compliant_class_fields, - DefaultValueDescription: diagnostics.X_true_for_ES2022_and_above_including_ESNext, - }, - { - Name: "preserveValueImports", - Kind: CommandLineOptionTypeBoolean, - AffectsEmit: true, - AffectsBuildInfo: true, - Category: diagnostics.Backwards_Compatibility, - Description: diagnostics.Preserve_unused_imported_values_in_the_JavaScript_output_that_would_otherwise_be_removed, - DefaultValueDescription: false, - }, - - { - // A list of plugins to load in the language service - Name: "plugins", - Kind: CommandLineOptionTypeList, - IsTSConfigOnly: true, - Description: diagnostics.Specify_a_list_of_language_service_plugins_to_include, - Category: diagnostics.Editor_Support, - }, - { - Name: "moduleDetection", - Kind: CommandLineOptionTypeEnum, - AffectsSourceFile: true, - AffectsModuleResolution: true, - Description: diagnostics.Control_what_method_is_used_to_detect_module_format_JS_files, - Category: diagnostics.Language_and_Environment, - DefaultValueDescription: diagnostics.X_auto_Colon_Treat_files_with_imports_exports_import_meta_jsx_with_jsx_Colon_react_jsx_or_esm_format_with_module_Colon_node16_as_modules, - }, - { - Name: "ignoreDeprecations", - Kind: CommandLineOptionTypeString, - DefaultValueDescription: core.TSUnknown, - }, -} - -var optionsType = reflect.TypeFor[core.CompilerOptions]() - -func optionsHaveChanges(oldOptions *core.CompilerOptions, newOptions *core.CompilerOptions, declFilter func(*CommandLineOption) bool) bool { - if oldOptions == newOptions { - return false - } - if oldOptions == nil || newOptions == nil { - return true - } - oldOptionsValue := reflect.ValueOf(oldOptions).Elem() - return ForEachCompilerOptionValue(newOptions, declFilter, func(option *CommandLineOption, value reflect.Value, i int) bool { - newValue := value.Interface() - oldValue := oldOptionsValue.Field(i).Interface() - if option.strictFlag { - return oldOptions.GetStrictOptionValue(oldValue.(core.Tristate)) != newOptions.GetStrictOptionValue(newValue.(core.Tristate)) - } - if option.allowJsFlag { - return oldOptions.GetAllowJS() != newOptions.GetAllowJS() - } - return !reflect.DeepEqual(newValue, oldValue) - }) -} - -func ForEachCompilerOptionValue(options *core.CompilerOptions, declFilter func(*CommandLineOption) bool, fn func(option *CommandLineOption, value reflect.Value, i int) bool) bool { - optionsValue := reflect.ValueOf(options).Elem() - for i := range optionsValue.NumField() { - field := optionsType.Field(i) - if !field.IsExported() { - continue - } - if optionDeclaration := CommandLineCompilerOptionsMap.Get(field.Name); optionDeclaration != nil && declFilter(optionDeclaration) { - if fn(optionDeclaration, optionsValue.Field(i), i) { - return true - } - } - } - return false -} - -func CompilerOptionsAffectSemanticDiagnostics( - oldOptions *core.CompilerOptions, - newOptions *core.CompilerOptions, -) bool { - return optionsHaveChanges(oldOptions, newOptions, func(option *CommandLineOption) bool { - return option.AffectsSemanticDiagnostics - }) -} - -func CompilerOptionsAffectDeclarationPath( - oldOptions *core.CompilerOptions, - newOptions *core.CompilerOptions, -) bool { - return optionsHaveChanges(oldOptions, newOptions, func(option *CommandLineOption) bool { - return option.AffectsDeclarationPath - }) -} - -func CompilerOptionsAffectEmit(oldOptions *core.CompilerOptions, newOptions *core.CompilerOptions) bool { - return optionsHaveChanges(oldOptions, newOptions, func(option *CommandLineOption) bool { - return option.AffectsEmit - }) -} diff --git a/kitcom/internal/tsgo/tsoptions/declstypeacquisition.go b/kitcom/internal/tsgo/tsoptions/declstypeacquisition.go deleted file mode 100644 index fe7ceb6..0000000 --- a/kitcom/internal/tsgo/tsoptions/declstypeacquisition.go +++ /dev/null @@ -1,29 +0,0 @@ -package tsoptions - -var typeAcquisitionDeclaration = &CommandLineOption{ - Name: "typeAcquisition", - Kind: CommandLineOptionTypeObject, - ElementOptions: commandLineOptionsToMap(typeAcquisitionDecls), -} - -// Do not delete this without updating the website's tsconfig generation. -var typeAcquisitionDecls = []*CommandLineOption{ - { - Name: "enable", - Kind: CommandLineOptionTypeBoolean, - DefaultValueDescription: false, - }, - { - Name: "include", - Kind: CommandLineOptionTypeList, - }, - { - Name: "exclude", - Kind: CommandLineOptionTypeList, - }, - { - Name: "disableFilenameBasedTypeAcquisition", - Kind: CommandLineOptionTypeBoolean, - DefaultValueDescription: false, - }, -} diff --git a/kitcom/internal/tsgo/tsoptions/declswatch.go b/kitcom/internal/tsgo/tsoptions/declswatch.go deleted file mode 100644 index 34f386e..0000000 --- a/kitcom/internal/tsgo/tsoptions/declswatch.go +++ /dev/null @@ -1,88 +0,0 @@ -package tsoptions - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -var optionsForWatch = []*CommandLineOption{ - { - Name: "watchInterval", - Kind: CommandLineOptionTypeNumber, - Category: diagnostics.Watch_and_Build_Modes, - }, - { - Name: "watchFile", - Kind: CommandLineOptionTypeEnum, - // new Map(Object.entries({ - // fixedpollinginterval: WatchFileKind.FixedPollingInterval, - // prioritypollinginterval: WatchFileKind.PriorityPollingInterval, - // dynamicprioritypolling: WatchFileKind.DynamicPriorityPolling, - // fixedchunksizepolling: WatchFileKind.FixedChunkSizePolling, - // usefsevents: WatchFileKind.UseFsEvents, - // usefseventsonparentdirectory: WatchFileKind.UseFsEventsOnParentDirectory, - // })), - Category: diagnostics.Watch_and_Build_Modes, - Description: diagnostics.Specify_how_the_TypeScript_watch_mode_works, - DefaultValueDescription: core.WatchFileKindUseFsEvents, - }, - { - Name: "watchDirectory", - Kind: CommandLineOptionTypeEnum, - // new Map(Object.entries({ - // usefsevents: WatchDirectoryKind.UseFsEvents, - // fixedpollinginterval: WatchDirectoryKind.FixedPollingInterval, - // dynamicprioritypolling: WatchDirectoryKind.DynamicPriorityPolling, - // fixedchunksizepolling: WatchDirectoryKind.FixedChunkSizePolling, - // })), - Category: diagnostics.Watch_and_Build_Modes, - Description: diagnostics.Specify_how_directories_are_watched_on_systems_that_lack_recursive_file_watching_functionality, - DefaultValueDescription: core.WatchDirectoryKindUseFsEvents, - }, - { - Name: "fallbackPolling", - Kind: CommandLineOptionTypeEnum, - // new Map(Object.entries({ - // fixedinterval: PollingWatchKind.FixedInterval, - // priorityinterval: PollingWatchKind.PriorityInterval, - // dynamicpriority: PollingWatchKind.DynamicPriority, - // fixedchunksize: PollingWatchKind.FixedChunkSize, - // })), - Category: diagnostics.Watch_and_Build_Modes, - Description: diagnostics.Specify_what_approach_the_watcher_should_use_if_the_system_runs_out_of_native_file_watchers, - DefaultValueDescription: core.PollingKindPriorityInterval, - }, - { - Name: "synchronousWatchDirectory", - Kind: CommandLineOptionTypeBoolean, - Category: diagnostics.Watch_and_Build_Modes, - Description: diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively, - DefaultValueDescription: false, - }, - { - Name: "excludeDirectories", - Kind: CommandLineOptionTypeList, - // element: { - // Name: "excludeDirectory", - // Kind: "string", - // isFilePath: true, - // extraValidation: specToDiagnostic, - // }, - allowConfigDirTemplateSubstitution: true, - Category: diagnostics.Watch_and_Build_Modes, - Description: diagnostics.Remove_a_list_of_directories_from_the_watch_process, - }, - { - Name: "excludeFiles", - Kind: CommandLineOptionTypeList, - // element: { - // Name: "excludeFile", - // Kind: "string", - // isFilePath: true, - // extraValidation: specToDiagnostic, - // }, - allowConfigDirTemplateSubstitution: true, - Category: diagnostics.Watch_and_Build_Modes, - Description: diagnostics.Remove_a_list_of_files_from_the_watch_mode_s_processing, - }, -} diff --git a/kitcom/internal/tsgo/tsoptions/diagnostics.go b/kitcom/internal/tsgo/tsoptions/diagnostics.go deleted file mode 100644 index 1a54889..0000000 --- a/kitcom/internal/tsgo/tsoptions/diagnostics.go +++ /dev/null @@ -1,67 +0,0 @@ -package tsoptions - -import ( - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -type DidYouMeanOptionsDiagnostics struct { - alternateMode *AlternateModeDiagnostics - OptionDeclarations []*CommandLineOption - UnknownOptionDiagnostic *diagnostics.Message - UnknownDidYouMeanDiagnostic *diagnostics.Message -} - -type AlternateModeDiagnostics struct { - diagnostic *diagnostics.Message - optionsNameMap *NameMap -} - -type ParseCommandLineWorkerDiagnostics struct { - didYouMean DidYouMeanOptionsDiagnostics - optionsNameMap *NameMap - optionsNameMapOnce sync.Once - OptionTypeMismatchDiagnostic *diagnostics.Message -} - -var CompilerOptionsDidYouMeanDiagnostics = getParseCommandLineWorkerDiagnostics(OptionsDeclarations) - -func getParseCommandLineWorkerDiagnostics(decls []*CommandLineOption) *ParseCommandLineWorkerDiagnostics { - // this will only return the correct diagnostics for `compiler` mode, and is factored into a function for testing reasons. - return &ParseCommandLineWorkerDiagnostics{ - didYouMean: DidYouMeanOptionsDiagnostics{ - alternateMode: &AlternateModeDiagnostics{ - diagnostic: diagnostics.Compiler_option_0_may_only_be_used_with_build, - optionsNameMap: BuildNameMap, - }, - OptionDeclarations: decls, - UnknownOptionDiagnostic: diagnostics.Unknown_compiler_option_0, - UnknownDidYouMeanDiagnostic: diagnostics.Unknown_compiler_option_0_Did_you_mean_1, - }, - OptionTypeMismatchDiagnostic: diagnostics.Compiler_option_0_expects_an_argument, - } -} - -var watchOptionsDidYouMeanDiagnostics = &ParseCommandLineWorkerDiagnostics{ - didYouMean: DidYouMeanOptionsDiagnostics{ - // no alternateMode - OptionDeclarations: optionsForWatch, - UnknownOptionDiagnostic: diagnostics.Unknown_watch_option_0, - UnknownDidYouMeanDiagnostic: diagnostics.Unknown_watch_option_0_Did_you_mean_1, - }, - OptionTypeMismatchDiagnostic: diagnostics.Watch_option_0_requires_a_value_of_type_1, -} - -var buildOptionsDidYouMeanDiagnostics = &ParseCommandLineWorkerDiagnostics{ - didYouMean: DidYouMeanOptionsDiagnostics{ - alternateMode: &AlternateModeDiagnostics{ - diagnostic: diagnostics.Compiler_option_0_may_not_be_used_with_build, - optionsNameMap: CompilerNameMap, - }, - OptionDeclarations: BuildOpts, - UnknownOptionDiagnostic: diagnostics.Unknown_build_option_0, - UnknownDidYouMeanDiagnostic: diagnostics.Unknown_build_option_0_Did_you_mean_1, - }, - OptionTypeMismatchDiagnostic: diagnostics.Build_option_0_requires_a_value_of_type_1, -} diff --git a/kitcom/internal/tsgo/tsoptions/enummaps.go b/kitcom/internal/tsgo/tsoptions/enummaps.go deleted file mode 100644 index 661fa31..0000000 --- a/kitcom/internal/tsgo/tsoptions/enummaps.go +++ /dev/null @@ -1,256 +0,0 @@ -package tsoptions - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -var LibMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - // JavaScript only - {Key: "es5", Value: "lib.es5.d.ts"}, - {Key: "es6", Value: "lib.es2015.d.ts"}, - {Key: "es2015", Value: "lib.es2015.d.ts"}, - {Key: "es7", Value: "lib.es2016.d.ts"}, - {Key: "es2016", Value: "lib.es2016.d.ts"}, - {Key: "es2017", Value: "lib.es2017.d.ts"}, - {Key: "es2018", Value: "lib.es2018.d.ts"}, - {Key: "es2019", Value: "lib.es2019.d.ts"}, - {Key: "es2020", Value: "lib.es2020.d.ts"}, - {Key: "es2021", Value: "lib.es2021.d.ts"}, - {Key: "es2022", Value: "lib.es2022.d.ts"}, - {Key: "es2023", Value: "lib.es2023.d.ts"}, - {Key: "es2024", Value: "lib.es2024.d.ts"}, - {Key: "esnext", Value: "lib.esnext.d.ts"}, - // Host only - {Key: "dom", Value: "lib.dom.d.ts"}, - {Key: "dom.iterable", Value: "lib.dom.iterable.d.ts"}, - {Key: "dom.asynciterable", Value: "lib.dom.asynciterable.d.ts"}, - {Key: "webworker", Value: "lib.webworker.d.ts"}, - {Key: "webworker.importscripts", Value: "lib.webworker.importscripts.d.ts"}, - {Key: "webworker.iterable", Value: "lib.webworker.iterable.d.ts"}, - {Key: "webworker.asynciterable", Value: "lib.webworker.asynciterable.d.ts"}, - {Key: "scripthost", Value: "lib.scripthost.d.ts"}, - // ES2015 Or ESNext By-feature options - {Key: "es2015.core", Value: "lib.es2015.core.d.ts"}, - {Key: "es2015.collection", Value: "lib.es2015.collection.d.ts"}, - {Key: "es2015.generator", Value: "lib.es2015.generator.d.ts"}, - {Key: "es2015.iterable", Value: "lib.es2015.iterable.d.ts"}, - {Key: "es2015.promise", Value: "lib.es2015.promise.d.ts"}, - {Key: "es2015.proxy", Value: "lib.es2015.proxy.d.ts"}, - {Key: "es2015.reflect", Value: "lib.es2015.reflect.d.ts"}, - {Key: "es2015.symbol", Value: "lib.es2015.symbol.d.ts"}, - {Key: "es2015.symbol.wellknown", Value: "lib.es2015.symbol.wellknown.d.ts"}, - {Key: "es2016.array.include", Value: "lib.es2016.array.include.d.ts"}, - {Key: "es2016.intl", Value: "lib.es2016.intl.d.ts"}, - {Key: "es2017.arraybuffer", Value: "lib.es2017.arraybuffer.d.ts"}, - {Key: "es2017.date", Value: "lib.es2017.date.d.ts"}, - {Key: "es2017.object", Value: "lib.es2017.object.d.ts"}, - {Key: "es2017.sharedmemory", Value: "lib.es2017.sharedmemory.d.ts"}, - {Key: "es2017.string", Value: "lib.es2017.string.d.ts"}, - {Key: "es2017.intl", Value: "lib.es2017.intl.d.ts"}, - {Key: "es2017.typedarrays", Value: "lib.es2017.typedarrays.d.ts"}, - {Key: "es2018.asyncgenerator", Value: "lib.es2018.asyncgenerator.d.ts"}, - {Key: "es2018.asynciterable", Value: "lib.es2018.asynciterable.d.ts"}, - {Key: "es2018.intl", Value: "lib.es2018.intl.d.ts"}, - {Key: "es2018.promise", Value: "lib.es2018.promise.d.ts"}, - {Key: "es2018.regexp", Value: "lib.es2018.regexp.d.ts"}, - {Key: "es2019.array", Value: "lib.es2019.array.d.ts"}, - {Key: "es2019.object", Value: "lib.es2019.object.d.ts"}, - {Key: "es2019.string", Value: "lib.es2019.string.d.ts"}, - {Key: "es2019.symbol", Value: "lib.es2019.symbol.d.ts"}, - {Key: "es2019.intl", Value: "lib.es2019.intl.d.ts"}, - {Key: "es2020.bigint", Value: "lib.es2020.bigint.d.ts"}, - {Key: "es2020.date", Value: "lib.es2020.date.d.ts"}, - {Key: "es2020.promise", Value: "lib.es2020.promise.d.ts"}, - {Key: "es2020.sharedmemory", Value: "lib.es2020.sharedmemory.d.ts"}, - {Key: "es2020.string", Value: "lib.es2020.string.d.ts"}, - {Key: "es2020.symbol.wellknown", Value: "lib.es2020.symbol.wellknown.d.ts"}, - {Key: "es2020.intl", Value: "lib.es2020.intl.d.ts"}, - {Key: "es2020.number", Value: "lib.es2020.number.d.ts"}, - {Key: "es2021.promise", Value: "lib.es2021.promise.d.ts"}, - {Key: "es2021.string", Value: "lib.es2021.string.d.ts"}, - {Key: "es2021.weakref", Value: "lib.es2021.weakref.d.ts"}, - {Key: "es2021.intl", Value: "lib.es2021.intl.d.ts"}, - {Key: "es2022.array", Value: "lib.es2022.array.d.ts"}, - {Key: "es2022.error", Value: "lib.es2022.error.d.ts"}, - {Key: "es2022.intl", Value: "lib.es2022.intl.d.ts"}, - {Key: "es2022.object", Value: "lib.es2022.object.d.ts"}, - {Key: "es2022.string", Value: "lib.es2022.string.d.ts"}, - {Key: "es2022.regexp", Value: "lib.es2022.regexp.d.ts"}, - {Key: "es2023.array", Value: "lib.es2023.array.d.ts"}, - {Key: "es2023.collection", Value: "lib.es2023.collection.d.ts"}, - {Key: "es2023.intl", Value: "lib.es2023.intl.d.ts"}, - {Key: "es2024.arraybuffer", Value: "lib.es2024.arraybuffer.d.ts"}, - {Key: "es2024.collection", Value: "lib.es2024.collection.d.ts"}, - {Key: "es2024.object", Value: "lib.es2024.object.d.ts"}, - {Key: "es2024.promise", Value: "lib.es2024.promise.d.ts"}, - {Key: "es2024.regexp", Value: "lib.es2024.regexp.d.ts"}, - {Key: "es2024.sharedmemory", Value: "lib.es2024.sharedmemory.d.ts"}, - {Key: "es2024.string", Value: "lib.es2024.string.d.ts"}, - {Key: "esnext.array", Value: "lib.es2023.array.d.ts"}, - {Key: "esnext.collection", Value: "lib.esnext.collection.d.ts"}, - {Key: "esnext.symbol", Value: "lib.es2019.symbol.d.ts"}, - {Key: "esnext.asynciterable", Value: "lib.es2018.asynciterable.d.ts"}, - {Key: "esnext.intl", Value: "lib.esnext.intl.d.ts"}, - {Key: "esnext.disposable", Value: "lib.esnext.disposable.d.ts"}, - {Key: "esnext.bigint", Value: "lib.es2020.bigint.d.ts"}, - {Key: "esnext.string", Value: "lib.es2022.string.d.ts"}, - {Key: "esnext.promise", Value: "lib.es2024.promise.d.ts"}, - {Key: "esnext.weakref", Value: "lib.es2021.weakref.d.ts"}, - {Key: "esnext.decorators", Value: "lib.esnext.decorators.d.ts"}, - {Key: "esnext.object", Value: "lib.es2024.object.d.ts"}, - {Key: "esnext.array", Value: "lib.esnext.array.d.ts"}, - {Key: "esnext.regexp", Value: "lib.es2024.regexp.d.ts"}, - {Key: "esnext.string", Value: "lib.es2024.string.d.ts"}, - {Key: "esnext.iterator", Value: "lib.esnext.iterator.d.ts"}, - {Key: "esnext.promise", Value: "lib.esnext.promise.d.ts"}, - {Key: "esnext.float16", Value: "lib.esnext.float16.d.ts"}, - {Key: "esnext.error", Value: "lib.esnext.error.d.ts"}, - {Key: "esnext.sharedmemory", Value: "lib.esnext.sharedmemory.d.ts"}, - {Key: "decorators", Value: "lib.decorators.d.ts"}, - {Key: "decorators.legacy", Value: "lib.decorators.legacy.d.ts"}, -}) - -var ( - Libs = slices.Collect(LibMap.Keys()) - LibFilesSet = collections.NewSetFromItems(core.Map(slices.Collect(LibMap.Values()), func(s any) string { return s.(string) })...) -) - -func GetLibFileName(libName string) (string, bool) { - // checks if the libName is a valid lib name or file name and converts the lib name to the filename if needed - libName = tspath.ToFileNameLowerCase(libName) - if LibFilesSet.Has(libName) { - return libName, true - } - lib, ok := LibMap.Get(libName) - if !ok { - return "", false - } - return lib.(string), true -} - -var moduleResolutionOptionMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "node16", Value: core.ModuleResolutionKindNode16}, - {Key: "nodenext", Value: core.ModuleResolutionKindNodeNext}, - {Key: "bundler", Value: core.ModuleResolutionKindBundler}, - {Key: "node", Value: core.ModuleResolutionKindBundler}, // TODO: remove when node is fully deprecated -- this is helpful for testing porting - {Key: "classic", Value: core.ModuleResolutionKindBundler}, // TODO: remove when fully deprecated - {Key: "node10", Value: core.ModuleResolutionKindBundler}, // TODO: remove when fully deprecated -}) - -var targetOptionMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "es3", Value: core.ScriptTargetES3}, - {Key: "es5", Value: core.ScriptTargetES5}, - {Key: "es6", Value: core.ScriptTargetES2015}, - {Key: "es2015", Value: core.ScriptTargetES2015}, - {Key: "es2016", Value: core.ScriptTargetES2016}, - {Key: "es2017", Value: core.ScriptTargetES2017}, - {Key: "es2018", Value: core.ScriptTargetES2018}, - {Key: "es2019", Value: core.ScriptTargetES2019}, - {Key: "es2020", Value: core.ScriptTargetES2020}, - {Key: "es2021", Value: core.ScriptTargetES2021}, - {Key: "es2022", Value: core.ScriptTargetES2022}, - {Key: "es2023", Value: core.ScriptTargetES2023}, - {Key: "es2024", Value: core.ScriptTargetES2024}, - {Key: "esnext", Value: core.ScriptTargetESNext}, -}) - -var moduleOptionMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "none", Value: core.ModuleKindNone}, - {Key: "commonjs", Value: core.ModuleKindCommonJS}, - {Key: "amd", Value: core.ModuleKindAMD}, - {Key: "system", Value: core.ModuleKindSystem}, - {Key: "umd", Value: core.ModuleKindUMD}, - {Key: "es6", Value: core.ModuleKindES2015}, - {Key: "es2015", Value: core.ModuleKindES2015}, - {Key: "es2020", Value: core.ModuleKindES2020}, - {Key: "es2022", Value: core.ModuleKindES2022}, - {Key: "esnext", Value: core.ModuleKindESNext}, - {Key: "node16", Value: core.ModuleKindNode16}, - {Key: "node18", Value: core.ModuleKindNode18}, - {Key: "node20", Value: core.ModuleKindNode20}, - {Key: "nodenext", Value: core.ModuleKindNodeNext}, - {Key: "preserve", Value: core.ModuleKindPreserve}, -}) - -var moduleDetectionOptionMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "auto", Value: core.ModuleDetectionKindAuto}, - {Key: "legacy", Value: core.ModuleDetectionKindLegacy}, - {Key: "force", Value: core.ModuleDetectionKindForce}, -}) - -var jsxOptionMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "preserve", Value: core.JsxEmitPreserve}, - {Key: "react-native", Value: core.JsxEmitReactNative}, - {Key: "react-jsx", Value: core.JsxEmitReactJSX}, - {Key: "react-jsxdev", Value: core.JsxEmitReactJSXDev}, - {Key: "react", Value: core.JsxEmitReact}, -}) - -var InverseJsxOptionMap = collections.NewOrderedMapFromList(func() []collections.MapEntry[core.JsxEmit, string] { - entries := make([]collections.MapEntry[core.JsxEmit, string], 0, jsxOptionMap.Size()) - for key, value := range jsxOptionMap.Entries() { - entries = append(entries, collections.MapEntry[core.JsxEmit, string]{ - Key: value.(core.JsxEmit), - Value: key, - }) - } - return entries -}()) - -var newLineOptionMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "crlf", Value: core.NewLineKindCRLF}, - {Key: "lf", Value: core.NewLineKindLF}, -}) - -var targetToLibMap = map[core.ScriptTarget]string{ - core.ScriptTargetESNext: "lib.esnext.full.d.ts", - core.ScriptTargetES2024: "lib.es2024.full.d.ts", - core.ScriptTargetES2023: "lib.es2023.full.d.ts", - core.ScriptTargetES2022: "lib.es2022.full.d.ts", - core.ScriptTargetES2021: "lib.es2021.full.d.ts", - core.ScriptTargetES2020: "lib.es2020.full.d.ts", - core.ScriptTargetES2019: "lib.es2019.full.d.ts", - core.ScriptTargetES2018: "lib.es2018.full.d.ts", - core.ScriptTargetES2017: "lib.es2017.full.d.ts", - core.ScriptTargetES2016: "lib.es2016.full.d.ts", - core.ScriptTargetES2015: "lib.es6.d.ts", // We don't use lib.es2015.full.d.ts due to breaking change. -} - -func TargetToLibMap() map[core.ScriptTarget]string { - return targetToLibMap -} - -func GetDefaultLibFileName(options *core.CompilerOptions) string { - name, ok := targetToLibMap[options.GetEmitScriptTarget()] - if !ok { - return "lib.d.ts" - } - return name -} - -var watchFileEnumMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "fixedpollinginterval", Value: core.WatchFileKindFixedPollingInterval}, - {Key: "prioritypollinginterval", Value: core.WatchFileKindPriorityPollingInterval}, - {Key: "dynamicprioritypolling", Value: core.WatchFileKindDynamicPriorityPolling}, - {Key: "fixedchunksizepolling", Value: core.WatchFileKindFixedChunkSizePolling}, - {Key: "usefsevents", Value: core.WatchFileKindUseFsEvents}, - {Key: "usefseventsonparentdirectory", Value: core.WatchFileKindUseFsEventsOnParentDirectory}, -}) - -var watchDirectoryEnumMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "usefsevents", Value: core.WatchDirectoryKindUseFsEvents}, - {Key: "fixedpollinginterval", Value: core.WatchDirectoryKindFixedPollingInterval}, - {Key: "dynamicprioritypolling", Value: core.WatchDirectoryKindDynamicPriorityPolling}, - {Key: "fixedchunksizepolling", Value: core.WatchDirectoryKindFixedChunkSizePolling}, -}) - -var fallbackEnumMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, any]{ - {Key: "fixedinterval", Value: core.PollingKindFixedInterval}, - {Key: "priorityinterval", Value: core.PollingKindPriorityInterval}, - {Key: "dynamicpriority", Value: core.PollingKindDynamicPriority}, - {Key: "fixedchunksize", Value: core.PollingKindFixedChunkSize}, -}) diff --git a/kitcom/internal/tsgo/tsoptions/errors.go b/kitcom/internal/tsgo/tsoptions/errors.go deleted file mode 100644 index c8f698f..0000000 --- a/kitcom/internal/tsgo/tsoptions/errors.go +++ /dev/null @@ -1,105 +0,0 @@ -package tsoptions - -import ( - "fmt" - "slices" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" -) - -func createDiagnosticForInvalidEnumType(opt *CommandLineOption, sourceFile *ast.SourceFile, node *ast.Node) *ast.Diagnostic { - namesOfType := slices.Collect(opt.EnumMap().Keys()) - stringNames := formatEnumTypeKeys(opt, namesOfType) - optName := "--" + opt.Name - return CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics.Argument_for_0_option_must_be_Colon_1, optName, stringNames) -} - -func formatEnumTypeKeys(opt *CommandLineOption, keys []string) string { - if opt.DeprecatedKeys() != nil { - keys = core.Filter(keys, func(key string) bool { return !opt.DeprecatedKeys().Has(key) }) - } - return "'" + strings.Join(keys, "', '") + "'" -} - -func getCompilerOptionValueTypeString(option *CommandLineOption) string { - switch option.Kind { - case CommandLineOptionTypeListOrElement: - return fmt.Sprintf("%v or Array", getCompilerOptionValueTypeString(option.Elements())) - case CommandLineOptionTypeList: - return "Array" - default: - return string(option.Kind) - } -} - -func (parser *commandLineParser) createUnknownOptionError( - unknownOption string, - unknownOptionErrorText string, - node *ast.Node, - sourceFile *ast.SourceFile, -) *ast.Diagnostic { - return createUnknownOptionError( - unknownOption, - parser.UnknownOptionDiagnostic(), - unknownOptionErrorText, - node, - sourceFile, - parser.AlternateMode(), - ) -} - -func createUnknownOptionError( - unknownOption string, - unknownOptionDiagnostic *diagnostics.Message, - unknownOptionErrorText string, // optional - node *ast.Node, // optional - sourceFile *ast.SourceFile, // optional - alternateMode *AlternateModeDiagnostics, // optional -) *ast.Diagnostic { - if alternateMode != nil && alternateMode.optionsNameMap != nil { - otherOption := alternateMode.optionsNameMap.Get(strings.ToLower(unknownOption)) - if otherOption != nil { - // tscbuildoption - diagnostic := alternateMode.diagnostic - if otherOption.Name == "build" { - diagnostic = diagnostics.Option_build_must_be_the_first_command_line_argument - } - return CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostic, unknownOption) - } - } - if unknownOptionErrorText == "" { - unknownOptionErrorText = unknownOption - } - // TODO: possibleOption := spelling suggestion - return CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, unknownOptionDiagnostic, unknownOptionErrorText) -} - -func CreateDiagnosticForNodeInSourceFile(sourceFile *ast.SourceFile, node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { - return ast.NewDiagnostic(sourceFile, core.NewTextRange(scanner.SkipTrivia(sourceFile.Text(), node.Loc.Pos()), node.End()), message, args...) -} - -func CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile *ast.SourceFile, node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { - if sourceFile != nil && node != nil { - return CreateDiagnosticForNodeInSourceFile(sourceFile, node, message, args...) - } - return ast.NewCompilerDiagnostic(message, args...) -} - -func extraKeyDiagnostics(s string) *diagnostics.Message { - switch s { - case "compilerOptions": - return diagnostics.Unknown_compiler_option_0 - case "watchOptions": - return diagnostics.Unknown_watch_option_0 - case "typeAcquisition": - return diagnostics.Unknown_type_acquisition_option_0 - case "buildOptions": - return diagnostics.Unknown_build_option_0 - default: - return nil - } -} diff --git a/kitcom/internal/tsgo/tsoptions/export_test.go b/kitcom/internal/tsgo/tsoptions/export_test.go deleted file mode 100644 index f147284..0000000 --- a/kitcom/internal/tsgo/tsoptions/export_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package tsoptions - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" -) - -func getTestParseCommandLineWorkerDiagnostics(decls []*CommandLineOption) *ParseCommandLineWorkerDiagnostics { - if len(decls) == 0 { - return CompilerOptionsDidYouMeanDiagnostics - } - return getParseCommandLineWorkerDiagnostics(decls) -} - -func ParseCommandLineTestWorker( - decls []*CommandLineOption, - commandLine []string, - fs vfs.FS, -) *TestCommandLineParser { - parser := &commandLineParser{ - fs: fs, - workerDiagnostics: CompilerOptionsDidYouMeanDiagnostics, - fileNames: []string{}, - options: &collections.OrderedMap[string, any]{}, - errors: []*ast.Diagnostic{}, - } - if len(decls) != 0 { - parser.workerDiagnostics = getTestParseCommandLineWorkerDiagnostics(decls) - } - - parser.optionsMap = GetNameMapFromList(parser.OptionsDeclarations()) - parser.parseStrings(commandLine) - return &TestCommandLineParser{ - Fs: fs, - WorkerDiagnostics: parser.workerDiagnostics, - FileNames: parser.fileNames, - Options: parser.options, - Errors: parser.errors, - } -} - -type TestCommandLineParser struct { - Fs vfs.FS - WorkerDiagnostics *ParseCommandLineWorkerDiagnostics - FileNames []string - Options *collections.OrderedMap[string, any] - Errors []*ast.Diagnostic -} diff --git a/kitcom/internal/tsgo/tsoptions/namemap.go b/kitcom/internal/tsgo/tsoptions/namemap.go deleted file mode 100644 index 45040e3..0000000 --- a/kitcom/internal/tsgo/tsoptions/namemap.go +++ /dev/null @@ -1,58 +0,0 @@ -package tsoptions - -import ( - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" -) - -var ( - CompilerNameMap = GetNameMapFromList(OptionsDeclarations) - BuildNameMap = GetNameMapFromList(BuildOpts) - WatchNameMap = GetNameMapFromList(optionsForWatch) -) - -func GetNameMapFromList(optDecls []*CommandLineOption) *NameMap { - optionsNames := collections.NewOrderedMapWithSizeHint[string, *CommandLineOption](len(optDecls)) - shortOptionNames := map[string]string{} - for _, option := range optDecls { - optionsNames.Set(strings.ToLower(option.Name), option) - if option.ShortName != "" { - shortOptionNames[option.ShortName] = option.Name - } - } - return &NameMap{ - optionsNames: optionsNames, - shortOptionNames: shortOptionNames, - } -} - -type NameMap struct { - optionsNames *collections.OrderedMap[string, *CommandLineOption] - shortOptionNames map[string]string -} - -func (nm *NameMap) Get(name string) *CommandLineOption { - return nm.optionsNames.GetOrZero(strings.ToLower(name)) -} - -func (nm *NameMap) GetFromShort(shortName string) *CommandLineOption { - // returns option only if shortName is a valid short option - name, ok := nm.shortOptionNames[shortName] - if !ok { - return nil - } - return nm.Get(name) -} - -func (nm *NameMap) GetOptionDeclarationFromName(optionName string, allowShort bool) *CommandLineOption { - optionName = strings.ToLower(optionName) - // Try to translate short option names to their full equivalents. - if allowShort { - short := nm.shortOptionNames[optionName] - if short != "" { - optionName = short - } - } - return nm.Get(optionName) -} diff --git a/kitcom/internal/tsgo/tsoptions/parsedbuildcommandline.go b/kitcom/internal/tsgo/tsoptions/parsedbuildcommandline.go deleted file mode 100644 index e7e8de8..0000000 --- a/kitcom/internal/tsgo/tsoptions/parsedbuildcommandline.go +++ /dev/null @@ -1,33 +0,0 @@ -package tsoptions - -import ( - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type ParsedBuildCommandLine struct { - BuildOptions *core.BuildOptions `json:"buildOptions"` - CompilerOptions *core.CompilerOptions `json:"compilerOptions"` - WatchOptions *core.WatchOptions `json:"watchOptions"` - Projects []string `json:"projects"` - Errors []*ast.Diagnostic `json:"errors"` - - comparePathsOptions tspath.ComparePathsOptions - - resolvedProjectPaths []string - resolvedProjectPathsOnce sync.Once -} - -func (p *ParsedBuildCommandLine) ResolvedProjectPaths() []string { - p.resolvedProjectPathsOnce.Do(func() { - p.resolvedProjectPaths = core.Map(p.Projects, func(project string) string { - return core.ResolveConfigFileNameOfProjectReference( - tspath.ResolvePath(p.comparePathsOptions.CurrentDirectory, project), - ) - }) - }) - return p.resolvedProjectPaths -} diff --git a/kitcom/internal/tsgo/tsoptions/parsedcommandline.go b/kitcom/internal/tsgo/tsoptions/parsedcommandline.go deleted file mode 100644 index 3be8561..0000000 --- a/kitcom/internal/tsgo/tsoptions/parsedcommandline.go +++ /dev/null @@ -1,381 +0,0 @@ -package tsoptions - -import ( - "fmt" - "iter" - "slices" - "strings" - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/glob" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/outputpaths" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" -) - -const ( - fileGlobPattern = "*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,json}" - recursiveFileGlobPattern = "**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,json}" -) - -type ParsedCommandLine struct { - ParsedConfig *core.ParsedOptions `json:"parsedConfig"` - - ConfigFile *TsConfigSourceFile `json:"configFile"` // TsConfigSourceFile, used in Program and ExecuteCommandLine - Errors []*ast.Diagnostic `json:"errors"` - Raw any `json:"raw"` - CompileOnSave *bool `json:"compileOnSave"` - - comparePathsOptions tspath.ComparePathsOptions - wildcardDirectoriesOnce sync.Once - wildcardDirectories map[string]bool - includeGlobsOnce sync.Once - includeGlobs []*glob.Glob - extraFileExtensions []FileExtensionInfo - - sourceAndOutputMapsOnce sync.Once - sourceToProjectReference map[tspath.Path]*SourceOutputAndProjectReference - outputDtsToProjectReference map[tspath.Path]*SourceOutputAndProjectReference - - commonSourceDirectory string - commonSourceDirectoryOnce sync.Once - - resolvedProjectReferencePaths []string - resolvedProjectReferencePathsOnce sync.Once - - literalFileNamesLen int - fileNamesByPath map[tspath.Path]string // maps file names to their paths, used for quick lookups - fileNamesByPathOnce sync.Once -} - -func NewParsedCommandLine( - compilerOptions *core.CompilerOptions, - rootFileNames []string, - comparePathsOptions tspath.ComparePathsOptions, -) *ParsedCommandLine { - return &ParsedCommandLine{ - ParsedConfig: &core.ParsedOptions{ - CompilerOptions: compilerOptions, - FileNames: rootFileNames, - }, - comparePathsOptions: comparePathsOptions, - } -} - -type SourceOutputAndProjectReference struct { - Source string - OutputDts string - Resolved *ParsedCommandLine -} - -var ( - _ module.ResolvedProjectReference = (*ParsedCommandLine)(nil) - _ outputpaths.OutputPathsHost = (*ParsedCommandLine)(nil) -) - -func (p *ParsedCommandLine) ConfigName() string { - if p == nil { - return "" - } - return p.ConfigFile.SourceFile.FileName() -} - -func (p *ParsedCommandLine) SourceToProjectReference() map[tspath.Path]*SourceOutputAndProjectReference { - return p.sourceToProjectReference -} - -func (p *ParsedCommandLine) OutputDtsToProjectReference() map[tspath.Path]*SourceOutputAndProjectReference { - return p.outputDtsToProjectReference -} - -func (p *ParsedCommandLine) ParseInputOutputNames() { - p.sourceAndOutputMapsOnce.Do(func() { - sourceToOutput := map[tspath.Path]*SourceOutputAndProjectReference{} - outputDtsToSource := map[tspath.Path]*SourceOutputAndProjectReference{} - - for outputDts, source := range p.GetOutputDeclarationAndSourceFileNames() { - path := tspath.ToPath(source, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames()) - projectReference := &SourceOutputAndProjectReference{ - Source: source, - OutputDts: outputDts, - Resolved: p, - } - if outputDts != "" { - outputDtsToSource[tspath.ToPath(outputDts, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())] = projectReference - } - sourceToOutput[path] = projectReference - } - p.outputDtsToProjectReference = outputDtsToSource - p.sourceToProjectReference = sourceToOutput - }) -} - -func (p *ParsedCommandLine) CommonSourceDirectory() string { - p.commonSourceDirectoryOnce.Do(func() { - p.commonSourceDirectory = outputpaths.GetCommonSourceDirectory( - p.ParsedConfig.CompilerOptions, - func() []string { - return core.Filter( - p.ParsedConfig.FileNames, - func(file string) bool { - return !(p.ParsedConfig.CompilerOptions.NoEmitForJsFiles.IsTrue() && tspath.HasJSFileExtension(file)) && - !tspath.IsDeclarationFileName(file) - }) - }, - p.GetCurrentDirectory(), - p.UseCaseSensitiveFileNames(), - ) - }) - return p.commonSourceDirectory -} - -func (p *ParsedCommandLine) GetCurrentDirectory() string { - return p.comparePathsOptions.CurrentDirectory -} - -func (p *ParsedCommandLine) UseCaseSensitiveFileNames() bool { - return p.comparePathsOptions.UseCaseSensitiveFileNames -} - -func (p *ParsedCommandLine) GetOutputDeclarationAndSourceFileNames() iter.Seq2[string, string] { - return func(yield func(dtsName string, inputName string) bool) { - for _, fileName := range p.ParsedConfig.FileNames { - var outputDts string - if !tspath.IsDeclarationFileName(fileName) && !tspath.FileExtensionIs(fileName, tspath.ExtensionJson) { - outputDts = outputpaths.GetOutputDeclarationFileNameWorker(fileName, p.CompilerOptions(), p) - } - if !yield(outputDts, fileName) { - return - } - } - } -} - -func (p *ParsedCommandLine) GetOutputFileNames() iter.Seq[string] { - return func(yield func(outputName string) bool) { - for _, fileName := range p.ParsedConfig.FileNames { - if tspath.IsDeclarationFileName(fileName) { - continue - } - jsFileName := outputpaths.GetOutputJSFileName(fileName, p.CompilerOptions(), p) - isJson := tspath.FileExtensionIs(fileName, tspath.ExtensionJson) - if jsFileName != "" { - if !yield(jsFileName) { - return - } - if !isJson { - sourceMap := outputpaths.GetSourceMapFilePath(jsFileName, p.CompilerOptions()) - if sourceMap != "" { - if !yield(sourceMap) { - return - } - } - } - } - if isJson { - continue - } - if p.CompilerOptions().GetEmitDeclarations() { - dtsFileName := outputpaths.GetOutputDeclarationFileNameWorker(fileName, p.CompilerOptions(), p) - if dtsFileName != "" { - if !yield(dtsFileName) { - return - } - if p.CompilerOptions().GetAreDeclarationMapsEnabled() { - declarationMap := dtsFileName + ".map" - if !yield(declarationMap) { - return - } - } - } - } - } - } -} - -func (p *ParsedCommandLine) GetBuildInfoFileName() string { - return outputpaths.GetBuildInfoFileName(p.CompilerOptions(), p.comparePathsOptions) -} - -// WildcardDirectories returns the cached wildcard directories, initializing them if needed -func (p *ParsedCommandLine) WildcardDirectories() map[string]bool { - if p == nil { - return nil - } - - p.wildcardDirectoriesOnce.Do(func() { - if p.wildcardDirectories == nil { - p.wildcardDirectories = getWildcardDirectories( - p.ConfigFile.configFileSpecs.validatedIncludeSpecs, - p.ConfigFile.configFileSpecs.validatedExcludeSpecs, - p.comparePathsOptions, - ) - } - }) - - return p.wildcardDirectories -} - -func (p *ParsedCommandLine) WildcardDirectoryGlobs() []*glob.Glob { - wildcardDirectories := p.WildcardDirectories() - if wildcardDirectories == nil { - return nil - } - - p.includeGlobsOnce.Do(func() { - if p.includeGlobs == nil { - globs := make([]*glob.Glob, 0, len(wildcardDirectories)) - for dir, recursive := range wildcardDirectories { - if parsed, err := glob.Parse(fmt.Sprintf("%s/%s", tspath.NormalizePath(dir), core.IfElse(recursive, recursiveFileGlobPattern, fileGlobPattern))); err == nil { - globs = append(globs, parsed) - } - } - p.includeGlobs = globs - } - }) - - return p.includeGlobs -} - -// Normalized file names explicitly specified in `files` -func (p *ParsedCommandLine) LiteralFileNames() []string { - if p != nil && p.ConfigFile != nil { - return p.FileNames()[0:p.literalFileNamesLen] - } - return nil -} - -func (p *ParsedCommandLine) SetParsedOptions(o *core.ParsedOptions) { - p.ParsedConfig = o -} - -func (p *ParsedCommandLine) SetCompilerOptions(o *core.CompilerOptions) { - p.ParsedConfig.CompilerOptions = o -} - -func (p *ParsedCommandLine) CompilerOptions() *core.CompilerOptions { - if p == nil { - return nil - } - return p.ParsedConfig.CompilerOptions -} - -func (p *ParsedCommandLine) SetTypeAcquisition(o *core.TypeAcquisition) { - p.ParsedConfig.TypeAcquisition = o -} - -func (p *ParsedCommandLine) TypeAcquisition() *core.TypeAcquisition { - return p.ParsedConfig.TypeAcquisition -} - -// All file names matched by files, include, and exclude patterns -func (p *ParsedCommandLine) FileNames() []string { - return p.ParsedConfig.FileNames -} - -func (p *ParsedCommandLine) FileNamesByPath() map[tspath.Path]string { - p.fileNamesByPathOnce.Do(func() { - p.fileNamesByPath = make(map[tspath.Path]string, len(p.ParsedConfig.FileNames)) - for _, fileName := range p.ParsedConfig.FileNames { - path := tspath.ToPath(fileName, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames()) - p.fileNamesByPath[path] = fileName - } - }) - return p.fileNamesByPath -} - -func (p *ParsedCommandLine) ProjectReferences() []*core.ProjectReference { - return p.ParsedConfig.ProjectReferences -} - -func (p *ParsedCommandLine) ResolvedProjectReferencePaths() []string { - p.resolvedProjectReferencePathsOnce.Do(func() { - p.resolvedProjectReferencePaths = core.Map(p.ParsedConfig.ProjectReferences, core.ResolveProjectReferencePath) - }) - return p.resolvedProjectReferencePaths -} - -func (p *ParsedCommandLine) ExtendedSourceFiles() []string { - if p == nil || p.ConfigFile == nil { - return nil - } - return p.ConfigFile.ExtendedSourceFiles -} - -func (p *ParsedCommandLine) GetConfigFileParsingDiagnostics() []*ast.Diagnostic { - if p.ConfigFile != nil { - // todo: !!! should be ConfigFile.ParseDiagnostics, check if they are the same - return slices.Concat(p.ConfigFile.SourceFile.Diagnostics(), p.Errors) - } - return p.Errors -} - -// PossiblyMatchesFileName is a fast check to see if a file is currently included by a config -// or would be included if the file were to be created. It may return false positives. -func (p *ParsedCommandLine) PossiblyMatchesFileName(fileName string) bool { - path := tspath.ToPath(fileName, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames()) - if _, ok := p.FileNamesByPath()[path]; ok { - return true - } - - for _, include := range p.ConfigFile.configFileSpecs.validatedIncludeSpecs { - if !strings.ContainsAny(include, "*?") && !vfs.IsImplicitGlob(include) { - includePath := tspath.ToPath(include, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames()) - if includePath == path { - return true - } - } - } - if wildcardDirectoryGlobs := p.WildcardDirectoryGlobs(); len(wildcardDirectoryGlobs) > 0 { - for _, glob := range wildcardDirectoryGlobs { - if glob.Match(fileName) { - return true - } - } - } - return false -} - -func (p *ParsedCommandLine) GetMatchedFileSpec(fileName string) string { - return p.ConfigFile.configFileSpecs.getMatchedFileSpec(fileName, p.comparePathsOptions) -} - -func (p *ParsedCommandLine) GetMatchedIncludeSpec(fileName string) (string, bool) { - if len(p.ConfigFile.configFileSpecs.validatedIncludeSpecs) == 0 { - return "", false - } - - if p.ConfigFile.configFileSpecs.isDefaultIncludeSpec { - return p.ConfigFile.configFileSpecs.validatedIncludeSpecs[0], true - } - - return p.ConfigFile.configFileSpecs.getMatchedIncludeSpec(fileName, p.comparePathsOptions), false -} - -func (p *ParsedCommandLine) ReloadFileNamesOfParsedCommandLine(fs vfs.FS) *ParsedCommandLine { - parsedConfig := *p.ParsedConfig - fileNames, literalFileNamesLen := getFileNamesFromConfigSpecs( - *p.ConfigFile.configFileSpecs, - p.GetCurrentDirectory(), - p.CompilerOptions(), - fs, - p.extraFileExtensions, - ) - parsedConfig.FileNames = fileNames - parsedCommandLine := ParsedCommandLine{ - ParsedConfig: &parsedConfig, - ConfigFile: p.ConfigFile, - Errors: p.Errors, - Raw: p.Raw, - CompileOnSave: p.CompileOnSave, - comparePathsOptions: p.comparePathsOptions, - wildcardDirectories: p.wildcardDirectories, - includeGlobs: p.includeGlobs, - extraFileExtensions: p.extraFileExtensions, - literalFileNamesLen: literalFileNamesLen, - } - return &parsedCommandLine -} diff --git a/kitcom/internal/tsgo/tsoptions/parsedcommandline_test.go b/kitcom/internal/tsgo/tsoptions/parsedcommandline_test.go deleted file mode 100644 index b729f45..0000000 --- a/kitcom/internal/tsgo/tsoptions/parsedcommandline_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package tsoptions_test - -import ( - "slices" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions/tsoptionstest" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/vfstest" - "gotest.tools/v3/assert" -) - -func TestParsedCommandLine(t *testing.T) { - t.Parallel() - t.Run("PossiblyMatchesFileName", func(t *testing.T) { - t.Parallel() - - noFiles := map[string]string{} - noFilesFS := vfstest.FromMap(noFiles, true) - - files := map[string]string{ - "/dev/a.ts": "", - "/dev/a.d.ts": "", - "/dev/a.js": "", - "/dev/b.ts": "", - "/dev/b.js": "", - "/dev/c.d.ts": "", - "/dev/z/a.ts": "", - "/dev/z/abz.ts": "", - "/dev/z/aba.ts": "", - "/dev/z/b.ts": "", - "/dev/z/bbz.ts": "", - "/dev/z/bba.ts": "", - "/dev/x/a.ts": "", - "/dev/x/aa.ts": "", - "/dev/x/b.ts": "", - "/dev/x/y/a.ts": "", - "/dev/x/y/b.ts": "", - "/dev/js/a.js": "", - "/dev/js/b.js": "", - "/dev/js/d.min.js": "", - "/dev/js/ab.min.js": "", - "/ext/ext.ts": "", - "/ext/b/a..b.ts": "", - } - - assertMatches := func(t *testing.T, parsedCommandLine *tsoptions.ParsedCommandLine, files map[string]string, matches []string) { - t.Helper() - for fileName := range files { - actual := parsedCommandLine.PossiblyMatchesFileName(fileName) - expected := slices.Contains(matches, fileName) - assert.Equal(t, actual, expected, "fileName: %s", fileName) - } - for _, fileName := range matches { - if _, ok := files[fileName]; !ok { - actual := parsedCommandLine.PossiblyMatchesFileName(fileName) - assert.Equal(t, actual, true, "fileName: %s", fileName) - } - } - } - - t.Run("with literal file list", func(t *testing.T) { - t.Parallel() - t.Run("without exclude", func(t *testing.T) { - t.Parallel() - parsedCommandLine := tsoptionstest.GetParsedCommandLine( - t, - `{ - "files": [ - "a.ts", - "b.ts" - ] - }`, - files, - "/dev", - /*useCaseSensitiveFileNames*/ true, - ) - - assertMatches(t, parsedCommandLine, files, []string{ - "/dev/a.ts", - "/dev/b.ts", - }) - }) - - t.Run("are not removed due to excludes", func(t *testing.T) { - t.Parallel() - parsedCommandLine := tsoptionstest.GetParsedCommandLine( - t, - `{ - "files": [ - "a.ts", - "b.ts" - ], - "exclude": [ - "b.ts" - ] - }`, - files, - "/dev", - /*useCaseSensitiveFileNames*/ true, - ) - - assertMatches(t, parsedCommandLine, files, []string{ - "/dev/a.ts", - "/dev/b.ts", - }) - - emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) - assertMatches(t, emptyParsedCommandLine, noFiles, []string{ - "/dev/a.ts", - "/dev/b.ts", - }) - }) - - t.Run("duplicates", func(t *testing.T) { - t.Parallel() - parsedCommandLine := tsoptionstest.GetParsedCommandLine( - t, - `{ - "files": [ - "a.ts", - "a.ts", - "b.ts", - ] - }`, - files, - "/dev", - /*useCaseSensitiveFileNames*/ true, - ) - - assert.DeepEqual(t, parsedCommandLine.LiteralFileNames(), []string{ - "/dev/a.ts", - "/dev/b.ts", - }) - }) - }) - - t.Run("with literal include list", func(t *testing.T) { - t.Parallel() - t.Run("without exclude", func(t *testing.T) { - t.Parallel() - parsedCommandLine := tsoptionstest.GetParsedCommandLine( - t, - `{ - "include": [ - "a.ts", - "b.ts" - ] - }`, - files, - "/dev", - /*useCaseSensitiveFileNames*/ true, - ) - - assertMatches(t, parsedCommandLine, files, []string{ - "/dev/a.ts", - "/dev/b.ts", - }) - - emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) - assertMatches(t, emptyParsedCommandLine, noFiles, []string{ - "/dev/a.ts", - "/dev/b.ts", - }) - }) - }) - }) -} diff --git a/kitcom/internal/tsgo/tsoptions/parsinghelpers.go b/kitcom/internal/tsgo/tsoptions/parsinghelpers.go deleted file mode 100644 index a8065a6..0000000 --- a/kitcom/internal/tsgo/tsoptions/parsinghelpers.go +++ /dev/null @@ -1,620 +0,0 @@ -package tsoptions - -import ( - "reflect" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -func parseTristate(value any) core.Tristate { - if value == nil { - return core.TSUnknown - } - if v, ok := value.(core.Tristate); ok { - return v - } - if value == true { - return core.TSTrue - } else { - return core.TSFalse - } -} - -func parseStringArray(value any) []string { - if arr, ok := value.([]any); ok { - if arr == nil { - return nil - } - result := make([]string, 0, len(arr)) - for _, v := range arr { - if str, ok := v.(string); ok { - result = append(result, str) - } - } - return result - } - return nil -} - -func parseStringMap(value any) *collections.OrderedMap[string, []string] { - if m, ok := value.(*collections.OrderedMap[string, any]); ok { - result := collections.NewOrderedMapWithSizeHint[string, []string](m.Size()) - for k, v := range m.Entries() { - result.Set(k, parseStringArray(v)) - } - return result - } - return nil -} - -func parseString(value any) string { - if str, ok := value.(string); ok { - return str - } - return "" -} - -func parseNumber(value any) *int { - if num, ok := value.(int); ok { - return &num - } - return nil -} - -func parseProjectReference(json any) []*core.ProjectReference { - var result []*core.ProjectReference - if v, ok := json.(*collections.OrderedMap[string, any]); ok { - var reference core.ProjectReference - if v, ok := v.Get("path"); ok { - reference.Path = v.(string) - } - if v, ok := v.Get("circular"); ok { - reference.Circular = v.(bool) - } - result = append(result, &reference) - } - return result -} - -func parseJsonToStringKey(json any) *collections.OrderedMap[string, any] { - result := collections.NewOrderedMapWithSizeHint[string, any](6) - if m, ok := json.(*collections.OrderedMap[string, any]); ok { - if v, ok := m.Get("include"); ok { - result.Set("include", v) - } - if v, ok := m.Get("exclude"); ok { - result.Set("exclude", v) - } - if v, ok := m.Get("files"); ok { - result.Set("files", v) - } - if v, ok := m.Get("references"); ok { - result.Set("references", v) - } - if v, ok := m.Get("extends"); ok { - if str, ok := v.(string); ok { - result.Set("extends", []any{str}) - } - result.Set("extends", v) - } - if v, ok := m.Get("compilerOptions"); ok { - result.Set("compilerOptions", v) - } - if v, ok := m.Get("excludes"); ok { - result.Set("excludes", v) - } - if v, ok := m.Get("typeAcquisition"); ok { - result.Set("typeAcquisition", v) - } - } - return result -} - -type optionParser interface { - ParseOption(key string, value any) []*ast.Diagnostic - UnknownOptionDiagnostic() *diagnostics.Message -} - -type compilerOptionsParser struct { - *core.CompilerOptions -} - -func (o *compilerOptionsParser) ParseOption(key string, value any) []*ast.Diagnostic { - return ParseCompilerOptions(key, value, o.CompilerOptions) -} - -func (o *compilerOptionsParser) UnknownOptionDiagnostic() *diagnostics.Message { - return extraKeyDiagnostics("compilerOptions") -} - -type watchOptionsParser struct { - *core.WatchOptions -} - -func (o *watchOptionsParser) ParseOption(key string, value any) []*ast.Diagnostic { - return ParseWatchOptions(key, value, o.WatchOptions) -} - -func (o *watchOptionsParser) UnknownOptionDiagnostic() *diagnostics.Message { - return extraKeyDiagnostics("watchOptions") -} - -type typeAcquisitionParser struct { - *core.TypeAcquisition -} - -func (o *typeAcquisitionParser) ParseOption(key string, value any) []*ast.Diagnostic { - return ParseTypeAcquisition(key, value, o.TypeAcquisition) -} - -func (o *typeAcquisitionParser) UnknownOptionDiagnostic() *diagnostics.Message { - return extraKeyDiagnostics("typeAcquisition") -} - -type buildOptionsParser struct { - *core.BuildOptions -} - -func (o *buildOptionsParser) ParseOption(key string, value any) []*ast.Diagnostic { - return ParseBuildOptions(key, value, o.BuildOptions) -} - -func (o *buildOptionsParser) UnknownOptionDiagnostic() *diagnostics.Message { - return extraKeyDiagnostics("buildOptions") -} - -func ParseCompilerOptions(key string, value any, allOptions *core.CompilerOptions) []*ast.Diagnostic { - if value == nil { - return nil - } - if allOptions == nil { - return nil - } - parseCompilerOptions(key, value, allOptions) - return nil -} - -func parseCompilerOptions(key string, value any, allOptions *core.CompilerOptions) (foundKey bool) { - option := CommandLineCompilerOptionsMap.Get(key) - if option != nil { - key = option.Name - } - switch key { - case "allowJs": - allOptions.AllowJs = parseTristate(value) - case "allowImportingTsExtensions": - allOptions.AllowImportingTsExtensions = parseTristate(value) - case "allowSyntheticDefaultImports": - allOptions.AllowSyntheticDefaultImports = parseTristate(value) - case "allowNonTsExtensions": - allOptions.AllowNonTsExtensions = parseTristate(value) - case "allowUmdGlobalAccess": - allOptions.AllowUmdGlobalAccess = parseTristate(value) - case "allowUnreachableCode": - allOptions.AllowUnreachableCode = parseTristate(value) - case "allowUnusedLabels": - allOptions.AllowUnusedLabels = parseTristate(value) - case "allowArbitraryExtensions": - allOptions.AllowArbitraryExtensions = parseTristate(value) - case "alwaysStrict": - allOptions.AlwaysStrict = parseTristate(value) - case "assumeChangesOnlyAffectDirectDependencies": - allOptions.AssumeChangesOnlyAffectDirectDependencies = parseTristate(value) - case "baseUrl": - allOptions.BaseUrl = parseString(value) - case "build": - allOptions.Build = parseTristate(value) - case "checkJs": - allOptions.CheckJs = parseTristate(value) - case "customConditions": - allOptions.CustomConditions = parseStringArray(value) - case "composite": - allOptions.Composite = parseTristate(value) - case "declarationDir": - allOptions.DeclarationDir = parseString(value) - case "diagnostics": - allOptions.Diagnostics = parseTristate(value) - case "disableSizeLimit": - allOptions.DisableSizeLimit = parseTristate(value) - case "disableSourceOfProjectReferenceRedirect": - allOptions.DisableSourceOfProjectReferenceRedirect = parseTristate(value) - case "disableSolutionSearching": - allOptions.DisableSolutionSearching = parseTristate(value) - case "disableReferencedProjectLoad": - allOptions.DisableReferencedProjectLoad = parseTristate(value) - case "declarationMap": - allOptions.DeclarationMap = parseTristate(value) - case "declaration": - allOptions.Declaration = parseTristate(value) - case "downlevelIteration": - allOptions.DownlevelIteration = parseTristate(value) - case "erasableSyntaxOnly": - allOptions.ErasableSyntaxOnly = parseTristate(value) - case "emitDeclarationOnly": - allOptions.EmitDeclarationOnly = parseTristate(value) - case "extendedDiagnostics": - allOptions.ExtendedDiagnostics = parseTristate(value) - case "emitDecoratorMetadata": - allOptions.EmitDecoratorMetadata = parseTristate(value) - case "emitBOM": - allOptions.EmitBOM = parseTristate(value) - case "esModuleInterop": - allOptions.ESModuleInterop = parseTristate(value) - case "exactOptionalPropertyTypes": - allOptions.ExactOptionalPropertyTypes = parseTristate(value) - case "explainFiles": - allOptions.ExplainFiles = parseTristate(value) - case "experimentalDecorators": - allOptions.ExperimentalDecorators = parseTristate(value) - case "forceConsistentCasingInFileNames": - allOptions.ForceConsistentCasingInFileNames = parseTristate(value) - case "generateCpuProfile": - allOptions.GenerateCpuProfile = parseString(value) - case "generateTrace": - allOptions.GenerateTrace = parseString(value) - case "isolatedModules": - allOptions.IsolatedModules = parseTristate(value) - case "ignoreDeprecations": - allOptions.IgnoreDeprecations = parseString(value) - case "importHelpers": - allOptions.ImportHelpers = parseTristate(value) - case "incremental": - allOptions.Incremental = parseTristate(value) - case "init": - allOptions.Init = parseTristate(value) - case "inlineSourceMap": - allOptions.InlineSourceMap = parseTristate(value) - case "inlineSources": - allOptions.InlineSources = parseTristate(value) - case "isolatedDeclarations": - allOptions.IsolatedDeclarations = parseTristate(value) - case "jsx": - allOptions.Jsx = floatOrInt32ToFlag[core.JsxEmit](value) - case "jsxFactory": - allOptions.JsxFactory = parseString(value) - case "jsxFragmentFactory": - allOptions.JsxFragmentFactory = parseString(value) - case "jsxImportSource": - allOptions.JsxImportSource = parseString(value) - case "lib": - if _, ok := value.([]string); ok { - allOptions.Lib = value.([]string) - } else { - allOptions.Lib = parseStringArray(value) - } - case "libReplacement": - allOptions.LibReplacement = parseTristate(value) - case "listEmittedFiles": - allOptions.ListEmittedFiles = parseTristate(value) - case "listFiles": - allOptions.ListFiles = parseTristate(value) - case "listFilesOnly": - allOptions.ListFilesOnly = parseTristate(value) - case "locale": - allOptions.Locale = parseString(value) - case "mapRoot": - allOptions.MapRoot = parseString(value) - case "module": - allOptions.Module = floatOrInt32ToFlag[core.ModuleKind](value) - case "moduleDetectionKind": - allOptions.ModuleDetection = floatOrInt32ToFlag[core.ModuleDetectionKind](value) - case "moduleResolution": - allOptions.ModuleResolution = floatOrInt32ToFlag[core.ModuleResolutionKind](value) - case "moduleSuffixes": - allOptions.ModuleSuffixes = parseStringArray(value) - case "moduleDetection": - allOptions.ModuleDetection = floatOrInt32ToFlag[core.ModuleDetectionKind](value) - case "noCheck": - allOptions.NoCheck = parseTristate(value) - case "noFallthroughCasesInSwitch": - allOptions.NoFallthroughCasesInSwitch = parseTristate(value) - case "noEmitForJsFiles": - allOptions.NoEmitForJsFiles = parseTristate(value) - case "noErrorTruncation": - allOptions.NoErrorTruncation = parseTristate(value) - case "noImplicitAny": - allOptions.NoImplicitAny = parseTristate(value) - case "noImplicitThis": - allOptions.NoImplicitThis = parseTristate(value) - case "noLib": - allOptions.NoLib = parseTristate(value) - case "noPropertyAccessFromIndexSignature": - allOptions.NoPropertyAccessFromIndexSignature = parseTristate(value) - case "noUncheckedIndexedAccess": - allOptions.NoUncheckedIndexedAccess = parseTristate(value) - case "noEmitHelpers": - allOptions.NoEmitHelpers = parseTristate(value) - case "noEmitOnError": - allOptions.NoEmitOnError = parseTristate(value) - case "noImplicitReturns": - allOptions.NoImplicitReturns = parseTristate(value) - case "noUnusedLocals": - allOptions.NoUnusedLocals = parseTristate(value) - case "noUnusedParameters": - allOptions.NoUnusedParameters = parseTristate(value) - case "noImplicitOverride": - allOptions.NoImplicitOverride = parseTristate(value) - case "noUncheckedSideEffectImports": - allOptions.NoUncheckedSideEffectImports = parseTristate(value) - case "outFile": - allOptions.OutFile = parseString(value) - case "noResolve": - allOptions.NoResolve = parseTristate(value) - case "paths": - allOptions.Paths = parseStringMap(value) - case "preserveWatchOutput": - allOptions.PreserveWatchOutput = parseTristate(value) - case "preserveConstEnums": - allOptions.PreserveConstEnums = parseTristate(value) - case "preserveSymlinks": - allOptions.PreserveSymlinks = parseTristate(value) - case "project": - allOptions.Project = parseString(value) - case "pretty": - allOptions.Pretty = parseTristate(value) - case "resolveJsonModule": - allOptions.ResolveJsonModule = parseTristate(value) - case "resolvePackageJsonExports": - allOptions.ResolvePackageJsonExports = parseTristate(value) - case "resolvePackageJsonImports": - allOptions.ResolvePackageJsonImports = parseTristate(value) - case "reactNamespace": - allOptions.ReactNamespace = parseString(value) - case "rewriteRelativeImportExtensions": - allOptions.RewriteRelativeImportExtensions = parseTristate(value) - case "rootDir": - allOptions.RootDir = parseString(value) - case "rootDirs": - allOptions.RootDirs = parseStringArray(value) - case "removeComments": - allOptions.RemoveComments = parseTristate(value) - case "strict": - allOptions.Strict = parseTristate(value) - case "strictBindCallApply": - allOptions.StrictBindCallApply = parseTristate(value) - case "strictBuiltinIteratorReturn": - allOptions.StrictBuiltinIteratorReturn = parseTristate(value) - case "strictFunctionTypes": - allOptions.StrictFunctionTypes = parseTristate(value) - case "strictNullChecks": - allOptions.StrictNullChecks = parseTristate(value) - case "strictPropertyInitialization": - allOptions.StrictPropertyInitialization = parseTristate(value) - case "skipDefaultLibCheck": - allOptions.SkipDefaultLibCheck = parseTristate(value) - case "sourceMap": - allOptions.SourceMap = parseTristate(value) - case "sourceRoot": - allOptions.SourceRoot = parseString(value) - case "stripInternal": - allOptions.StripInternal = parseTristate(value) - case "suppressOutputPathCheck": - allOptions.SuppressOutputPathCheck = parseTristate(value) - case "target": - allOptions.Target = floatOrInt32ToFlag[core.ScriptTarget](value) - case "traceResolution": - allOptions.TraceResolution = parseTristate(value) - case "tsBuildInfoFile": - allOptions.TsBuildInfoFile = parseString(value) - case "typeRoots": - allOptions.TypeRoots = parseStringArray(value) - case "types": - allOptions.Types = parseStringArray(value) - case "useDefineForClassFields": - allOptions.UseDefineForClassFields = parseTristate(value) - case "useUnknownInCatchVariables": - allOptions.UseUnknownInCatchVariables = parseTristate(value) - case "verbatimModuleSyntax": - allOptions.VerbatimModuleSyntax = parseTristate(value) - case "version": - allOptions.Version = parseTristate(value) - case "help": - allOptions.Help = parseTristate(value) - case "all": - allOptions.All = parseTristate(value) - case "maxNodeModuleJsDepth": - allOptions.MaxNodeModuleJsDepth = parseNumber(value) - case "skipLibCheck": - allOptions.SkipLibCheck = parseTristate(value) - case "noEmit": - allOptions.NoEmit = parseTristate(value) - case "showConfig": - allOptions.ShowConfig = parseTristate(value) - case "configFilePath": - allOptions.ConfigFilePath = parseString(value) - case "noDtsResolution": - allOptions.NoDtsResolution = parseTristate(value) - case "pathsBasePath": - allOptions.PathsBasePath = parseString(value) - case "outDir": - allOptions.OutDir = parseString(value) - case "newLine": - allOptions.NewLine = floatOrInt32ToFlag[core.NewLineKind](value) - case "watch": - allOptions.Watch = parseTristate(value) - case "pprofDir": - allOptions.PprofDir = parseString(value) - case "singleThreaded": - allOptions.SingleThreaded = parseTristate(value) - case "quiet": - allOptions.Quiet = parseTristate(value) - default: - // different than any key above - return false - } - return true -} - -func floatOrInt32ToFlag[T ~int32](value any) T { - if v, ok := value.(T); ok { - return v - } - return T(value.(float64)) -} - -func ParseWatchOptions(key string, value any, allOptions *core.WatchOptions) []*ast.Diagnostic { - if allOptions == nil { - return nil - } - switch key { - case "watchInterval": - allOptions.Interval = parseNumber(value) - case "watchFile": - if value != nil { - allOptions.FileKind = value.(core.WatchFileKind) - } - case "watchDirectory": - if value != nil { - allOptions.DirectoryKind = value.(core.WatchDirectoryKind) - } - case "fallbackPolling": - if value != nil { - allOptions.FallbackPolling = value.(core.PollingKind) - } - case "synchronousWatchDirectory": - allOptions.SyncWatchDir = parseTristate(value) - case "excludeDirectories": - allOptions.ExcludeDir = parseStringArray(value) - case "excludeFiles": - allOptions.ExcludeFiles = parseStringArray(value) - } - return nil -} - -func ParseTypeAcquisition(key string, value any, allOptions *core.TypeAcquisition) []*ast.Diagnostic { - if value == nil { - return nil - } - if allOptions == nil { - return nil - } - switch key { - case "enable": - allOptions.Enable = parseTristate(value) - case "include": - allOptions.Include = parseStringArray(value) - case "exclude": - allOptions.Exclude = parseStringArray(value) - case "disableFilenameBasedTypeAcquisition": - allOptions.DisableFilenameBasedTypeAcquisition = parseTristate(value) - } - return nil -} - -func ParseBuildOptions(key string, value any, allOptions *core.BuildOptions) []*ast.Diagnostic { - if value == nil { - return nil - } - if allOptions == nil { - return nil - } - option := BuildNameMap.Get(key) - if option != nil { - key = option.Name - } - switch key { - case "clean": - allOptions.Clean = parseTristate(value) - case "dry": - allOptions.Dry = parseTristate(value) - case "force": - allOptions.Force = parseTristate(value) - case "stopBuildOnErrors": - allOptions.StopBuildOnErrors = parseTristate(value) - case "verbose": - allOptions.Verbose = parseTristate(value) - } - return nil -} - -// mergeCompilerOptions merges the source compiler options into the target compiler options -// with optional awareness of explicitly set null values in the raw JSON. -// Fields in the source options will overwrite the corresponding fields in the target options, -// including when they are explicitly set to null in the raw configuration (if rawSource is provided). -func mergeCompilerOptions(targetOptions, sourceOptions *core.CompilerOptions, rawSource any) *core.CompilerOptions { - if sourceOptions == nil { - return targetOptions - } - - // Collect explicitly null field names from raw JSON - var explicitNullFields collections.Set[string] - if rawSource != nil { - if rawMap, ok := rawSource.(*collections.OrderedMap[string, any]); ok { - if compilerOptionsRaw, exists := rawMap.Get("compilerOptions"); exists { - if compilerOptionsMap, ok := compilerOptionsRaw.(*collections.OrderedMap[string, any]); ok { - for key, value := range compilerOptionsMap.Entries() { - if value == nil { - explicitNullFields.Add(key) - } - } - } - } - } - } - - // Do the merge, handling explicit nulls during the normal merge - targetValue := reflect.ValueOf(targetOptions).Elem() - sourceValue := reflect.ValueOf(sourceOptions).Elem() - targetType := targetValue.Type() - - for i := range targetValue.NumField() { - targetField := targetValue.Field(i) - sourceField := sourceValue.Field(i) - - // Get the JSON field name for this struct field and check if it's explicitly null - if jsonTag := targetType.Field(i).Tag.Get("json"); jsonTag != "" { - if jsonFieldName, _, _ := strings.Cut(jsonTag, ","); jsonFieldName != "" && explicitNullFields.Has(jsonFieldName) { - targetField.SetZero() - continue - } - } - - // Normal merge behavior: copy non-zero fields - if !sourceField.IsZero() { - targetField.Set(sourceField) - } - } - - return targetOptions -} - -func convertToOptionsWithAbsolutePaths(optionsBase *collections.OrderedMap[string, any], optionMap CommandLineOptionNameMap, cwd string) *collections.OrderedMap[string, any] { - // !!! convert to options with absolute paths was previously done with `CompilerOptions` object, but for ease of implementation, we do it pre-conversion. - // !!! Revisit this choice if/when refactoring when conversion is done in tsconfig parsing - if optionsBase == nil { - return nil - } - for o, v := range optionsBase.Entries() { - result, ok := ConvertOptionToAbsolutePath(o, v, optionMap, cwd) - if ok { - optionsBase.Set(o, result) - } - } - return optionsBase -} - -func ConvertOptionToAbsolutePath(o string, v any, optionMap CommandLineOptionNameMap, cwd string) (any, bool) { - option := optionMap.Get(o) - if option == nil { - return nil, false - } - if option.Kind == "list" { - if option.Elements().IsFilePath { - if arr, ok := v.([]string); ok { - return core.Map(arr, func(item string) string { - return tspath.GetNormalizedAbsolutePath(item, cwd) - }), true - } - } - } else if option.IsFilePath { - if value, ok := v.(string); ok { - return tspath.GetNormalizedAbsolutePath(value, cwd), true - } - } - return nil, false -} diff --git a/kitcom/internal/tsgo/tsoptions/parsinghelpers_test.go b/kitcom/internal/tsgo/tsoptions/parsinghelpers_test.go deleted file mode 100644 index 9a7e063..0000000 --- a/kitcom/internal/tsgo/tsoptions/parsinghelpers_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package tsoptions - -import ( - "reflect" - "strings" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" -) - -func TestParseCompilerOptionNoMissingFields(t *testing.T) { - t.Parallel() - var missingKeys []string - for _, field := range reflect.VisibleFields(reflect.TypeFor[core.CompilerOptions]()) { - if !field.IsExported() { - continue - } - - keyName := field.Name - // use the JSON key from the tag, if present - // e.g. `json:"dog[,anythingelse]"` --> dog - if jsonTag, ok := field.Tag.Lookup("json"); ok { - keyName = strings.SplitN(jsonTag, ",", 2)[0] - } - val := reflect.Zero(field.Type).Interface() - co := core.CompilerOptions{} - found := parseCompilerOptions(keyName, val, &co) - if !found { - missingKeys = append(missingKeys, keyName) - } - } - if len(missingKeys) > 0 { - t.Errorf("The following keys are missing entries in the ParseCompilerOptions"+ - " switch statement:\n%v", missingKeys) - } -} diff --git a/kitcom/internal/tsgo/tsoptions/tsconfigparsing.go b/kitcom/internal/tsgo/tsoptions/tsconfigparsing.go deleted file mode 100644 index b43bf66..0000000 --- a/kitcom/internal/tsgo/tsoptions/tsconfigparsing.go +++ /dev/null @@ -1,1776 +0,0 @@ -package tsoptions - -import ( - "fmt" - "reflect" - "regexp" - "slices" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" - "github.com/dlclark/regexp2" -) - -type extendsResult struct { - options *core.CompilerOptions - // watchOptions compiler.WatchOptions - watchOptionsCopied bool - include []any - exclude []any - files []any - compileOnSave bool - extendedSourceFiles collections.Set[string] -} - -var compilerOptionsDeclaration = &CommandLineOption{ - Name: "compilerOptions", - Kind: CommandLineOptionTypeObject, - ElementOptions: CommandLineCompilerOptionsMap, -} - -var compileOnSaveCommandLineOption = &CommandLineOption{ - Name: "compileOnSave", - Kind: CommandLineOptionTypeBoolean, - DefaultValueDescription: false, -} - -var extendsOptionDeclaration = &CommandLineOption{ - Name: "extends", - Kind: CommandLineOptionTypeListOrElement, - Category: diagnostics.File_Management, - ElementOptions: commandLineOptionsToMap([]*CommandLineOption{ - {Name: "extends", Kind: CommandLineOptionTypeString}, - }), -} - -var tsconfigRootOptionsMap = &CommandLineOption{ - Name: "undefined", // should never be needed since this is root - Kind: CommandLineOptionTypeObject, - ElementOptions: commandLineOptionsToMap([]*CommandLineOption{ - compilerOptionsDeclaration, - // watchOptionsDeclaration, - typeAcquisitionDeclaration, - extendsOptionDeclaration, - { - Name: "references", - Kind: CommandLineOptionTypeList, // should be a list of projectReference - // Category: diagnostics.Projects, - }, - { - Name: "files", - Kind: CommandLineOptionTypeList, - // Category: diagnostics.File_Management, - }, - { - Name: "include", - Kind: CommandLineOptionTypeList, - // Category: diagnostics.File_Management, - // DefaultValueDescription: diagnostics.if_files_is_specified_otherwise_Asterisk_Asterisk_Slash_Asterisk, - }, - { - Name: "exclude", - Kind: CommandLineOptionTypeList, - // Category: diagnostics.File_Management, - // DefaultValueDescription: diagnostics.Node_modules_bower_components_jspm_packages_plus_the_value_of_outDir_if_one_is_specified, - }, - compileOnSaveCommandLineOption, - }), -} - -type configFileSpecs struct { - filesSpecs any - // Present to report errors (user specified specs), validatedIncludeSpecs are used for file name matching - includeSpecs any - // Present to report errors (user specified specs), validatedExcludeSpecs are used for file name matching - excludeSpecs any - validatedFilesSpec []string - validatedIncludeSpecs []string - validatedExcludeSpecs []string - validatedFilesSpecBeforeSubstitution []string - validatedIncludeSpecsBeforeSubstitution []string - isDefaultIncludeSpec bool -} - -func (c *configFileSpecs) matchesExclude(fileName string, comparePathsOptions tspath.ComparePathsOptions) bool { - if len(c.validatedExcludeSpecs) == 0 { - return false - } - excludePattern := vfs.GetRegularExpressionForWildcard(c.validatedExcludeSpecs, comparePathsOptions.CurrentDirectory, "exclude") - excludeRegex := vfs.GetRegexFromPattern(excludePattern, comparePathsOptions.UseCaseSensitiveFileNames) - if match, err := excludeRegex.MatchString(fileName); err == nil && match { - return true - } - if !tspath.HasExtension(fileName) { - if match, err := excludeRegex.MatchString(tspath.EnsureTrailingDirectorySeparator(fileName)); err == nil && match { - return true - } - } - return false -} - -func (c *configFileSpecs) getMatchedIncludeSpec(fileName string, comparePathsOptions tspath.ComparePathsOptions) string { - if len(c.validatedIncludeSpecs) == 0 { - return "" - } - for index, spec := range c.validatedIncludeSpecs { - includePattern := vfs.GetPatternFromSpec(spec, comparePathsOptions.CurrentDirectory, "files") - if includePattern != "" { - includeRegex := vfs.GetRegexFromPattern(includePattern, comparePathsOptions.UseCaseSensitiveFileNames) - if match, err := includeRegex.MatchString(fileName); err == nil && match { - return c.validatedIncludeSpecsBeforeSubstitution[index] - } - } - } - return "" -} - -func (c *configFileSpecs) getMatchedFileSpec(fileName string, comparePathsOptions tspath.ComparePathsOptions) string { - if len(c.validatedFilesSpec) == 0 { - return "" - } - filePath := tspath.ToPath(fileName, comparePathsOptions.CurrentDirectory, comparePathsOptions.UseCaseSensitiveFileNames) - for index, spec := range c.validatedFilesSpec { - if tspath.ToPath(spec, comparePathsOptions.CurrentDirectory, comparePathsOptions.UseCaseSensitiveFileNames) == filePath { - return c.validatedFilesSpecBeforeSubstitution[index] - } - } - return "" -} - -type FileExtensionInfo struct { - Extension string - IsMixedContent bool - ScriptKind core.ScriptKind -} - -type ExtendedConfigCache interface { - GetExtendedConfig(fileName string, path tspath.Path, parse func() *ExtendedConfigCacheEntry) *ExtendedConfigCacheEntry -} - -type ExtendedConfigCacheEntry struct { - extendedResult *TsConfigSourceFile - extendedConfig *parsedTsconfig - errors []*ast.Diagnostic -} - -type parsedTsconfig struct { - raw any - options *core.CompilerOptions - // watchOptions *core.WatchOptions - typeAcquisition *core.TypeAcquisition - // Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet - extendedConfigPath any -} - -func parseOwnConfigOfJsonSourceFile( - sourceFile *ast.SourceFile, - host ParseConfigHost, - basePath string, - configFileName string, -) (*parsedTsconfig, []*ast.Diagnostic) { - compilerOptions := getDefaultCompilerOptions(configFileName) - typeAcquisition := getDefaultTypeAcquisition(configFileName) - // var watchOptions *compiler.WatchOptions - var extendedConfigPath any - var rootCompilerOptions []*ast.PropertyName - var errors []*ast.Diagnostic - onPropertySet := func( - keyText string, - value any, - propertyAssignment *ast.PropertyAssignment, - parentOption *CommandLineOption, // TsConfigOnlyOption, - option *CommandLineOption, - ) (any, []*ast.Diagnostic) { - // Ensure value is verified except for extends which is handled in its own way for error reporting - var propertySetErrors []*ast.Diagnostic - if option != nil && option != extendsOptionDeclaration { - value, propertySetErrors = convertJsonOption(option, value, basePath, propertyAssignment, propertyAssignment.Initializer, sourceFile) - } - if parentOption != nil && parentOption.Name != "undefined" && value != nil { - if option != nil && option.Name != "" { - var parseDiagnostics []*ast.Diagnostic - switch parentOption.Name { - case "compilerOptions": - parseDiagnostics = ParseCompilerOptions(option.Name, value, compilerOptions) - case "typeAcquisition": - parseDiagnostics = ParseTypeAcquisition(option.Name, value, typeAcquisition) - } - propertySetErrors = append(propertySetErrors, parseDiagnostics...) - } else if keyText != "" && extraKeyDiagnostics(parentOption.Name) != nil { - unknownNameDiag := extraKeyDiagnostics(parentOption.Name) - if parentOption.ElementOptions != nil { - // !!! TODO: support suggestion - propertySetErrors = append(propertySetErrors, createUnknownOptionError( - keyText, - unknownNameDiag, - "", /*unknownOptionErrorText*/ - propertyAssignment.Name(), - sourceFile, - nil, /*alternateMode*/ - )) - } else { - // errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Unknown_compiler_option_0_Did_you_mean_1, keyText, core.FindKey(parentOption.ElementOptions, keyText))) - } - } - } else if parentOption == tsconfigRootOptionsMap { - if option == extendsOptionDeclaration { - configPath, err := getExtendsConfigPathOrArray(value, host, basePath, configFileName, propertyAssignment, propertyAssignment.Initializer, sourceFile) - extendedConfigPath = configPath - propertySetErrors = append(propertySetErrors, err...) - } else if option == nil { - if keyText == "excludes" { - propertySetErrors = append(propertySetErrors, CreateDiagnosticForNodeInSourceFile(sourceFile, propertyAssignment.Name(), diagnostics.Unknown_option_excludes_Did_you_mean_exclude)) - } - if core.Find(OptionsDeclarations, func(option *CommandLineOption) bool { return option.Name == keyText }) != nil { - rootCompilerOptions = append(rootCompilerOptions, propertyAssignment.Name()) - } - } - } - return value, propertySetErrors - } - - json, err := convertConfigFileToObject( - sourceFile, - &jsonConversionNotifier{ - tsconfigRootOptionsMap, - onPropertySet, - }, - ) - errors = append(errors, err...) - // if len(rootCompilerOptions) != 0 && json != nil && json.CompilerOptions != nil { - // errors = append(errors, ast.NewDiagnostic(sourceFile, rootCompilerOptions[0], diagnostics.X_0_should_be_set_inside_the_compilerOptions_object_of_the_config_json_file)) - // } - return &parsedTsconfig{ - raw: json, - options: compilerOptions, - // watchOptions: watchOptions, - typeAcquisition: typeAcquisition, - extendedConfigPath: extendedConfigPath, - }, errors -} - -type TsConfigSourceFile struct { - ExtendedSourceFiles []string - configFileSpecs *configFileSpecs - SourceFile *ast.SourceFile -} - -func tsconfigToSourceFile(tsconfigSourceFile *TsConfigSourceFile) *ast.SourceFile { - if tsconfigSourceFile == nil { - return nil - } - return tsconfigSourceFile.SourceFile -} - -func NewTsconfigSourceFileFromFilePath(configFileName string, configPath tspath.Path, configSourceText string) *TsConfigSourceFile { - sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ - FileName: configFileName, - Path: configPath, - }, configSourceText, core.ScriptKindJSON) - return &TsConfigSourceFile{ - SourceFile: sourceFile, - } -} - -type jsonConversionNotifier struct { - rootOptions *CommandLineOption - onPropertySet func(keyText string, value any, propertyAssignment *ast.PropertyAssignment, parentOption *CommandLineOption, option *CommandLineOption) (any, []*ast.Diagnostic) -} - -func convertConfigFileToObject( - sourceFile *ast.SourceFile, - jsonConversionNotifier *jsonConversionNotifier, -) (any, []*ast.Diagnostic) { - var rootExpression *ast.Expression - if len(sourceFile.Statements.Nodes) > 0 { - rootExpression = sourceFile.Statements.Nodes[0].AsExpressionStatement().Expression - } - if rootExpression != nil && rootExpression.Kind != ast.KindObjectLiteralExpression { - baseFileName := "tsconfig.json" - if tspath.GetBaseFileName(sourceFile.FileName()) == "jsconfig.json" { - baseFileName = "jsconfig.json" - } - errors := []*ast.Diagnostic{ast.NewCompilerDiagnostic(diagnostics.The_root_value_of_a_0_file_must_be_an_object, baseFileName)} - // Last-ditch error recovery. Somewhat useful because the JSON parser will recover from some parse errors by - // synthesizing a top-level array literal expression. There's a reasonable chance the first element of that - // array is a well-formed configuration object, made into an array element by stray characters. - if ast.IsArrayLiteralExpression(rootExpression) { - firstObject := core.Find(rootExpression.AsArrayLiteralExpression().Elements.Nodes, ast.IsObjectLiteralExpression) - if firstObject != nil { - return convertToJson(sourceFile, firstObject, true /*returnValue*/, jsonConversionNotifier) - } - } - return &collections.OrderedMap[string, any]{}, errors - } - return convertToJson(sourceFile, rootExpression, true, jsonConversionNotifier) -} - -var orderedMapType = reflect.TypeFor[*collections.OrderedMap[string, any]]() - -func isCompilerOptionsValue(option *CommandLineOption, value any) bool { - if option != nil { - if value == nil { - return !option.DisallowNullOrUndefined() - } - if option.Kind == "list" { - return reflect.TypeOf(value).Kind() == reflect.Slice - } - if option.Kind == "listOrElement" { - if reflect.TypeOf(value).Kind() == reflect.Slice { - return true - } else { - return isCompilerOptionsValue(option.Elements(), value) - } - } - if option.Kind == "string" { - return reflect.TypeOf(value).Kind() == reflect.String - } - if option.Kind == "boolean" { - return reflect.TypeOf(value).Kind() == reflect.Bool - } - if option.Kind == "number" { - return reflect.TypeOf(value).Kind() == reflect.Float64 - } - if option.Kind == "object" { - return reflect.TypeOf(value) == orderedMapType - } - if option.Kind == "enum" && reflect.TypeOf(value).Kind() == reflect.String { - return true - } - } - return false -} - -func validateJsonOptionValue( - opt *CommandLineOption, - val any, - valueExpression *ast.Expression, - sourceFile *ast.SourceFile, -) (any, []*ast.Diagnostic) { - if val == nil { - return nil, nil - } - errors := []*ast.Diagnostic{} - if opt.extraValidation { - diag := specToDiagnostic(val.(string), false) - if diag != nil { - errors = append(errors, CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, valueExpression, diag)) - return nil, errors - } - } - return val, nil -} - -func convertJsonOptionOfListType( - option *CommandLineOption, - values any, - basePath string, - propertyAssignment *ast.PropertyAssignment, - valueExpression *ast.Node, - sourceFile *ast.SourceFile, -) ([]any, []*ast.Diagnostic) { - var expression *ast.Node - var errors []*ast.Diagnostic - if values, ok := values.([]any); ok { - mappedValues := core.MapIndex(values, func(v any, index int) any { - if valueExpression != nil { - expression = valueExpression.AsArrayLiteralExpression().Elements.Nodes[index] - } - result, err := convertJsonOption(option.Elements(), v, basePath, propertyAssignment, expression, sourceFile) - errors = append(errors, err...) - return result - }) - filteredValues := mappedValues - if !option.listPreserveFalsyValues { - filteredValues = core.Filter(mappedValues, func(v any) bool { - return (v != nil && v != false && v != 0 && v != "") - }) - } - return filteredValues, errors - } - return nil, errors -} - -const configDirTemplate = "${configDir}" - -func startsWithConfigDirTemplate(value any) bool { - str, ok := value.(string) - if !ok { - return false - } - return strings.HasPrefix(strings.ToLower(str), strings.ToLower(configDirTemplate)) -} - -func normalizeNonListOptionValue(option *CommandLineOption, basePath string, value any) any { - if option.IsFilePath { - value = tspath.NormalizeSlashes(value.(string)) - if !startsWithConfigDirTemplate(value) { - value = tspath.GetNormalizedAbsolutePath(value.(string), basePath) - } - if value == "" { - value = "." - } - } - return value -} - -func convertJsonOption( - opt *CommandLineOption, - value any, - basePath string, - propertyAssignment *ast.PropertyAssignment, - valueExpression *ast.Expression, - sourceFile *ast.SourceFile, -) (any, []*ast.Diagnostic) { - if opt.IsCommandLineOnly { - var nodeValue *ast.Node - if propertyAssignment != nil { - nodeValue = propertyAssignment.Name() - } - if sourceFile == nil && nodeValue == nil { - return nil, []*ast.Diagnostic{ast.NewCompilerDiagnostic(diagnostics.Option_0_can_only_be_specified_on_command_line, opt.Name)} - } else { - return nil, []*ast.Diagnostic{CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, nodeValue, diagnostics.Option_0_can_only_be_specified_on_command_line, opt.Name)} - } - } - if isCompilerOptionsValue(opt, value) { - switch opt.Kind { - case CommandLineOptionTypeList: - return convertJsonOptionOfListType(opt, value, basePath, propertyAssignment, valueExpression, sourceFile) // as ArrayLiteralExpression | undefined - case CommandLineOptionTypeListOrElement: - if reflect.TypeOf(value).Kind() == reflect.Slice { - return convertJsonOptionOfListType(opt, value, basePath, propertyAssignment, valueExpression, sourceFile) - } else { - return convertJsonOption(opt.Elements(), value, basePath, propertyAssignment, valueExpression, sourceFile) - } - case CommandLineOptionTypeEnum: - return convertJsonOptionOfEnumType(opt, value.(string), valueExpression, sourceFile) - } - - validatedValue, errors := validateJsonOptionValue(opt, value, valueExpression, sourceFile) - if len(errors) > 0 || validatedValue == nil { - return validatedValue, errors - } else { - return normalizeNonListOptionValue(opt, basePath, validatedValue), errors - } - } else { - return nil, []*ast.Diagnostic{CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, valueExpression, diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.Name, getCompilerOptionValueTypeString(opt))} - } -} - -func getExtendsConfigPathOrArray( - value CompilerOptionsValue, - host ParseConfigHost, - basePath string, - configFileName string, - propertyAssignment *ast.PropertyAssignment, - valueExpression *ast.Expression, - sourceFile *ast.SourceFile, -) ([]string, []*ast.Diagnostic) { - var extendedConfigPathArray []string - newBase := basePath - if configFileName != "" { - newBase = directoryOfCombinedPath(configFileName, basePath) - } - if reflect.TypeOf(value).Kind() == reflect.String { - val, err := getExtendsConfigPath(value.(string), host, newBase, valueExpression, sourceFile) - if val != "" { - extendedConfigPathArray = append(extendedConfigPathArray, val) - } - return extendedConfigPathArray, err - } - var errors []*ast.Diagnostic - if reflect.TypeOf(value).Kind() == reflect.Slice { - for index, fileName := range value.([]any) { - var expression *ast.Expression = nil - if valueExpression != nil { - expression = valueExpression.AsArrayLiteralExpression().Elements.Nodes[index] - } - if reflect.TypeOf(fileName).Kind() == reflect.String { - val, err := getExtendsConfigPath(fileName.(string), host, newBase, expression, sourceFile) - if val != "" { - extendedConfigPathArray = append(extendedConfigPathArray, val) - } - errors = append(errors, err...) - } else { - _, err := convertJsonOption(extendsOptionDeclaration.Elements(), value, basePath, propertyAssignment, expression, sourceFile) - errors = append(errors, err...) - } - } - } else { - _, errors = convertJsonOption(extendsOptionDeclaration, value, basePath, propertyAssignment, valueExpression, sourceFile) - } - return extendedConfigPathArray, errors -} - -func getExtendsConfigPath( - extendedConfig string, - host ParseConfigHost, - basePath string, - valueExpression *ast.Expression, - sourceFile *ast.SourceFile, -) (string, []*ast.Diagnostic) { - extendedConfig = tspath.NormalizeSlashes(extendedConfig) - var errors []*ast.Diagnostic - var errorFile *ast.SourceFile - if sourceFile != nil { - errorFile = sourceFile - } - if tspath.IsRootedDiskPath(extendedConfig) || strings.HasPrefix(extendedConfig, "./") || strings.HasPrefix(extendedConfig, "../") { - extendedConfigPath := tspath.GetNormalizedAbsolutePath(extendedConfig, basePath) - if !host.FS().FileExists(extendedConfigPath) && !strings.HasSuffix(extendedConfigPath, tspath.ExtensionJson) { - extendedConfigPath = extendedConfigPath + tspath.ExtensionJson - if !host.FS().FileExists(extendedConfigPath) { - errors = append(errors, CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(errorFile, valueExpression, diagnostics.File_0_not_found, extendedConfig)) - return "", errors - } - } - return extendedConfigPath, errors - } - // If the path isn't a rooted or relative path, resolve like a module - resolverHost := &resolverHost{host} - if resolved := module.ResolveConfig(extendedConfig, tspath.CombinePaths(basePath, "tsconfig.json"), resolverHost); resolved.IsResolved() { - return resolved.ResolvedFileName, errors - } - if extendedConfig == "" { - errors = append(errors, CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(errorFile, valueExpression, diagnostics.Compiler_option_0_cannot_be_given_an_empty_string, "extends")) - } else { - errors = append(errors, CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(errorFile, valueExpression, diagnostics.File_0_not_found, extendedConfig)) - } - return "", errors -} - -type tsConfigOptions struct { - prop map[string][]string - references []*core.ProjectReference - notDefined string -} - -type CommandLineOptionNameMap map[string]*CommandLineOption - -func (m CommandLineOptionNameMap) Get(name string) *CommandLineOption { - opt, ok := m[name] - if !ok { - opt, _ = m[strings.ToLower(name)] - } - return opt -} - -func commandLineOptionsToMap(compilerOptions []*CommandLineOption) CommandLineOptionNameMap { - result := make(map[string]*CommandLineOption, len(compilerOptions)*2) - for i := range compilerOptions { - result[compilerOptions[i].Name] = compilerOptions[i] - result[strings.ToLower(compilerOptions[i].Name)] = compilerOptions[i] - } - return result -} - -var CommandLineCompilerOptionsMap CommandLineOptionNameMap = commandLineOptionsToMap(OptionsDeclarations) - -func convertMapToOptions[O optionParser](compilerOptions *collections.OrderedMap[string, any], result O) O { - // this assumes any `key`, `value` pair in `options` will have `value` already be the correct type. this function should no error handling - for key, value := range compilerOptions.Entries() { - result.ParseOption(key, value) - } - return result -} - -func convertOptionsFromJson[O optionParser](optionsNameMap CommandLineOptionNameMap, jsonOptions any, basePath string, result O) (O, []*ast.Diagnostic) { - if jsonOptions == nil { - return result, nil - } - jsonMap, ok := jsonOptions.(*collections.OrderedMap[string, any]) - if !ok { - // !!! probably should be an error - return result, nil - } - var errors []*ast.Diagnostic - for key, value := range jsonMap.Entries() { - opt := optionsNameMap.Get(key) - if opt == nil { - // !!! TODO?: support suggestion - errors = append(errors, createUnknownOptionError(key, result.UnknownOptionDiagnostic(), "", nil, nil, nil)) - continue - } - - commandLineOptionEnumMapVal := opt.EnumMap() - if commandLineOptionEnumMapVal != nil { - val, ok := commandLineOptionEnumMapVal.Get(strings.ToLower(value.(string))) - if ok { - errors = result.ParseOption(key, val) - } - } else { - convertJson, err := convertJsonOption(opt, value, basePath, nil, nil, nil) - errors = append(errors, err...) - compilerOptionsErr := result.ParseOption(key, convertJson) - errors = append(errors, compilerOptionsErr...) - } - } - return result, errors -} - -func convertArrayLiteralExpressionToJson( - sourceFile *ast.SourceFile, - elements []*ast.Expression, - elementOption *CommandLineOption, - returnValue bool, -) (any, []*ast.Diagnostic) { - if !returnValue { - for _, element := range elements { - convertPropertyValueToJson(sourceFile, element, elementOption, returnValue, nil) - } - return nil, nil - } - // Filter out invalid values - if len(elements) == 0 { - // Always return an empty array, even if elements is nil. - // The parser will produce nil slices instead of allocating empty ones. - return []any{}, nil - } - var errors []*ast.Diagnostic - var value []any - for _, element := range elements { - convertedValue, err := convertPropertyValueToJson(sourceFile, element, elementOption, returnValue, nil) - errors = append(errors, err...) - if convertedValue != nil { - value = append(value, convertedValue) - } - } - return value, errors -} - -func directoryOfCombinedPath(fileName string, basePath string) string { - // Use the `getNormalizedAbsolutePath` function to avoid canonicalizing the path, as it must remain noncanonical - // until consistent casing errors are reported - return tspath.GetDirectoryPath(tspath.GetNormalizedAbsolutePath(fileName, basePath)) -} - -// ParseConfigFileTextToJson parses the text of the tsconfig.json file -// fileName is the path to the config file -// jsonText is the text of the config file -func ParseConfigFileTextToJson(fileName string, path tspath.Path, jsonText string) (any, []*ast.Diagnostic) { - jsonSourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ - FileName: fileName, - Path: path, - }, jsonText, core.ScriptKindJSON) - config, errors := convertConfigFileToObject(jsonSourceFile /*jsonConversionNotifier*/, nil) - if len(jsonSourceFile.Diagnostics()) > 0 { - errors = []*ast.Diagnostic{jsonSourceFile.Diagnostics()[0]} - } - return config, errors -} - -type ParseConfigHost interface { - FS() vfs.FS - GetCurrentDirectory() string -} - -type resolverHost struct { - ParseConfigHost -} - -func (r *resolverHost) Trace(msg string) {} - -func ParseJsonSourceFileConfigFileContent( - sourceFile *TsConfigSourceFile, - host ParseConfigHost, - basePath string, - existingOptions *core.CompilerOptions, - configFileName string, - resolutionStack []tspath.Path, - extraFileExtensions []FileExtensionInfo, - extendedConfigCache ExtendedConfigCache, -) *ParsedCommandLine { - // tracing?.push(tracing.Phase.Parse, "parseJsonSourceFileConfigFileContent", { path: sourceFile.fileName }); - result := parseJsonConfigFileContentWorker(nil /*json*/, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache) - // tracing?.pop(); - return result -} - -func convertObjectLiteralExpressionToJson( - sourceFile *ast.SourceFile, - returnValue bool, - node *ast.ObjectLiteralExpression, - objectOption *CommandLineOption, - jsonConversionNotifier *jsonConversionNotifier, -) (*collections.OrderedMap[string, any], []*ast.Diagnostic) { - var result *collections.OrderedMap[string, any] - if returnValue { - result = &collections.OrderedMap[string, any]{} - } - var errors []*ast.Diagnostic - for _, element := range node.Properties.Nodes { - if element.Kind != ast.KindPropertyAssignment { - errors = append(errors, ast.NewDiagnostic(sourceFile, element.Loc, diagnostics.Property_assignment_expected)) - continue - } - - // !!! - // if ast.IsQuestionToken(element) { - // errors = append(errors, ast.NewDiagnostic(sourceFile, element.Loc, diagnostics.Property_assignment_expected)) - // } - if element.Name() != nil && !isDoubleQuotedString(element.Name()) { - errors = append(errors, ast.NewDiagnostic(sourceFile, element.Loc, diagnostics.String_literal_with_double_quotes_expected)) - } - - textOfKey := "" - if !ast.IsComputedNonLiteralName(element.Name()) { - textOfKey, _ = ast.TryGetTextOfPropertyName(element.Name()) - } - keyText := textOfKey - var option *CommandLineOption = nil - if keyText != "" && objectOption != nil && objectOption.ElementOptions != nil { - option = objectOption.ElementOptions.Get(keyText) - } - value, err := convertPropertyValueToJson(sourceFile, element.AsPropertyAssignment().Initializer, option, returnValue, jsonConversionNotifier) - errors = append(errors, err...) - if keyText != "" { - if returnValue { - result.Set(keyText, value) - } - // Notify key value set, if user asked for it - if jsonConversionNotifier != nil { - _, err := jsonConversionNotifier.onPropertySet(keyText, value, element.AsPropertyAssignment(), objectOption, option) - errors = append(errors, err...) - } - } - } - return result, errors -} - -// convertToJson converts the json syntax tree into the json value and report errors -// This returns the json value (apart from checking errors) only if returnValue provided is true. -// Otherwise it just checks the errors and returns undefined -func convertToJson( - sourceFile *ast.SourceFile, - rootExpression *ast.Expression, - returnValue bool, - jsonConversionNotifier *jsonConversionNotifier, -) (any, []*ast.Diagnostic) { - if rootExpression == nil { - if returnValue { - return struct{}{}, nil - } else { - return nil, nil - } - } - var rootOptions *CommandLineOption - if jsonConversionNotifier != nil { - rootOptions = jsonConversionNotifier.rootOptions - } - return convertPropertyValueToJson(sourceFile, rootExpression, rootOptions, returnValue, jsonConversionNotifier) -} - -func isDoubleQuotedString(node *ast.Node) bool { - return ast.IsStringLiteral(node) -} - -func convertPropertyValueToJson(sourceFile *ast.SourceFile, valueExpression *ast.Expression, option *CommandLineOption, returnValue bool, jsonConversionNotifier *jsonConversionNotifier) (any, []*ast.Diagnostic) { - switch valueExpression.Kind { - case ast.KindTrueKeyword: - return true, nil - case ast.KindFalseKeyword: - return false, nil - case ast.KindNullKeyword: // todo: how to manage null - return nil, nil - - case ast.KindStringLiteral: - if !isDoubleQuotedString(valueExpression) { - return valueExpression.AsStringLiteral().Text, []*ast.Diagnostic{ast.NewDiagnostic(sourceFile, valueExpression.Loc, diagnostics.String_literal_with_double_quotes_expected)} - } - return valueExpression.AsStringLiteral().Text, nil - - case ast.KindNumericLiteral: - return float64(jsnum.FromString(valueExpression.AsNumericLiteral().Text)), nil - case ast.KindPrefixUnaryExpression: - if valueExpression.AsPrefixUnaryExpression().Operator != ast.KindMinusToken || valueExpression.AsPrefixUnaryExpression().Operand.Kind != ast.KindNumericLiteral { - break // not valid JSON syntax - } - return float64(-jsnum.FromString(valueExpression.AsPrefixUnaryExpression().Operand.AsNumericLiteral().Text)), nil - case ast.KindObjectLiteralExpression: - objectLiteralExpression := valueExpression.AsObjectLiteralExpression() - // Currently having element option declaration in the tsconfig with type "object" - // determines if it needs onSetValidOptionKeyValueInParent callback or not - // At moment there are only "compilerOptions", "typeAcquisition" and "typingOptions" - // that satisfies it and need it to modify options set in them (for normalizing file paths) - // vs what we set in the json - // If need arises, we can modify this interface and callbacks as needed - return convertObjectLiteralExpressionToJson(sourceFile, returnValue, objectLiteralExpression, option, jsonConversionNotifier) - case ast.KindArrayLiteralExpression: - result, errors := convertArrayLiteralExpressionToJson( - sourceFile, - valueExpression.AsArrayLiteralExpression().Elements.Nodes, - option, - returnValue, - ) - return result, errors - } - // Not in expected format - var errors []*ast.Diagnostic - if option != nil { - errors = []*ast.Diagnostic{ast.NewDiagnostic(sourceFile, valueExpression.Loc, diagnostics.Compiler_option_0_requires_a_value_of_type_1, option.Name, getCompilerOptionValueTypeString(option))} - } else { - errors = []*ast.Diagnostic{ast.NewDiagnostic(sourceFile, valueExpression.Loc, diagnostics.Property_value_can_only_be_string_literal_numeric_literal_true_false_null_object_literal_or_array_literal)} - } - return nil, errors -} - -// ParseJsonConfigFileContent parses the contents of a config file (tsconfig.json). -// jsonNode: The contents of the config file to parse -// host: Instance of ParseConfigHost used to enumerate files in folder. -// basePath: A root directory to resolve relative path entries in the config file to. e.g. outDir -func ParseJsonConfigFileContent(json any, host ParseConfigHost, basePath string, existingOptions *core.CompilerOptions, configFileName string, resolutionStack []tspath.Path, extraFileExtensions []FileExtensionInfo, extendedConfigCache ExtendedConfigCache) *ParsedCommandLine { - result := parseJsonConfigFileContentWorker(parseJsonToStringKey(json), nil /*sourceFile*/, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache) - return result -} - -// convertToObject converts the json syntax tree into the json value -func convertToObject(sourceFile *ast.SourceFile) (any, []*ast.Diagnostic) { - var rootExpression *ast.Expression - if len(sourceFile.Statements.Nodes) != 0 { - rootExpression = sourceFile.Statements.Nodes[0].AsExpressionStatement().Expression - } - return convertToJson(sourceFile, rootExpression, true /*returnValue*/, nil /*jsonConversionNotifier*/) -} - -func getDefaultCompilerOptions(configFileName string) *core.CompilerOptions { - options := &core.CompilerOptions{} - if configFileName != "" && tspath.GetBaseFileName(configFileName) == "jsconfig.json" { - depth := 2 - options = &core.CompilerOptions{ - AllowJs: core.TSTrue, - MaxNodeModuleJsDepth: &depth, - AllowSyntheticDefaultImports: core.TSTrue, - SkipLibCheck: core.TSTrue, - NoEmit: core.TSTrue, - } - } - return options -} - -func getDefaultTypeAcquisition(configFileName string) *core.TypeAcquisition { - options := &core.TypeAcquisition{} - if configFileName != "" && tspath.GetBaseFileName(configFileName) == "jsconfig.json" { - options.Enable = core.TSTrue - } - return options -} - -func convertCompilerOptionsFromJsonWorker(jsonOptions any, basePath string, configFileName string) (*core.CompilerOptions, []*ast.Diagnostic) { - options := getDefaultCompilerOptions(configFileName) - _, errors := convertOptionsFromJson(CommandLineCompilerOptionsMap, jsonOptions, basePath, &compilerOptionsParser{options}) - if configFileName != "" { - options.ConfigFilePath = tspath.NormalizeSlashes(configFileName) - } - return options, errors -} - -func convertTypeAcquisitionFromJsonWorker(jsonOptions any, basePath string, configFileName string) (*core.TypeAcquisition, []*ast.Diagnostic) { - options := getDefaultTypeAcquisition(configFileName) - _, errors := convertOptionsFromJson(typeAcquisitionDeclaration.ElementOptions, jsonOptions, basePath, &typeAcquisitionParser{options}) - return options, errors -} - -func parseOwnConfigOfJson( - json *collections.OrderedMap[string, any], - host ParseConfigHost, - basePath string, - configFileName string, -) (*parsedTsconfig, []*ast.Diagnostic) { - var errors []*ast.Diagnostic - if json.Has("excludes") { - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Unknown_option_excludes_Did_you_mean_exclude)) - } - options, err := convertCompilerOptionsFromJsonWorker(json.GetOrZero("compilerOptions"), basePath, configFileName) - typeAcquisition, err2 := convertTypeAcquisitionFromJsonWorker(json.GetOrZero("typeAcquisition"), basePath, configFileName) - errors = append(append(errors, err...), err2...) - // watchOptions := convertWatchOptionsFromJsonWorker(json.watchOptions, basePath, errors) - // json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors) - var extendedConfigPath []string - if extends := json.GetOrZero("extends"); extends != nil && extends != "" { - extendedConfigPath, err = getExtendsConfigPathOrArray(extends, host, basePath, configFileName, nil, nil, nil) - errors = append(errors, err...) - } - parsedConfig := &parsedTsconfig{ - raw: json, - options: options, - typeAcquisition: typeAcquisition, - extendedConfigPath: extendedConfigPath, - } - return parsedConfig, errors -} - -func readJsonConfigFile(fileName string, path tspath.Path, readFile func(fileName string) (string, bool)) (*TsConfigSourceFile, []*ast.Diagnostic) { - text, diagnostic := tryReadFile(fileName, readFile, []*ast.Diagnostic{}) - if text != "" { - return &TsConfigSourceFile{ - SourceFile: parser.ParseSourceFile(ast.SourceFileParseOptions{ - FileName: fileName, - Path: path, - }, text, core.ScriptKindJSON), - }, diagnostic - } else { - file := &TsConfigSourceFile{ - SourceFile: (&ast.NodeFactory{}).NewSourceFile(ast.SourceFileParseOptions{FileName: fileName, Path: path}, "", nil, (&ast.NodeFactory{}).NewToken(ast.KindEndOfFile)).AsSourceFile(), - } - file.SourceFile.SetDiagnostics(diagnostic) - return file, diagnostic - } -} - -func getExtendedConfig( - sourceFile *TsConfigSourceFile, - extendedConfigFileName string, - host ParseConfigHost, - resolutionStack []string, - extendedConfigCache ExtendedConfigCache, - result *extendsResult, -) (*parsedTsconfig, []*ast.Diagnostic) { - var errors []*ast.Diagnostic - extendedConfigPath := tspath.ToPath(extendedConfigFileName, host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames()) - - parse := func() *ExtendedConfigCacheEntry { - var extendedConfig *parsedTsconfig - var entryErrors []*ast.Diagnostic - extendedResult, err := readJsonConfigFile(extendedConfigFileName, extendedConfigPath, host.FS().ReadFile) - entryErrors = append(entryErrors, err...) - if len(extendedResult.SourceFile.Diagnostics()) == 0 { - extendedConfig, err = parseConfig(nil, extendedResult, host, tspath.GetDirectoryPath(extendedConfigFileName), tspath.GetBaseFileName(extendedConfigFileName), resolutionStack, extendedConfigCache) - entryErrors = append(entryErrors, err...) - } - return &ExtendedConfigCacheEntry{ - extendedResult: extendedResult, - extendedConfig: extendedConfig, - errors: entryErrors, - } - } - - var cacheEntry *ExtendedConfigCacheEntry - if extendedConfigCache != nil { - cacheEntry = extendedConfigCache.GetExtendedConfig(extendedConfigFileName, extendedConfigPath, parse) - } else { - cacheEntry = parse() - } - - if len(cacheEntry.errors) > 0 { - errors = append(errors, cacheEntry.errors...) - } - - if cacheEntry.extendedResult != nil { - if sourceFile != nil { - result.extendedSourceFiles.Add(cacheEntry.extendedResult.SourceFile.FileName()) - for _, extendedSourceFile := range cacheEntry.extendedResult.ExtendedSourceFiles { - result.extendedSourceFiles.Add(extendedSourceFile) - } - } - } - return cacheEntry.extendedConfig, errors -} - -// parseConfig just extracts options/include/exclude/files out of a config file. -// It does not resolve the included files. -func parseConfig( - json *collections.OrderedMap[string, any], - sourceFile *TsConfigSourceFile, - host ParseConfigHost, - basePath string, - configFileName string, - resolutionStack []string, - extendedConfigCache ExtendedConfigCache, -) (*parsedTsconfig, []*ast.Diagnostic) { - basePath = tspath.NormalizeSlashes(basePath) - resolvedPath := tspath.GetNormalizedAbsolutePath(configFileName, basePath) - var errors []*ast.Diagnostic - if slices.Contains(resolutionStack, resolvedPath) { - var result *parsedTsconfig - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Circularity_detected_while_resolving_configuration_Colon_0)) - if json.Size() == 0 { - result = &parsedTsconfig{raw: json} - } else { - rawResult, err := convertToObject(sourceFile.SourceFile) - errors = append(errors, err...) - result = &parsedTsconfig{raw: rawResult} - } - return result, errors - } - - var ownConfig *parsedTsconfig - var err []*ast.Diagnostic - if json != nil { - ownConfig, err = parseOwnConfigOfJson(json, host, basePath, configFileName) - } else { - ownConfig, err = parseOwnConfigOfJsonSourceFile(tsconfigToSourceFile(sourceFile), host, basePath, configFileName) - } - errors = append(errors, err...) - if ownConfig.options != nil && ownConfig.options.Paths != nil { - // If we end up needing to resolve relative paths from 'paths' relative to - // the config file location, we'll need to know where that config file was. - // Since 'paths' can be inherited from an extended config in another directory, - // we wouldn't know which directory to use unless we store it here. - ownConfig.options.PathsBasePath = basePath - } - - applyExtendedConfig := func(result *extendsResult, extendedConfigPath string) { - extendedConfig, extendedErrors := getExtendedConfig(sourceFile, extendedConfigPath, host, resolutionStack, extendedConfigCache, result) - errors = append(errors, extendedErrors...) - if extendedConfig != nil && extendedConfig.options != nil { - extendsRaw := extendedConfig.raw - relativeDifference := "" - setPropertyValue := func(propertyName string) { - if rawMap, ok := ownConfig.raw.(*collections.OrderedMap[string, any]); ok && rawMap.Has(propertyName) { - return - } - if propertyName == "include" || propertyName == "exclude" || propertyName == "files" { - if rawMap, ok := extendsRaw.(*collections.OrderedMap[string, any]); ok && rawMap.Has(propertyName) { - if slice, _ := rawMap.GetOrZero(propertyName).([]any); slice != nil { - value := core.Map(slice, func(path any) any { - if startsWithConfigDirTemplate(path) || tspath.IsRootedDiskPath(path.(string)) { - return path.(string) - } else { - if relativeDifference == "" { - t := tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: basePath, - } - relativeDifference = tspath.ConvertToRelativePath(tspath.GetDirectoryPath(extendedConfigPath), t) - } - return tspath.CombinePaths(relativeDifference, path.(string)) - } - }) - if propertyName == "include" { - result.include = value - } else if propertyName == "exclude" { - result.exclude = value - } else if propertyName == "files" { - result.files = value - } - } - } - } - } - - setPropertyValue("include") - setPropertyValue("exclude") - setPropertyValue("files") - if extendedRawMap, ok := extendsRaw.(*collections.OrderedMap[string, any]); ok && extendedRawMap.Has("compileOnSave") { - if compileOnSave, ok := extendedRawMap.GetOrZero("compileOnSave").(bool); ok { - result.compileOnSave = compileOnSave - } - } - mergeCompilerOptions(result.options, extendedConfig.options, extendsRaw) - } - } - - if ownConfig.extendedConfigPath != nil { - // copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios. - resolutionStack = append(resolutionStack, resolvedPath) - var result *extendsResult = &extendsResult{ - options: &core.CompilerOptions{}, - } - if reflect.TypeOf(ownConfig.extendedConfigPath).Kind() == reflect.String { - applyExtendedConfig(result, ownConfig.extendedConfigPath.(string)) - } else if configPath, ok := ownConfig.extendedConfigPath.([]string); ok { - for _, extendedConfigPath := range configPath { - applyExtendedConfig(result, extendedConfigPath) - } - } - if result.include != nil { - ownConfig.raw.(*collections.OrderedMap[string, any]).Set("include", result.include) - } - if result.exclude != nil { - ownConfig.raw.(*collections.OrderedMap[string, any]).Set("exclude", result.exclude) - } - if result.files != nil { - ownConfig.raw.(*collections.OrderedMap[string, any]).Set("files", result.files) - } - if result.compileOnSave && !ownConfig.raw.(*collections.OrderedMap[string, any]).Has("compileOnSave") { - ownConfig.raw.(*collections.OrderedMap[string, any]).Set("compileOnSave", result.compileOnSave) - } - if sourceFile != nil { - for extendedSourceFile := range result.extendedSourceFiles.Keys() { - sourceFile.ExtendedSourceFiles = append(sourceFile.ExtendedSourceFiles, extendedSourceFile) - } - } - ownConfig.options = mergeCompilerOptions(result.options, ownConfig.options, ownConfig.raw) - // ownConfig.watchOptions = ownConfig.watchOptions && result.watchOptions ? - // assignWatchOptions(result, ownConfig.watchOptions) : - // ownConfig.watchOptions || result.watchOptions; - } - return ownConfig, errors -} - -const defaultIncludeSpec = "**/*" - -type propOfRaw struct { - sliceValue []any - wrongValue string -} - -// parseJsonConfigFileContentWorker parses the contents of a config file from json or json source file (tsconfig.json). -// json: The contents of the config file to parse -// sourceFile: sourceFile corresponding to the Json -// host: Instance of ParseConfigHost used to enumerate files in folder. -// basePath: A root directory to resolve relative path entries in the config file to. e.g. outDir -// resolutionStack: Only present for backwards-compatibility. Should be empty. -func parseJsonConfigFileContentWorker( - json *collections.OrderedMap[string, any], - sourceFile *TsConfigSourceFile, - host ParseConfigHost, - basePath string, - existingOptions *core.CompilerOptions, - configFileName string, - resolutionStack []tspath.Path, - extraFileExtensions []FileExtensionInfo, - extendedConfigCache ExtendedConfigCache, -) *ParsedCommandLine { - debug.Assert((json == nil && sourceFile != nil) || (json != nil && sourceFile == nil)) - - basePathForFileNames := "" - if configFileName != "" { - basePathForFileNames = tspath.NormalizePath(directoryOfCombinedPath(configFileName, basePath)) - } else { - basePathForFileNames = tspath.NormalizePath(basePath) - } - - var errors []*ast.Diagnostic - resolutionStackString := []string{} - parsedConfig, errors := parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStackString, extendedConfigCache) - mergeCompilerOptions(parsedConfig.options, existingOptions, nil) - handleOptionConfigDirTemplateSubstitution(parsedConfig.options, basePathForFileNames) - rawConfig := parseJsonToStringKey(parsedConfig.raw) - if configFileName != "" && parsedConfig.options != nil { - parsedConfig.options.ConfigFilePath = tspath.NormalizeSlashes(configFileName) - } - getPropFromRaw := func(prop string, validateElement func(value any) bool, elementTypeName string) propOfRaw { - value, exists := rawConfig.Get(prop) - if exists && value != nil { - if reflect.TypeOf(value).Kind() == reflect.Slice { - result := rawConfig.GetOrZero(prop) - if _, ok := result.([]any); ok { - if sourceFile == nil && !core.Every(result.([]any), validateElement) { - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Compiler_option_0_requires_a_value_of_type_1, prop, elementTypeName)) - } - } - return propOfRaw{sliceValue: result.([]any)} - } else if sourceFile == nil { - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Compiler_option_0_requires_a_value_of_type_1, prop, "Array")) - return propOfRaw{sliceValue: nil, wrongValue: "not-array"} - } - } - return propOfRaw{sliceValue: nil, wrongValue: "no-prop"} - } - referencesOfRaw := getPropFromRaw("references", func(element any) bool { return reflect.TypeOf(element) == orderedMapType }, "object") - fileSpecs := getPropFromRaw("files", func(element any) bool { return reflect.TypeOf(element).Kind() == reflect.String }, "string") - if fileSpecs.sliceValue != nil || fileSpecs.wrongValue == "" { - hasZeroOrNoReferences := false - if referencesOfRaw.wrongValue == "no-prop" || referencesOfRaw.wrongValue == "not-array" || len(referencesOfRaw.sliceValue) == 0 { - hasZeroOrNoReferences = true - } - hasExtends := rawConfig.GetOrZero("extends") - if fileSpecs.sliceValue != nil && len(fileSpecs.sliceValue) == 0 && hasZeroOrNoReferences && hasExtends == nil { - if sourceFile != nil { - var fileName string - if configFileName != "" { - fileName = configFileName - } else { - fileName = "tsconfig.json" - } - diagnosticMessage := diagnostics.The_files_list_in_config_file_0_is_empty - nodeValue := ForEachTsConfigPropArray(sourceFile.SourceFile, "files", func(property *ast.PropertyAssignment) *ast.Node { return property.Initializer }) - errors = append(errors, CreateDiagnosticForNodeInSourceFile(sourceFile.SourceFile, nodeValue, diagnosticMessage, fileName)) - } else { - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.The_files_list_in_config_file_0_is_empty, configFileName)) - } - } - } - includeSpecs := getPropFromRaw("include", func(element any) bool { return reflect.TypeOf(element).Kind() == reflect.String }, "string") - excludeSpecs := getPropFromRaw("exclude", func(element any) bool { return reflect.TypeOf(element).Kind() == reflect.String }, "string") - isDefaultIncludeSpec := false - if excludeSpecs.wrongValue == "no-prop" && parsedConfig.options != nil { - outDir := parsedConfig.options.OutDir - declarationDir := parsedConfig.options.DeclarationDir - if outDir != "" || declarationDir != "" { - var values []any - if outDir != "" { - values = append(values, outDir) - } - if declarationDir != "" { - values = append(values, declarationDir) - } - excludeSpecs = propOfRaw{sliceValue: values} - } - } - if fileSpecs.sliceValue == nil && includeSpecs.sliceValue == nil { - includeSpecs = propOfRaw{sliceValue: []any{defaultIncludeSpec}} - isDefaultIncludeSpec = true - } - var validatedIncludeSpecs []string - var validatedIncludeSpecsBeforeSubstitution []string - var validatedExcludeSpecs []string - var validatedFilesSpec []string - var validatedFilesSpecBeforeSubstitution []string - // The exclude spec list is converted into a regular expression, which allows us to quickly - // test whether a file or directory should be excluded before recursively traversing the - // file system. - if includeSpecs.sliceValue != nil { - var err []*ast.Diagnostic - validatedIncludeSpecsBeforeSubstitution, err = validateSpecs(includeSpecs.sliceValue, true /*disallowTrailingRecursion*/, tsconfigToSourceFile(sourceFile), "include") - errors = append(errors, err...) - if validatedIncludeSpecs = getSubstitutedStringArrayWithConfigDirTemplate(validatedIncludeSpecsBeforeSubstitution, basePathForFileNames); validatedIncludeSpecs == nil { - validatedIncludeSpecs = validatedIncludeSpecsBeforeSubstitution - } - } - if excludeSpecs.sliceValue != nil { - var err []*ast.Diagnostic - validatedExcludeSpecs, err = validateSpecs(excludeSpecs.sliceValue, false /*disallowTrailingRecursion*/, tsconfigToSourceFile(sourceFile), "exclude") - errors = append(errors, err...) - if validatedExcludeSpecsWithSubstitution := getSubstitutedStringArrayWithConfigDirTemplate(validatedExcludeSpecs, basePathForFileNames); validatedExcludeSpecsWithSubstitution != nil { - validatedExcludeSpecs = validatedExcludeSpecsWithSubstitution - } - } - if fileSpecs.sliceValue != nil { - fileSpecs := core.Filter(fileSpecs.sliceValue, func(spec any) bool { return reflect.TypeOf(spec).Kind() == reflect.String }) - for _, spec := range fileSpecs { - if spec, ok := spec.(string); ok { - validatedFilesSpecBeforeSubstitution = append(validatedFilesSpecBeforeSubstitution, spec) - } - } - if validatedFilesSpec = getSubstitutedStringArrayWithConfigDirTemplate(validatedFilesSpecBeforeSubstitution, basePathForFileNames); validatedFilesSpec == nil { - validatedFilesSpec = validatedFilesSpecBeforeSubstitution - } - } - configFileSpecs := configFileSpecs{ - fileSpecs.sliceValue, - includeSpecs.sliceValue, - excludeSpecs.sliceValue, - validatedFilesSpec, - validatedIncludeSpecs, - validatedExcludeSpecs, - validatedFilesSpecBeforeSubstitution, - validatedIncludeSpecsBeforeSubstitution, - isDefaultIncludeSpec, - } - - if sourceFile != nil { - sourceFile.configFileSpecs = &configFileSpecs - } - - getFileNames := func(basePath string) ([]string, int) { - parsedConfigOptions := parsedConfig.options - fileNames, literalFileNamesLen := getFileNamesFromConfigSpecs(configFileSpecs, basePath, parsedConfigOptions, host.FS(), extraFileExtensions) - if shouldReportNoInputFiles(fileNames, canJsonReportNoInputFiles(rawConfig), resolutionStack) { - includeSpecs := configFileSpecs.includeSpecs - excludeSpecs := configFileSpecs.excludeSpecs - if includeSpecs == nil { - includeSpecs = []string{} - } - if excludeSpecs == nil { - excludeSpecs = []string{} - } - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, configFileName, core.Must(core.StringifyJson(includeSpecs, "", "")), core.Must(core.StringifyJson(excludeSpecs, "", "")))) - } - return fileNames, literalFileNamesLen - } - - getProjectReferences := func(basePath string) []*core.ProjectReference { - var projectReferences []*core.ProjectReference - newReferencesOfRaw := getPropFromRaw("references", func(element any) bool { return reflect.TypeOf(element) == orderedMapType }, "object") - if newReferencesOfRaw.sliceValue != nil { - projectReferences = []*core.ProjectReference{} - for _, reference := range newReferencesOfRaw.sliceValue { - for _, ref := range parseProjectReference(reference) { - if reflect.TypeOf(ref.Path).Kind() != reflect.String { - if sourceFile == nil { - errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string")) - } - } else { - projectReferences = append(projectReferences, &core.ProjectReference{ - Path: tspath.GetNormalizedAbsolutePath(ref.Path, basePath), - OriginalPath: ref.Path, - Circular: ref.Circular, - }) - } - } - } - } - return projectReferences - } - - fileNames, literalFileNamesLen := getFileNames(basePathForFileNames) - return &ParsedCommandLine{ - ParsedConfig: &core.ParsedOptions{ - CompilerOptions: parsedConfig.options, - TypeAcquisition: parsedConfig.typeAcquisition, - // WatchOptions: nil, - FileNames: fileNames, - ProjectReferences: getProjectReferences(basePathForFileNames), - }, - ConfigFile: sourceFile, - Raw: parsedConfig.raw, - Errors: errors, - - extraFileExtensions: extraFileExtensions, - comparePathsOptions: tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: basePathForFileNames, - }, - literalFileNamesLen: literalFileNamesLen, - } -} - -func canJsonReportNoInputFiles(rawConfig *collections.OrderedMap[string, any]) bool { - filesExists := rawConfig.Has("files") - referencesExists := rawConfig.Has("references") - return !filesExists && !referencesExists -} - -func shouldReportNoInputFiles(fileNames []string, canJsonReportNoInputFiles bool, resolutionStack []tspath.Path) bool { - return len(fileNames) == 0 && canJsonReportNoInputFiles && len(resolutionStack) == 0 -} - -func validateSpecs(specs any, disallowTrailingRecursion bool, jsonSourceFile *ast.SourceFile, specKey string) ([]string, []*ast.Diagnostic) { - createDiagnostic := func(message *diagnostics.Message, spec string) *ast.Diagnostic { - element := GetTsConfigPropArrayElementValue(jsonSourceFile, specKey, spec) - return CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(jsonSourceFile, element.AsNode(), message, spec) - } - var errors []*ast.Diagnostic - var finalSpecs []string - for _, spec := range specs.([]any) { - if reflect.TypeOf(spec).Kind() != reflect.String { - continue - } - diag := specToDiagnostic(spec.(string), disallowTrailingRecursion) - if diag != nil { - errors = append(errors, createDiagnostic(diag, spec.(string))) - } else { - finalSpecs = append(finalSpecs, spec.(string)) - } - } - return finalSpecs, errors -} - -func specToDiagnostic(spec string, disallowTrailingRecursion bool) *diagnostics.Message { - if disallowTrailingRecursion { - if ok, _ := regexp.MatchString(invalidTrailingRecursionPattern, spec); ok { - return diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0 - } - } else if invalidDotDotAfterRecursiveWildcard(spec) { - return diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0 - } - return nil -} - -func invalidDotDotAfterRecursiveWildcard(s string) bool { - // We used to use the regex /(^|\/)\*\*\/(.*\/)?\.\.($|\/)/ to check for this case, but - // in v8, that has polynomial performance because the recursive wildcard match - **/ - - // can be matched in many arbitrary positions when multiple are present, resulting - // in bad backtracking (and we don't care which is matched - just that some /.. segment - // comes after some **/ segment). - var wildcardIndex int - if strings.HasPrefix(s, "**/") { - wildcardIndex = 0 - } else { - wildcardIndex = strings.Index(s, "/**/") - } - if wildcardIndex == -1 { - return false - } - var lastDotIndex int - if strings.HasSuffix(s, "/..") { - lastDotIndex = len(s) - } else { - lastDotIndex = strings.LastIndex(s, "/../") - } - return lastDotIndex > wildcardIndex -} - -// Tests for a path that ends in a recursive directory wildcard. -// -// Matches **, \**, **\, and \**\, but not a**b. -// NOTE: used \ in place of / above to avoid issues with multiline comments. -// -// Breakdown: -// -// (^|\/) # matches either the beginning of the string or a directory separator. -// \*\* # matches the recursive directory wildcard "**". -// \/?$ # matches an optional trailing directory separator at the end of the string. -const invalidTrailingRecursionPattern = `(?:^|\/)\*\*\/?$` - -func GetTsConfigPropArrayElementValue(tsConfigSourceFile *ast.SourceFile, propKey string, elementValue string) *ast.StringLiteral { - callback := GetCallbackForFindingPropertyAssignmentByValue(elementValue) - return ForEachTsConfigPropArray(tsConfigSourceFile, propKey, func(property *ast.PropertyAssignment) *ast.StringLiteral { - if value := callback(property); value != nil { - return value.AsStringLiteral() - } - return nil - }) -} - -func ForEachTsConfigPropArray[T any](tsConfigSourceFile *ast.SourceFile, propKey string, callback func(property *ast.PropertyAssignment) *T) *T { - if tsConfigSourceFile != nil { - return ForEachPropertyAssignment(getTsConfigObjectLiteralExpression(tsConfigSourceFile), propKey, callback) - } - return nil -} - -func CreateDiagnosticAtReferenceSyntax(config *ParsedCommandLine, index int, message *diagnostics.Message, args ...any) *ast.Diagnostic { - return ForEachTsConfigPropArray(config.ConfigFile.SourceFile, "references", func(property *ast.PropertyAssignment) *ast.Diagnostic { - if ast.IsArrayLiteralExpression(property.Initializer) { - value := property.Initializer.AsArrayLiteralExpression().Elements.Nodes - if len(value) > index { - return CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, value[index], message, args...) - } - } - return nil - }) -} - -func GetCallbackForFindingPropertyAssignmentByValue(value string) func(property *ast.PropertyAssignment) *ast.Node { - return func(property *ast.PropertyAssignment) *ast.Node { - if ast.IsArrayLiteralExpression(property.Initializer) { - return core.Find(property.Initializer.AsArrayLiteralExpression().Elements.Nodes, func(element *ast.Node) bool { - return ast.IsStringLiteral(element) && element.AsStringLiteral().Text == value - }) - } - return nil - } -} - -func GetOptionsSyntaxByArrayElementValue(objectLiteral *ast.ObjectLiteralExpression, propKey string, elementValue string) *ast.Node { - return ForEachPropertyAssignment(objectLiteral, propKey, GetCallbackForFindingPropertyAssignmentByValue(elementValue)) -} - -func ForEachPropertyAssignment[T any](objectLiteral *ast.ObjectLiteralExpression, key string, callback func(property *ast.PropertyAssignment) *T, key2 ...string) *T { - if objectLiteral != nil { - for _, property := range objectLiteral.Properties.Nodes { - if !ast.IsPropertyAssignment(property) { - continue - } - if propName, ok := ast.TryGetTextOfPropertyName(property.Name()); ok { - if propName == key || (len(key2) > 0 && key2[0] == propName) { - return callback(property.AsPropertyAssignment()) - } - } - } - } - return nil -} - -func getTsConfigObjectLiteralExpression(tsConfigSourceFile *ast.SourceFile) *ast.ObjectLiteralExpression { - if tsConfigSourceFile != nil && tsConfigSourceFile.Statements != nil && len(tsConfigSourceFile.Statements.Nodes) > 0 { - expression := tsConfigSourceFile.Statements.Nodes[0].AsExpressionStatement().Expression - return expression.AsObjectLiteralExpression() - } - return nil -} - -func getSubstitutedPathWithConfigDirTemplate(value string, basePath string) string { - return tspath.GetNormalizedAbsolutePath(strings.Replace(value, configDirTemplate, "./", 1), basePath) -} - -func getSubstitutedStringArrayWithConfigDirTemplate(list []string, basePath string) []string { - var result []string - for i, element := range list { - if startsWithConfigDirTemplate(element) { - if result == nil { - result = slices.Clone(list) - } - result[i] = getSubstitutedPathWithConfigDirTemplate(element, basePath) - } - } - if result != nil { - return result - } - return nil -} - -func handleOptionConfigDirTemplateSubstitution(compilerOptions *core.CompilerOptions, basePath string) { - if compilerOptions == nil { - return - } - - // !!! don't hardcode this; use options declarations? - - for k, v := range compilerOptions.Paths.Entries() { - if substitution := getSubstitutedStringArrayWithConfigDirTemplate(v, basePath); substitution != nil { - compilerOptions.Paths.Set(k, substitution) - } - } - - if rootDirs := getSubstitutedStringArrayWithConfigDirTemplate(compilerOptions.RootDirs, basePath); rootDirs != nil { - compilerOptions.RootDirs = rootDirs - } - if typeRoots := getSubstitutedStringArrayWithConfigDirTemplate(compilerOptions.TypeRoots, basePath); typeRoots != nil { - compilerOptions.TypeRoots = typeRoots - } - if startsWithConfigDirTemplate(compilerOptions.GenerateCpuProfile) { - compilerOptions.GenerateCpuProfile = getSubstitutedPathWithConfigDirTemplate(compilerOptions.GenerateCpuProfile, basePath) - } - if startsWithConfigDirTemplate(compilerOptions.GenerateTrace) { - compilerOptions.GenerateTrace = getSubstitutedPathWithConfigDirTemplate(compilerOptions.GenerateTrace, basePath) - } - if startsWithConfigDirTemplate(compilerOptions.OutFile) { - compilerOptions.OutFile = getSubstitutedPathWithConfigDirTemplate(compilerOptions.OutFile, basePath) - } - if startsWithConfigDirTemplate(compilerOptions.OutDir) { - compilerOptions.OutDir = getSubstitutedPathWithConfigDirTemplate(compilerOptions.OutDir, basePath) - } - if startsWithConfigDirTemplate(compilerOptions.RootDir) { - compilerOptions.RootDir = getSubstitutedPathWithConfigDirTemplate(compilerOptions.RootDir, basePath) - } - if startsWithConfigDirTemplate(compilerOptions.TsBuildInfoFile) { - compilerOptions.TsBuildInfoFile = getSubstitutedPathWithConfigDirTemplate(compilerOptions.TsBuildInfoFile, basePath) - } - if startsWithConfigDirTemplate(compilerOptions.BaseUrl) { - compilerOptions.BaseUrl = getSubstitutedPathWithConfigDirTemplate(compilerOptions.BaseUrl, basePath) - } - if startsWithConfigDirTemplate(compilerOptions.DeclarationDir) { - compilerOptions.DeclarationDir = getSubstitutedPathWithConfigDirTemplate(compilerOptions.DeclarationDir, basePath) - } -} - -// hasFileWithHigherPriorityExtension determines whether a literal or wildcard file has already been included that has a higher extension priority. -// file is the path to the file. -func hasFileWithHigherPriorityExtension(file string, extensions [][]string, hasFile func(fileName string) bool) bool { - var extensionGroup []string - for _, group := range extensions { - if tspath.FileExtensionIsOneOf(file, group) { - extensionGroup = append(extensionGroup, group...) - } - } - if len(extensionGroup) == 0 { - return false - } - for _, ext := range extensionGroup { - // d.ts files match with .ts extension and with case sensitive sorting the file order for same files with ts tsx and dts extension is - // d.ts, .ts, .tsx in that order so we need to handle tsx and dts of same same name case here and in remove files with same extensions - // So dont match .d.ts files with .ts extension - if tspath.FileExtensionIs(file, ext) && (ext != tspath.ExtensionTs || !tspath.FileExtensionIs(file, tspath.ExtensionDts)) { - return false - } - if hasFile(tspath.ChangeExtension(file, ext)) { - if ext == tspath.ExtensionDts && (tspath.FileExtensionIs(file, tspath.ExtensionJs) || tspath.FileExtensionIs(file, tspath.ExtensionJsx)) { - // LEGACY BEHAVIOR: An off-by-one bug somewhere in the extension priority system for wildcard module loading allowed declaration - // files to be loaded alongside their js(x) counterparts. We regard this as generally undesirable, but retain the behavior to - // prevent breakage. - continue - } - return true - } - } - return false -} - -// Removes files included via wildcard expansion with a lower extension priority that have already been included. -// file is the path to the file. -func removeWildcardFilesWithLowerPriorityExtension(file string, wildcardFiles *collections.OrderedMap[string, string], extensions [][]string, keyMapper func(value string) string) { - var extensionGroup []string - for _, group := range extensions { - if tspath.FileExtensionIsOneOf(file, group) { - extensionGroup = append(extensionGroup, group...) - } - } - if extensionGroup == nil { - return - } - for i := len(extensionGroup) - 1; i >= 0; i-- { - ext := extensionGroup[i] - if tspath.FileExtensionIs(file, ext) { - return - } - lowerPriorityPath := keyMapper(tspath.ChangeExtension(file, ext)) - wildcardFiles.Delete(lowerPriorityPath) - } -} - -// getFileNamesFromConfigSpecs gets the file names from the provided config file specs that contain, files, include, exclude and -// other properties needed to resolve the file names -// configFileSpecs is the config file specs extracted with file names to include, wildcards to include/exclude and other details -// basePath is the base path for any relative file specifications. -// options is the Compiler options. -// host is the host used to resolve files and directories. -// extraFileExtensions optionally file extra file extension information from host -func getFileNamesFromConfigSpecs( - configFileSpecs configFileSpecs, - basePath string, // considering this is the current directory - options *core.CompilerOptions, - host vfs.FS, - extraFileExtensions []FileExtensionInfo, -) ([]string, int) { - extraFileExtensions = []FileExtensionInfo{} - basePath = tspath.NormalizePath(basePath) - keyMappper := func(value string) string { return tspath.GetCanonicalFileName(value, host.UseCaseSensitiveFileNames()) } - // Literal file names (provided via the "files" array in tsconfig.json) are stored in a - // file map with a possibly case insensitive key. We use this map later when when including - // wildcard paths. - var literalFileMap collections.OrderedMap[string, string] - // Wildcard paths (provided via the "includes" array in tsconfig.json) are stored in a - // file map with a possibly case insensitive key. We use this map to store paths matched - // via wildcard, and to handle extension priority. - var wildcardFileMap collections.OrderedMap[string, string] - // Wildcard paths of json files (provided via the "includes" array in tsconfig.json) are stored in a - // file map with a possibly case insensitive key. We use this map to store paths matched - // via wildcard of *.json kind - var wildCardJsonFileMap collections.OrderedMap[string, string] - validatedFilesSpec := configFileSpecs.validatedFilesSpec - validatedIncludeSpecs := configFileSpecs.validatedIncludeSpecs - validatedExcludeSpecs := configFileSpecs.validatedExcludeSpecs - // Rather than re-query this for each file and filespec, we query the supported extensions - // once and store it on the expansion context. - supportedExtensions := GetSupportedExtensions(options, extraFileExtensions) - supportedExtensionsWithJsonIfResolveJsonModule := GetSupportedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions) - // Literal files are always included verbatim. An "include" or "exclude" specification cannot - // remove a literal file. - for _, fileName := range validatedFilesSpec { - file := tspath.GetNormalizedAbsolutePath(fileName, basePath) - literalFileMap.Set(keyMappper(fileName), file) - } - - var jsonOnlyIncludeRegexes []*regexp2.Regexp - if len(validatedIncludeSpecs) > 0 { - files := vfs.ReadDirectory(host, basePath, basePath, core.Flatten(supportedExtensionsWithJsonIfResolveJsonModule), validatedExcludeSpecs, validatedIncludeSpecs, nil) - for _, file := range files { - if tspath.FileExtensionIs(file, tspath.ExtensionJson) { - if jsonOnlyIncludeRegexes == nil { - includes := core.Filter(validatedIncludeSpecs, func(include string) bool { return strings.HasSuffix(include, tspath.ExtensionJson) }) - includeFilePatterns := core.Map(vfs.GetRegularExpressionsForWildcards(includes, basePath, "files"), func(pattern string) string { return fmt.Sprintf("^%s$", pattern) }) - if includeFilePatterns != nil { - jsonOnlyIncludeRegexes = core.Map(includeFilePatterns, func(pattern string) *regexp2.Regexp { - return vfs.GetRegexFromPattern(pattern, host.UseCaseSensitiveFileNames()) - }) - } else { - jsonOnlyIncludeRegexes = nil - } - } - includeIndex := core.FindIndex(jsonOnlyIncludeRegexes, func(re *regexp2.Regexp) bool { return core.Must(re.MatchString(file)) }) - if includeIndex != -1 { - key := keyMappper(file) - if !literalFileMap.Has(key) && !wildCardJsonFileMap.Has(key) { - wildCardJsonFileMap.Set(key, file) - } - } - continue - } - // If we have already included a literal or wildcard path with a - // higher priority extension, we should skip this file. - // - // This handles cases where we may encounter both .ts and - // .d.ts (or .js if "allowJs" is enabled) in the same - // directory when they are compilation outputs. - if hasFileWithHigherPriorityExtension(file, supportedExtensions, func(fileName string) bool { - canonicalFileName := keyMappper(fileName) - return literalFileMap.Has(canonicalFileName) || wildcardFileMap.Has(canonicalFileName) - }) { - continue - } - // We may have included a wildcard path with a lower priority - // extension due to the user-defined order of entries in the - // "include" array. If there is a lower priority extension in the - // same directory, we should remove it. - removeWildcardFilesWithLowerPriorityExtension(file, &wildcardFileMap, supportedExtensions, keyMappper) - key := keyMappper(file) - if !literalFileMap.Has(key) && !wildcardFileMap.Has(key) { - wildcardFileMap.Set(key, file) - } - } - } - files := make([]string, 0, literalFileMap.Size()+wildcardFileMap.Size()+wildCardJsonFileMap.Size()) - for file := range literalFileMap.Values() { - files = append(files, file) - } - for file := range wildcardFileMap.Values() { - files = append(files, file) - } - for file := range wildCardJsonFileMap.Values() { - files = append(files, file) - } - return files, literalFileMap.Size() -} - -func GetSupportedExtensions(compilerOptions *core.CompilerOptions, extraFileExtensions []FileExtensionInfo) [][]string { - needJSExtensions := compilerOptions.GetAllowJS() - if len(extraFileExtensions) == 0 { - if needJSExtensions { - return tspath.AllSupportedExtensions - } else { - return tspath.SupportedTSExtensions - } - } - var builtins [][]string - if needJSExtensions { - builtins = tspath.AllSupportedExtensions - } else { - builtins = tspath.SupportedTSExtensions - } - flatBuiltins := core.Flatten(builtins) - var result [][]string - for _, x := range extraFileExtensions { - if x.ScriptKind == core.ScriptKindDeferred || (needJSExtensions && (x.ScriptKind == core.ScriptKindJS || x.ScriptKind == core.ScriptKindJSX)) && !slices.Contains(flatBuiltins, x.Extension) { - result = append(result, []string{x.Extension}) - } - } - extensions := slices.Concat(builtins, result) - return extensions -} - -func GetSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions *core.CompilerOptions, supportedExtensions [][]string) [][]string { - if compilerOptions == nil || !compilerOptions.GetResolveJsonModule() { - return supportedExtensions - } - if core.Same(supportedExtensions, tspath.AllSupportedExtensions) { - return tspath.AllSupportedExtensionsWithJson - } - if core.Same(supportedExtensions, tspath.SupportedTSExtensions) { - return tspath.SupportedTSExtensionsWithJson - } - return slices.Concat(supportedExtensions, [][]string{{tspath.ExtensionJson}}) -} - -// Reads the config file and reports errors. -func GetParsedCommandLineOfConfigFile( - configFileName string, - options *core.CompilerOptions, - sys ParseConfigHost, - extendedConfigCache ExtendedConfigCache, -) (*ParsedCommandLine, []*ast.Diagnostic) { - configFileName = tspath.GetNormalizedAbsolutePath(configFileName, sys.GetCurrentDirectory()) - return GetParsedCommandLineOfConfigFilePath(configFileName, tspath.ToPath(configFileName, sys.GetCurrentDirectory(), sys.FS().UseCaseSensitiveFileNames()), options, sys, extendedConfigCache) -} - -func GetParsedCommandLineOfConfigFilePath( - configFileName string, - path tspath.Path, - options *core.CompilerOptions, - sys ParseConfigHost, - extendedConfigCache ExtendedConfigCache, -) (*ParsedCommandLine, []*ast.Diagnostic) { - errors := []*ast.Diagnostic{} - configFileText, errors := tryReadFile(configFileName, sys.FS().ReadFile, errors) - if len(errors) > 0 { - // these are unrecoverable errors--exit to report them as diagnostics - return nil, errors - } - - tsConfigSourceFile := NewTsconfigSourceFileFromFilePath(configFileName, path, configFileText) - // tsConfigSourceFile.resolvedPath = tsConfigSourceFile.FileName() - // tsConfigSourceFile.originalFileName = tsConfigSourceFile.FileName() - return ParseJsonSourceFileConfigFileContent( - tsConfigSourceFile, - sys, - tspath.GetDirectoryPath(configFileName), - options, - configFileName, - nil, - nil, - extendedConfigCache, - ), nil -} diff --git a/kitcom/internal/tsgo/tsoptions/tsconfigparsing_test.go b/kitcom/internal/tsgo/tsoptions/tsconfigparsing_test.go deleted file mode 100644 index 62b80e6..0000000 --- a/kitcom/internal/tsgo/tsoptions/tsconfigparsing_test.go +++ /dev/null @@ -1,1322 +0,0 @@ -package tsoptions_test - -import ( - "fmt" - "io" - "io/fs" - "path/filepath" - "strings" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnosticwriter" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsonutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/baseline" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions/tsoptionstest" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/osvfs" - "github.com/google/go-cmp/cmp/cmpopts" - "gotest.tools/v3/assert" -) - -type testConfig struct { - jsonText string - configFileName string - basePath string - allFileList map[string]string -} - -var parseConfigFileTextToJsonTests = []struct { - title string - input []string -}{ - { - title: "returns empty config for file with only whitespaces", - input: []string{ - "", - " ", - }, - }, - { - title: "returns empty config for file with comments only", - input: []string{ - "// Comment", - "/* Comment*/", - }, - }, - { - title: "returns empty config when config is empty object", - input: []string{ - `{}`, - }, - }, - { - title: "returns config object without comments", - input: []string{ - `{ // Excluded files - "exclude": [ - // Exclude d.ts - "file.d.ts" - ] - }`, - `{ - /* Excluded - Files - */ - "exclude": [ - /* multiline comments can be in the middle of a line */"file.d.ts" - ] - }`, - }, - }, - { - title: "keeps string content untouched", - input: []string{ - `{ - "exclude": [ - "xx//file.d.ts" - ] - }`, - `{ - "exclude": [ - "xx/*file.d.ts*/" - ] - }`, - }, - }, - { - title: "handles escaped characters in strings correctly", - input: []string{ - `{ - "exclude": [ - "xx\"//files" - ] - }`, - `{ - "exclude": [ - "xx\\" // end of line comment - ] - }`, - }, - }, - { - title: "returns object when users correctly specify library", - input: []string{ - `{ - "compilerOptions": { - "lib": ["es5"] - } - }`, - `{ - "compilerOptions": { - "lib": ["es5", "es6"] - } - }`, - }, - }, -} - -func TestParseConfigFileTextToJson(t *testing.T) { - t.Parallel() - repo.SkipIfNoTypeScriptSubmodule(t) - for _, rec := range parseConfigFileTextToJsonTests { - t.Run(rec.title, func(t *testing.T) { - t.Parallel() - var baselineContent strings.Builder - for i, jsonText := range rec.input { - baselineContent.WriteString("Input::\n") - baselineContent.WriteString(jsonText + "\n") - parsed, errors := tsoptions.ParseConfigFileTextToJson("/apath/tsconfig.json", "/apath", jsonText) - baselineContent.WriteString("Config::\n") - assert.NilError(t, writeJsonReadableText(&baselineContent, parsed), "Failed to write JSON text") - baselineContent.WriteString("\n") - baselineContent.WriteString("Errors::\n") - diagnosticwriter.FormatDiagnosticsWithColorAndContext(&baselineContent, errors, &diagnosticwriter.FormattingOptions{ - NewLine: "\n", - ComparePathsOptions: tspath.ComparePathsOptions{ - CurrentDirectory: "/", - UseCaseSensitiveFileNames: true, - }, - }) - baselineContent.WriteString("\n") - if i != len(rec.input)-1 { - baselineContent.WriteString("\n") - } - } - baseline.RunAgainstSubmodule(t, rec.title+" jsonParse.js", baselineContent.String(), baseline.Options{Subfolder: "config/tsconfigParsing"}) - }) - } -} - -type parseJsonConfigTestCase struct { - title string - noSubmoduleBaseline bool - input []testConfig -} - -var parseJsonConfigFileTests = []parseJsonConfigTestCase{ - { - title: "ignore dotted files and folders", - input: []testConfig{{ - jsonText: `{}`, - configFileName: "tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/test.ts": "", "/apath/.git/a.ts": "", "/apath/.b.ts": "", "/apath/..c.ts": ""}, - }}, - }, - { - title: "allow dotted files and folders when explicitly requested", - input: []testConfig{{ - jsonText: `{ - "files": ["/apath/.git/a.ts", "/apath/.b.ts", "/apath/..c.ts"] - }`, - configFileName: "tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/test.ts": "", "/apath/.git/a.ts": "", "/apath/.b.ts": "", "/apath/..c.ts": ""}, - }}, - }, - { - title: "implicitly exclude common package folders", - input: []testConfig{{ - jsonText: `{}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/node_modules/a.ts": "", "/bower_components/b.ts": "", "/jspm_packages/c.ts": "", "/d.ts": "", "/folder/e.ts": ""}, - }}, - }, - { - title: "generates errors for empty files list", - input: []testConfig{{ - jsonText: `{ - "files": [] - }`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/a.ts": ""}, - }}, - }, - { - title: "generates errors for empty files list when no references are provided", - input: []testConfig{{ - jsonText: `{ - "files": [], - "references": [] - }`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/a.ts": ""}, - }}, - }, - { - title: "generates errors for directory with no .ts files", - input: []testConfig{{ - jsonText: `{ - }`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/a.js": ""}, - }}, - }, - { - title: "generates errors for empty include", - input: []testConfig{{ - jsonText: `{ - "include": [] - }`, - configFileName: "/apath/tsconfig.json", - basePath: "tests/cases/unittests", - allFileList: map[string]string{"/apath/a.ts": ""}, - }}, - }, - { - title: "parses tsconfig with compilerOptions, files, include, and exclude", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "outDir": "./dist", - "strict": true, - "noImplicitAny": true, - "target": "ES2017", - "module": "ESNext", - "moduleResolution": "bundler", - "moduleDetection": "auto", - "jsx": "react", - "maxNodeModuleJsDepth": 1, - "paths": { - "jquery": ["./vendor/jquery/dist/jquery"] - } - }, - "files": ["/apath/src/index.ts", "/apath/src/app.ts"], - "include": ["/apath/src/**/*"], - "exclude": ["/apath/node_modules", "/apath/dist"] -}`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/src/index.ts": "", "/apath/src/app.ts": "", "/apath/node_modules/module.ts": "", "/apath/dist/output.js": ""}, - }}, - }, - { - title: "generates errors when commandline option is in tsconfig", - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "help": true - } -}`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/a.ts": ""}, - }}, - }, - { - title: "does not generate errors for empty files list when one or more references are provided", - input: []testConfig{{ - jsonText: `{ - "files": [], - "references": [{ "path": "/apath" }] - }`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/a.ts": ""}, - }}, - }, - { - title: "exclude outDir unless overridden", - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "outDir": "bin" - } - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/bin/a.ts": "", "/b.ts": ""}, - }, { - jsonText: `{ - "compilerOptions": { - "outDir": "bin" - }, - "exclude": [ "obj" ] - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/bin/a.ts": "", "/b.ts": ""}, - }}, - }, - { - title: "exclude declarationDir unless overridden", - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "declarationDir": "declarations" - } - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/declarations/a.d.ts": "", "/a.ts": ""}, - }, { - jsonText: `{ - "compilerOptions": { - "declarationDir": "declarations" - }, - "exclude": [ "types" ] - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/declarations/a.d.ts": "", "/a.ts": ""}, - }}, - }, - { - title: "generates errors for empty directory", - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "allowJs": true - } - }`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{}, - }}, - }, - { - title: "generates errors for includes with outDir", - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "outDir": "./" - }, - "include": ["**/*"] - }`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/a.ts": ""}, - }}, - }, - { - title: "generates errors when include is not string", - input: []testConfig{{ - jsonText: `{ - "include": [ - [ - "./**/*.ts" - ] - ] -}`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/a.ts": ""}, - }}, - }, - { - title: "generates errors when files is not string", - input: []testConfig{{ - jsonText: `{ - "files": [ - { - "compilerOptions": { - "experimentalDecorators": true, - "allowJs": true - } - } - ] -}`, - configFileName: "/apath/tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/a.ts": ""}, - }}, - }, - { - title: "with outDir from base tsconfig", - input: []testConfig{ - { - jsonText: `{ - "extends": "./tsconfigWithoutConfigDir.json" -}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{ - "/tsconfigWithoutConfigDir.json": tsconfigWithoutConfigDir, - "/bin/a.ts": "", - "/b.ts": "", - }, - }, - { - jsonText: `{ - "extends": "./tsconfigWithConfigDir.json" -}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{ - "/tsconfigWithConfigDir.json": tsconfigWithConfigDir, - "/bin/a.ts": "", - "/b.ts": "", - }, - }, - }, - }, - { - title: "returns error when tsconfig have excludes", - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "lib": ["es5"] - }, - "excludes": [ - "foge.ts" - ] - }`, - configFileName: "tsconfig.json", - basePath: "/apath", - allFileList: map[string]string{"/apath/test.ts": "", "/apath/foge.ts": ""}, - }}, - }, - { - title: "parses tsconfig with extends, files, include and other options", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "extends": "./tsconfigWithExtends.json", - "compilerOptions": { - "outDir": "./dist", - "strict": true, - "noImplicitAny": true, - "baseUrl": "", - }, - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/tsconfigWithExtends.json": tsconfigWithExtends, "/src/index.ts": "", "/src/app.ts": "", "/node_modules/module.ts": "", "/dist/output.js": ""}, - }}, - }, - { - title: "parses tsconfig with extends and configDir", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "extends": "./tsconfig.base.json" - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/tsconfig.base.json": tsconfigWithExtendsAndConfigDir, "/src/index.ts": "", "/src/app.ts": "", "/node_modules/module.ts": "", "/dist/output.js": ""}, - }}, - }, - { - title: "reports error for an unknown option", - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "unknown": true - } - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/app.ts": ""}, - }}, - }, - { - title: "reports errors for wrong type option and invalid enum value", - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "target": "invalid value", - "removeComments": "should be a boolean", - "moduleResolution": "invalid value" - } - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/app.ts": ""}, - }}, - }, - { - title: "handles empty types array", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "compilerOptions": { - "types": [] - } - }`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{"/app.ts": ""}, - }}, - }, - { - title: "issue 1267 scenario - extended files not picked up", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "extends": "./tsconfig-base/backend.json", - "compilerOptions": { - "baseUrl": "./", - "outDir": "dist", - "rootDir": "src", - "resolveJsonModule": true - }, - "exclude": ["node_modules", "dist"], - "include": ["src/**/*"] -}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{ - "/tsconfig-base/backend.json": `{ - "$schema": "https://json.schemastore.org/tsconfig", - "display": "Backend", - "compilerOptions": { - "allowJs": true, - "module": "nodenext", - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "esnext", - "lib": ["ESNext"], - "incremental": false, - "esModuleInterop": true, - "noImplicitAny": true, - "moduleResolution": "nodenext", - "types": ["node", "vitest/globals"], - "sourceMap": true, - "strictPropertyInitialization": false - }, - "files": [ - "types/ical2json.d.ts", - "types/express.d.ts", - "types/multer.d.ts", - "types/reset.d.ts", - "types/stripe-custom-typings.d.ts", - "types/nestjs-modules.d.ts", - "types/luxon.d.ts", - "types/nestjs-pino.d.ts" - ], - "ts-node": { - "files": true - } -}`, - "/tsconfig-base/types/ical2json.d.ts": "export {}", - "/tsconfig-base/types/express.d.ts": "export {}", - "/tsconfig-base/types/multer.d.ts": "export {}", - "/tsconfig-base/types/reset.d.ts": "export {}", - "/tsconfig-base/types/stripe-custom-typings.d.ts": "export {}", - "/tsconfig-base/types/nestjs-modules.d.ts": "export {}", - "/tsconfig-base/types/luxon.d.ts": `declare module 'luxon' { - interface TSSettings { - throwOnInvalid: true - } -} -export {}`, - "/tsconfig-base/types/nestjs-pino.d.ts": "export {}", - "/src/main.ts": "export {}", - "/src/utils.ts": "export {}", - }, - }}, - }, - { - title: "null overrides in extended tsconfig - array fields", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "extends": "./tsconfig-base.json", - "compilerOptions": { - "types": null, - "lib": null, - "typeRoots": null - } -}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{ - "/tsconfig-base.json": `{ - "compilerOptions": { - "types": ["node", "@types/jest"], - "lib": ["es2020", "dom"], - "typeRoots": ["./types", "./node_modules/@types"] - } -}`, - "/app.ts": "", - }, - }}, - }, - { - title: "null overrides in extended tsconfig - string fields", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "extends": "./tsconfig-base.json", - "compilerOptions": { - "outDir": null, - "baseUrl": null, - "rootDir": null - } -}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{ - "/tsconfig-base.json": `{ - "compilerOptions": { - "outDir": "./dist", - "baseUrl": "./src", - "rootDir": "./src" - } -}`, - "/app.ts": "", - }, - }}, - }, - { - title: "null overrides in extended tsconfig - mixed field types", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "extends": "./tsconfig-base.json", - "compilerOptions": { - "types": null, - "outDir": null, - "strict": false, - "lib": ["es2022"], - "allowJs": null - } -}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{ - "/tsconfig-base.json": `{ - "compilerOptions": { - "types": ["node"], - "lib": ["es2020", "dom"], - "outDir": "./dist", - "strict": true, - "allowJs": true, - "target": "es2020" - } -}`, - "/app.ts": "", - }, - }}, - }, - { - title: "null overrides with multiple extends levels", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "extends": "./tsconfig-middle.json", - "compilerOptions": { - "types": null, - "lib": null - } -}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{ - "/tsconfig-middle.json": `{ - "extends": "./tsconfig-base.json", - "compilerOptions": { - "types": ["jest"], - "outDir": "./build" - } -}`, - "/tsconfig-base.json": `{ - "compilerOptions": { - "types": ["node"], - "lib": ["es2020"], - "outDir": "./dist", - "strict": true - } -}`, - "/app.ts": "", - }, - }}, - }, - { - title: "null overrides in middle level of extends chain", - noSubmoduleBaseline: true, - input: []testConfig{{ - jsonText: `{ - "extends": "./tsconfig-middle.json", - "compilerOptions": { - "outDir": "./final" - } -}`, - configFileName: "tsconfig.json", - basePath: "/", - allFileList: map[string]string{ - "/tsconfig-middle.json": `{ - "extends": "./tsconfig-base.json", - "compilerOptions": { - "types": null, - "lib": null, - "outDir": "./middle" - } -}`, - "/tsconfig-base.json": `{ - "compilerOptions": { - "types": ["node"], - "lib": ["es2020"], - "outDir": "./base", - "strict": true - } -}`, - "/app.ts": "", - }, - }}, - }, -} - -var tsconfigWithExtends = `{ - "files": ["/src/index.ts", "/src/app.ts"], - "include": ["/src/**/*"], - "exclude": [], - "ts-node": { - "compilerOptions": { - "module": "commonjs" - }, - "transpileOnly": true - } -}` - -var tsconfigWithoutConfigDir = `{ - "compilerOptions": { - "outDir": "bin" - } -}` - -var tsconfigWithConfigDir = `{ - "compilerOptions": { - "outDir": "${configDir}/bin" - } -}` - -var tsconfigWithExtendsAndConfigDir = `{ - "compilerOptions": { - "outFile": "${configDir}/outFile", - "outDir": "${configDir}/outDir", - "rootDir": "${configDir}/rootDir", - "tsBuildInfoFile": "${configDir}/tsBuildInfoFile", - "baseUrl": "${configDir}/baseUrl", - "declarationDir": "${configDir}/declarationDir", - } -}` - -func TestParseJsonConfigFileContent(t *testing.T) { - t.Parallel() - repo.SkipIfNoTypeScriptSubmodule(t) - for _, rec := range parseJsonConfigFileTests { - t.Run(rec.title+" with json api", func(t *testing.T) { - t.Parallel() - baselineParseConfigWith(t, rec.title+" with json api.js", rec.noSubmoduleBaseline, rec.input, getParsedWithJsonApi) - }) - } -} - -func getParsedWithJsonApi(config testConfig, host tsoptions.ParseConfigHost, basePath string) *tsoptions.ParsedCommandLine { - configFileName := tspath.GetNormalizedAbsolutePath(config.configFileName, basePath) - path := tspath.ToPath(config.configFileName, basePath, host.FS().UseCaseSensitiveFileNames()) - parsed, _ := tsoptions.ParseConfigFileTextToJson(configFileName, path, config.jsonText) - return tsoptions.ParseJsonConfigFileContent( - parsed, - host, - basePath, - nil, - configFileName, - /*resolutionStack*/ nil, - /*extraFileExtensions*/ nil, - /*extendedConfigCache*/ nil, - ) -} - -func TestParseJsonSourceFileConfigFileContent(t *testing.T) { - t.Parallel() - repo.SkipIfNoTypeScriptSubmodule(t) - for _, rec := range parseJsonConfigFileTests { - t.Run(rec.title+" with jsonSourceFile api", func(t *testing.T) { - t.Parallel() - baselineParseConfigWith(t, rec.title+" with jsonSourceFile api.js", rec.noSubmoduleBaseline, rec.input, getParsedWithJsonSourceFileApi) - }) - } -} - -func getParsedWithJsonSourceFileApi(config testConfig, host tsoptions.ParseConfigHost, basePath string) *tsoptions.ParsedCommandLine { - configFileName := tspath.GetNormalizedAbsolutePath(config.configFileName, basePath) - path := tspath.ToPath(config.configFileName, basePath, host.FS().UseCaseSensitiveFileNames()) - parsed := parser.ParseSourceFile(ast.SourceFileParseOptions{ - FileName: configFileName, - Path: path, - }, config.jsonText, core.ScriptKindJSON) - tsConfigSourceFile := &tsoptions.TsConfigSourceFile{ - SourceFile: parsed, - } - return tsoptions.ParseJsonSourceFileConfigFileContent( - tsConfigSourceFile, - host, - host.GetCurrentDirectory(), - nil, - configFileName, - /*resolutionStack*/ nil, - /*extraFileExtensions*/ nil, - /*extendedConfigCache*/ nil, - ) -} - -func baselineParseConfigWith(t *testing.T, baselineFileName string, noSubmoduleBaseline bool, input []testConfig, getParsed func(config testConfig, host tsoptions.ParseConfigHost, basePath string) *tsoptions.ParsedCommandLine) { - noSubmoduleBaseline = true - var baselineContent strings.Builder - for i, config := range input { - basePath := config.basePath - if basePath == "" { - basePath = tspath.GetNormalizedAbsolutePath(tspath.GetDirectoryPath(config.configFileName), "") - } - configFileName := tspath.CombinePaths(basePath, config.configFileName) - allFileLists := make(map[string]string, len(config.allFileList)+1) - for file, content := range config.allFileList { - allFileLists[file] = content - } - allFileLists[configFileName] = config.jsonText - host := tsoptionstest.NewVFSParseConfigHost(allFileLists, config.basePath, true /*useCaseSensitiveFileNames*/) - parsedConfigFileContent := getParsed(config, host, basePath) - - baselineContent.WriteString("Fs::\n") - if err := printFS(&baselineContent, host.FS(), "/"); err != nil { - t.Fatal(err) - } - baselineContent.WriteString("\n") - baselineContent.WriteString("configFileName:: " + config.configFileName + "\n") - if noSubmoduleBaseline { - baselineContent.WriteString("CompilerOptions::\n") - assert.NilError(t, jsonutil.MarshalIndentWrite(&baselineContent, parsedConfigFileContent.ParsedConfig.CompilerOptions, "", " ")) - baselineContent.WriteString("\n") - baselineContent.WriteString("\n") - - if parsedConfigFileContent.ParsedConfig.TypeAcquisition != nil { - baselineContent.WriteString("TypeAcquisition::\n") - assert.NilError(t, jsonutil.MarshalIndentWrite(&baselineContent, parsedConfigFileContent.ParsedConfig.TypeAcquisition, "", " ")) - baselineContent.WriteString("\n") - baselineContent.WriteString("\n") - } - } - baselineContent.WriteString("FileNames::\n") - baselineContent.WriteString(strings.Join(parsedConfigFileContent.ParsedConfig.FileNames, ",") + "\n") - baselineContent.WriteString("Errors::\n") - diagnosticwriter.FormatDiagnosticsWithColorAndContext(&baselineContent, parsedConfigFileContent.Errors, &diagnosticwriter.FormattingOptions{ - NewLine: "\r\n", - ComparePathsOptions: tspath.ComparePathsOptions{ - CurrentDirectory: basePath, - UseCaseSensitiveFileNames: true, - }, - }) - baselineContent.WriteString("\n") - if i != len(input)-1 { - baselineContent.WriteString("\n") - } - } - if noSubmoduleBaseline { - baseline.Run(t, baselineFileName, baselineContent.String(), baseline.Options{Subfolder: "config/tsconfigParsing"}) - } else { - baseline.RunAgainstSubmodule(t, baselineFileName, baselineContent.String(), baseline.Options{Subfolder: "config/tsconfigParsing"}) - } -} - -func writeJsonReadableText(output io.Writer, input any) error { - return jsonutil.MarshalIndentWrite(output, input, "", " ") -} - -func TestParseTypeAcquisition(t *testing.T) { - t.Parallel() - // repo.SkipIfNoTypeScriptSubmodule(t) - cases := []struct { - title string - configName string - config string - }{ - { - title: "Convert correctly format tsconfig.json to typeAcquisition ", - config: `{ - "typeAcquisition": { - "enable": true, - "include": ["0.d.ts", "1.d.ts"], - "exclude": ["0.js", "1.js"], - }, -}`, - configName: "tsconfig.json", - }, - { - title: "Convert incorrect format tsconfig.json to typeAcquisition ", - config: `{ - "typeAcquisition": { - "enableAutoDiscovy": true, - } -}`, configName: "tsconfig.json", - }, - { - title: "Convert default tsconfig.json to typeAcquisition ", - config: `{}`, configName: "tsconfig.json", - }, - { - title: "Convert tsconfig.json with only enable property to typeAcquisition ", - config: `{ - "typeAcquisition": { - "enable": true, - }, -}`, configName: "tsconfig.json", - }, - - // jsconfig.json - { - title: "Convert jsconfig.json to typeAcquisition ", - config: `{ - "typeAcquisition": { - "enable": false, - "include": ["0.d.ts"], - "exclude": ["0.js"], - }, -}`, - configName: "jsconfig.json", - }, - {title: "Convert default jsconfig.json to typeAcquisition ", config: `{}`, configName: "jsconfig.json"}, - { - title: "Convert incorrect format jsconfig.json to typeAcquisition ", - config: `{ - "typeAcquisition": { - "enableAutoDiscovy": true, - }, -}`, - configName: "jsconfig.json", - }, - { - title: "Convert jsconfig.json with only enable property to typeAcquisition ", - config: `{ - "typeAcquisition": { - "enable": false, - }, -}`, - configName: "jsconfig.json", - }, - } - for _, test := range cases { - withJsonApiName := test.title + " with json api" - input := []testConfig{ - { - jsonText: test.config, - configFileName: test.configName, - basePath: "/apath", - allFileList: map[string]string{ - "/apath/a.ts": "", - "/apath/b.ts": "", - }, - }, - } - t.Run(withJsonApiName, func(t *testing.T) { - t.Parallel() - baselineParseConfigWith(t, withJsonApiName+".js", true, input, getParsedWithJsonApi) - }) - withJsonSourceFileApiName := test.title + " with jsonSourceFile api" - t.Run(withJsonSourceFileApiName, func(t *testing.T) { - t.Parallel() - baselineParseConfigWith(t, withJsonSourceFileApiName+".js", true, input, getParsedWithJsonSourceFileApi) - }) - } -} - -func printFS(output io.Writer, files vfs.FS, root string) error { - return files.WalkDir(root, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.Type().IsRegular() { - if content, ok := files.ReadFile(path); !ok { - return fmt.Errorf("failed to read file %s", path) - } else { - if _, err := fmt.Fprintf(output, "//// [%s]\r\n%s\r\n\r\n", path, content); err != nil { - return err - } - } - } - return nil - }) -} - -func TestParseSrcCompiler(t *testing.T) { - t.Parallel() - - repo.SkipIfNoTypeScriptSubmodule(t) - - compilerDir := tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "src", "compiler")) - tsconfigFileName := tspath.CombinePaths(compilerDir, "tsconfig.json") - - fs := osvfs.FS() - host := &tsoptionstest.VfsParseConfigHost{ - Vfs: fs, - CurrentDirectory: compilerDir, - } - - jsonText, ok := fs.ReadFile(tsconfigFileName) - assert.Assert(t, ok) - tsconfigPath := tspath.ToPath(tsconfigFileName, compilerDir, fs.UseCaseSensitiveFileNames()) - parsed := parser.ParseSourceFile(ast.SourceFileParseOptions{ - FileName: tsconfigFileName, - Path: tsconfigPath, - }, jsonText, core.ScriptKindJSON) - - if len(parsed.Diagnostics()) > 0 { - for _, error := range parsed.Diagnostics() { - t.Log(error.Message()) - } - t.FailNow() - } - - tsConfigSourceFile := &tsoptions.TsConfigSourceFile{ - SourceFile: parsed, - } - - parseConfigFileContent := tsoptions.ParseJsonSourceFileConfigFileContent( - tsConfigSourceFile, - host, - host.GetCurrentDirectory(), - nil, - tsconfigFileName, - /*resolutionStack*/ nil, - /*extraFileExtensions*/ nil, - /*extendedConfigCache*/ nil, - ) - - if len(parseConfigFileContent.Errors) > 0 { - for _, error := range parseConfigFileContent.Errors { - t.Log(error.Message()) - } - t.FailNow() - } - - opts := parseConfigFileContent.CompilerOptions() - assert.DeepEqual(t, opts, &core.CompilerOptions{ - Lib: []string{"lib.es2020.d.ts"}, - Module: core.ModuleKindNodeNext, - ModuleResolution: core.ModuleResolutionKindNodeNext, - NewLine: core.NewLineKindLF, - OutDir: tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "built", "local")), - Target: core.ScriptTargetES2020, - Types: []string{"node"}, - ConfigFilePath: tsconfigFileName, - Declaration: core.TSTrue, - DeclarationMap: core.TSTrue, - EmitDeclarationOnly: core.TSTrue, - AlwaysStrict: core.TSTrue, - Composite: core.TSTrue, - IsolatedDeclarations: core.TSTrue, - NoImplicitOverride: core.TSTrue, - PreserveConstEnums: core.TSTrue, - RootDir: tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "src")), - SkipLibCheck: core.TSTrue, - Strict: core.TSTrue, - StrictBindCallApply: core.TSFalse, - SourceMap: core.TSTrue, - UseUnknownInCatchVariables: core.TSFalse, - Pretty: core.TSTrue, - }, cmpopts.IgnoreUnexported(core.CompilerOptions{})) - - fileNames := parseConfigFileContent.ParsedConfig.FileNames - relativePaths := make([]string, 0, len(fileNames)) - for _, fileName := range fileNames { - if strings.Contains(fileName, ".generated.") { - continue - } - - relativePaths = append(relativePaths, tspath.ConvertToRelativePath(fileName, tspath.ComparePathsOptions{ - CurrentDirectory: compilerDir, - UseCaseSensitiveFileNames: fs.UseCaseSensitiveFileNames(), - })) - } - - assert.DeepEqual(t, relativePaths, []string{ - "binder.ts", - "builder.ts", - "builderPublic.ts", - "builderState.ts", - "builderStatePublic.ts", - "checker.ts", - "commandLineParser.ts", - "core.ts", - "corePublic.ts", - "debug.ts", - "emitter.ts", - "executeCommandLine.ts", - "expressionToTypeNode.ts", - "moduleNameResolver.ts", - "moduleSpecifiers.ts", - "parser.ts", - "path.ts", - "performance.ts", - "performanceCore.ts", - "program.ts", - "programDiagnostics.ts", - "resolutionCache.ts", - "scanner.ts", - "semver.ts", - "sourcemap.ts", - "symbolWalker.ts", - "sys.ts", - "tracing.ts", - "transformer.ts", - "tsbuild.ts", - "tsbuildPublic.ts", - "types.ts", - "utilities.ts", - "utilitiesPublic.ts", - "visitorPublic.ts", - "watch.ts", - "watchPublic.ts", - "watchUtilities.ts", - "_namespaces/ts.moduleSpecifiers.ts", - "_namespaces/ts.performance.ts", - "_namespaces/ts.ts", - "factory/baseNodeFactory.ts", - "factory/emitHelpers.ts", - "factory/emitNode.ts", - "factory/nodeChildren.ts", - "factory/nodeConverters.ts", - "factory/nodeFactory.ts", - "factory/nodeTests.ts", - "factory/parenthesizerRules.ts", - "factory/utilities.ts", - "factory/utilitiesPublic.ts", - "transformers/classFields.ts", - "transformers/classThis.ts", - "transformers/declarations.ts", - "transformers/destructuring.ts", - "transformers/es2016.ts", - "transformers/es2017.ts", - "transformers/es2018.ts", - "transformers/es2019.ts", - "transformers/es2020.ts", - "transformers/es2021.ts", - "transformers/esDecorators.ts", - "transformers/esnext.ts", - "transformers/jsx.ts", - "transformers/legacyDecorators.ts", - "transformers/namedEvaluation.ts", - "transformers/taggedTemplate.ts", - "transformers/ts.ts", - "transformers/typeSerializer.ts", - "transformers/utilities.ts", - "transformers/declarations/diagnostics.ts", - "transformers/module/esnextAnd2015.ts", - "transformers/module/impliedNodeFormatDependent.ts", - "transformers/module/module.ts", - "transformers/module/system.ts", - }) -} - -func BenchmarkParseSrcCompiler(b *testing.B) { - repo.SkipIfNoTypeScriptSubmodule(b) - - compilerDir := tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "src", "compiler")) - tsconfigFileName := tspath.CombinePaths(compilerDir, "tsconfig.json") - - fs := osvfs.FS() - host := &tsoptionstest.VfsParseConfigHost{ - Vfs: fs, - CurrentDirectory: compilerDir, - } - - jsonText, ok := fs.ReadFile(tsconfigFileName) - assert.Assert(b, ok) - tsconfigPath := tspath.ToPath(tsconfigFileName, compilerDir, fs.UseCaseSensitiveFileNames()) - parsed := parser.ParseSourceFile(ast.SourceFileParseOptions{ - FileName: tsconfigFileName, - Path: tsconfigPath, - }, jsonText, core.ScriptKindJSON) - - b.ReportAllocs() - - for b.Loop() { - tsoptions.ParseJsonSourceFileConfigFileContent( - &tsoptions.TsConfigSourceFile{ - SourceFile: parsed, - }, - host, - host.GetCurrentDirectory(), - nil, - tsconfigFileName, - /*resolutionStack*/ nil, - /*extraFileExtensions*/ nil, - /*extendedConfigCache*/ nil, - ) - } -} - -// memoCache is a minimal memoizing ExtendedConfigCache used by tests to simulate -// cache hits across multiple parses of configs that extend a common base. -type memoCache struct { - m map[tspath.Path]*tsoptions.ExtendedConfigCacheEntry -} - -func (mc *memoCache) GetExtendedConfig(fileName string, path tspath.Path, parse func() *tsoptions.ExtendedConfigCacheEntry) *tsoptions.ExtendedConfigCacheEntry { - if mc.m == nil { - mc.m = make(map[tspath.Path]*tsoptions.ExtendedConfigCacheEntry) - } - if e, ok := mc.m[path]; ok { - return e - } - e := parse() - mc.m[path] = e - return e -} - -var _ tsoptions.ExtendedConfigCache = (*memoCache)(nil) - -// TestExtendedConfigErrorsAppearOnCacheHit verifies that diagnostics produced while parsing an -// extended config are still reported when the extended config comes from the cache. -func TestExtendedConfigErrorsAppearOnCacheHit(t *testing.T) { - t.Parallel() - - t.Run("single config parsed twice", func(t *testing.T) { - t.Parallel() - files := map[string]string{ - "/tsconfig.json": `{ - "extends": "./base.json" -}`, - // 'excludes' instead of 'exclude' triggers diagnostic - "/base.json": `{ - "excludes": ["**/*.ts"] -}`, - "/app.ts": "export {}", - } - - host := tsoptionstest.NewVFSParseConfigHost(files, "/", true /*useCaseSensitiveFileNames*/) - - parseConfig := func(configFileName string, cache tsoptions.ExtendedConfigCache) *tsoptions.ParsedCommandLine { - cfgPath := tspath.ToPath(configFileName, host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames()) - jsonText, ok := host.FS().ReadFile(configFileName) - assert.Assert(t, ok, "missing %s in test fs", configFileName) - tsConfigSourceFile := &tsoptions.TsConfigSourceFile{ - SourceFile: parser.ParseSourceFile(ast.SourceFileParseOptions{FileName: configFileName, Path: cfgPath}, jsonText, core.ScriptKindJSON), - } - return tsoptions.ParseJsonSourceFileConfigFileContent( - tsConfigSourceFile, - host, - host.GetCurrentDirectory(), - nil, - configFileName, - nil, - nil, - cache, - ) - } - - cache := &memoCache{} - first := parseConfig("/tsconfig.json", cache) - assert.Assert(t, len(first.Errors) > 0, "expected diagnostics on first parse, got 0") - second := parseConfig("/tsconfig.json", cache) - assert.Assert(t, len(second.Errors) > 0, "expected diagnostics on second parse (cache hit), got 0") - }) - - t.Run("two configs share same base", func(t *testing.T) { - t.Parallel() - files := map[string]string{ - "/base.json": `{ - "excludes": ["**/*.ts"] -}`, - "/projA/tsconfig.json": `{ - "extends": "../base.json" -}`, - "/projB/tsconfig.json": `{ - "extends": "../base.json" -}`, - "/projA/app.ts": "export {}", - "/projB/app.ts": "export {}", - } - - host := tsoptionstest.NewVFSParseConfigHost(files, "/", true /*useCaseSensitiveFileNames*/) - - parseConfig := func(configFileName string, cache tsoptions.ExtendedConfigCache) *tsoptions.ParsedCommandLine { - cfgPath := tspath.ToPath(configFileName, host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames()) - jsonText, ok := host.FS().ReadFile(configFileName) - assert.Assert(t, ok, "missing %s in test fs", configFileName) - tsConfigSourceFile := &tsoptions.TsConfigSourceFile{ - SourceFile: parser.ParseSourceFile(ast.SourceFileParseOptions{FileName: configFileName, Path: cfgPath}, jsonText, core.ScriptKindJSON), - } - return tsoptions.ParseJsonSourceFileConfigFileContent( - tsConfigSourceFile, - host, - host.GetCurrentDirectory(), - nil, - configFileName, - nil, - nil, - cache, - ) - } - - cache := &memoCache{} - first := parseConfig("/projA/tsconfig.json", cache) - assert.Assert(t, len(first.Errors) > 0, "expected diagnostics for projA parse, got 0") - second := parseConfig("/projB/tsconfig.json", cache) - assert.Assert(t, len(second.Errors) > 0, "expected diagnostics for projB parse (cache hit on base), got 0") - }) -} diff --git a/kitcom/internal/tsgo/tsoptions/tsoptionstest/parsedcommandline.go b/kitcom/internal/tsgo/tsoptions/tsoptionstest/parsedcommandline.go deleted file mode 100644 index aacbfef..0000000 --- a/kitcom/internal/tsgo/tsoptions/tsoptionstest/parsedcommandline.go +++ /dev/null @@ -1,14 +0,0 @@ -package tsoptionstest - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "gotest.tools/v3/assert" -) - -func GetParsedCommandLine(t assert.TestingT, jsonText string, files map[string]string, currentDirectory string, useCaseSensitiveFileNames bool) *tsoptions.ParsedCommandLine { - host := NewVFSParseConfigHost(files, currentDirectory, useCaseSensitiveFileNames) - configFileName := tspath.CombinePaths(currentDirectory, "tsconfig.json") - tsconfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath(configFileName, tspath.ToPath(configFileName, currentDirectory, useCaseSensitiveFileNames), jsonText) - return tsoptions.ParseJsonSourceFileConfigFileContent(tsconfigSourceFile, host, currentDirectory, nil, configFileName, nil, nil, nil) -} diff --git a/kitcom/internal/tsgo/tsoptions/tsoptionstest/vfsparseconfighost.go b/kitcom/internal/tsgo/tsoptions/tsoptionstest/vfsparseconfighost.go deleted file mode 100644 index 628b6dc..0000000 --- a/kitcom/internal/tsgo/tsoptions/tsoptionstest/vfsparseconfighost.go +++ /dev/null @@ -1,41 +0,0 @@ -package tsoptionstest - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/vfstest" -) - -func fixRoot(path string) string { - rootLength := tspath.GetRootLength(path) - if rootLength == 0 { - return path - } - if len(path) == rootLength { - return "." - } - return path[rootLength:] -} - -type VfsParseConfigHost struct { - Vfs vfs.FS - CurrentDirectory string -} - -var _ tsoptions.ParseConfigHost = (*VfsParseConfigHost)(nil) - -func (h *VfsParseConfigHost) FS() vfs.FS { - return h.Vfs -} - -func (h *VfsParseConfigHost) GetCurrentDirectory() string { - return h.CurrentDirectory -} - -func NewVFSParseConfigHost(files map[string]string, currentDirectory string, useCaseSensitiveFileNames bool) *VfsParseConfigHost { - return &VfsParseConfigHost{ - Vfs: vfstest.FromMap(files, useCaseSensitiveFileNames), - CurrentDirectory: currentDirectory, - } -} diff --git a/kitcom/internal/tsgo/tsoptions/wildcarddirectories.go b/kitcom/internal/tsgo/tsoptions/wildcarddirectories.go deleted file mode 100644 index 5b0ef33..0000000 --- a/kitcom/internal/tsgo/tsoptions/wildcarddirectories.go +++ /dev/null @@ -1,144 +0,0 @@ -package tsoptions - -import ( - "regexp" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" - "github.com/dlclark/regexp2" -) - -func getWildcardDirectories(include []string, exclude []string, comparePathsOptions tspath.ComparePathsOptions) map[string]bool { - // We watch a directory recursively if it contains a wildcard anywhere in a directory segment - // of the pattern: - // - // /a/b/**/d - Watch /a/b recursively to catch changes to any d in any subfolder recursively - // /a/b/*/d - Watch /a/b recursively to catch any d in any immediate subfolder, even if a new subfolder is added - // /a/b - Watch /a/b recursively to catch changes to anything in any recursive subfoler - // - // We watch a directory without recursion if it contains a wildcard in the file segment of - // the pattern: - // - // /a/b/* - Watch /a/b directly to catch any new file - // /a/b/a?z - Watch /a/b directly to catch any new file matching a?z - - if len(include) == 0 { - return nil - } - - rawExcludeRegex := vfs.GetRegularExpressionForWildcard(exclude, comparePathsOptions.CurrentDirectory, "exclude") - var excludeRegex *regexp.Regexp - if rawExcludeRegex != "" { - options := "" - if !comparePathsOptions.UseCaseSensitiveFileNames { - options = "(?i)" - } - excludeRegex = regexp.MustCompile(options + rawExcludeRegex) - } - - wildcardDirectories := make(map[string]bool) - wildCardKeyToPath := make(map[string]string) - - var recursiveKeys []string - - for _, file := range include { - spec := tspath.NormalizeSlashes(tspath.CombinePaths(comparePathsOptions.CurrentDirectory, file)) - if excludeRegex != nil && excludeRegex.MatchString(spec) { - continue - } - - match := getWildcardDirectoryFromSpec(spec, comparePathsOptions.UseCaseSensitiveFileNames) - if match != nil { - key := match.Key - path := match.Path - recursive := match.Recursive - - existingPath, existsPath := wildCardKeyToPath[key] - var existingRecursive bool - - if existsPath { - existingRecursive = wildcardDirectories[existingPath] - } - - if !existsPath || (!existingRecursive && recursive) { - pathToUse := path - if existsPath { - pathToUse = existingPath - } - wildcardDirectories[pathToUse] = recursive - - if !existsPath { - wildCardKeyToPath[key] = path - } - - if recursive { - recursiveKeys = append(recursiveKeys, key) - } - } - } - - // Remove any subpaths under an existing recursively watched directory - for path := range wildcardDirectories { - for _, recursiveKey := range recursiveKeys { - key := toCanonicalKey(path, comparePathsOptions.UseCaseSensitiveFileNames) - if key != recursiveKey && tspath.ContainsPath(recursiveKey, key, comparePathsOptions) { - delete(wildcardDirectories, path) - } - } - } - } - - return wildcardDirectories -} - -func toCanonicalKey(path string, useCaseSensitiveFileNames bool) string { - if useCaseSensitiveFileNames { - return path - } - return strings.ToLower(path) -} - -// wildcardDirectoryPattern matches paths with wildcard characters -var wildcardDirectoryPattern = regexp2.MustCompile(`^[^*?]*(?=\/[^/]*[*?])`, 0) - -// wildcardDirectoryMatch represents the result of a wildcard directory match -type wildcardDirectoryMatch struct { - Key string - Path string - Recursive bool -} - -func getWildcardDirectoryFromSpec(spec string, useCaseSensitiveFileNames bool) *wildcardDirectoryMatch { - match, _ := wildcardDirectoryPattern.FindStringMatch(spec) - if match != nil { - // We check this with a few `Index` calls because it's more efficient than complex regex - questionWildcardIndex := strings.Index(spec, "?") - starWildcardIndex := strings.Index(spec, "*") - lastDirectorySeparatorIndex := strings.LastIndexByte(spec, tspath.DirectorySeparator) - - // Determine if this should be watched recursively - recursive := (questionWildcardIndex != -1 && questionWildcardIndex < lastDirectorySeparatorIndex) || - (starWildcardIndex != -1 && starWildcardIndex < lastDirectorySeparatorIndex) - - return &wildcardDirectoryMatch{ - Key: toCanonicalKey(match.String(), useCaseSensitiveFileNames), - Path: match.String(), - Recursive: recursive, - } - } - - if lastSepIndex := strings.LastIndexByte(spec, tspath.DirectorySeparator); lastSepIndex != -1 { - lastSegment := spec[lastSepIndex+1:] - if vfs.IsImplicitGlob(lastSegment) { - path := tspath.RemoveTrailingDirectorySeparator(spec) - return &wildcardDirectoryMatch{ - Key: toCanonicalKey(path, useCaseSensitiveFileNames), - Path: path, - Recursive: true, - } - } - } - - return nil -}