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