remove unused packages
This commit is contained in:
parent
e24ccad485
commit
031ee56d1e
@ -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
|
||||
@ -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)}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
@ -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,
|
||||
},
|
||||
}
|
||||
@ -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,
|
||||
},
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
@ -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},
|
||||
})
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
@ -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)
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user