remove unused packages

This commit is contained in:
Egor Aristov 2025-10-15 19:25:37 +03:00
parent e24ccad485
commit 031ee56d1e
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
23 changed files with 0 additions and 7605 deletions

View File

@ -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

View File

@ -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)}
}

View File

@ -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)
}
}
})
}

View File

@ -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)
}
}

View File

@ -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,
},
}

File diff suppressed because it is too large Load Diff

View File

@ -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,
},
}

View File

@ -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,
},
}

View File

@ -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,
}

View File

@ -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},
})

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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",
})
})
})
})
}

View File

@ -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
}

View File

@ -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)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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)
}

View File

@ -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,
}
}

View File

@ -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
}