Skip to content

Commit e32aa05

Browse files
authored
parse enums correctly when converting to compilerOptions struct (microsoft#383)
1 parent b49d2d6 commit e32aa05

File tree

7 files changed

+362
-77
lines changed

7 files changed

+362
-77
lines changed

internal/execute/tsc_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ func TestTsc(t *testing.T) {
7272
for _, testCase := range testCases {
7373
testCase.verify(t)
7474
}
75+
76+
(&tscInput{
77+
scenario: "commandLine",
78+
subScenario: "Parse --lib option with file name",
79+
sys: newTestSys(FileMap{"/home/src/workspaces/project/first.ts": `export const Key = Symbol()`}, ""),
80+
commandLineArgs: []string{"--lib", "es6 ", "first.ts"},
81+
}).verify(t)
82+
83+
(&tscInput{
84+
scenario: "commandLine",
85+
subScenario: "Parse enum type options",
86+
sys: newTestSys(nil, ""),
87+
commandLineArgs: []string{"--moduleResolution", "nodenext ", "first.ts", "--module", "nodenext", "--target", "esnext", "--moduleDetection", "auto", "--jsx", "react", "--newLine", "crlf"},
88+
}).verify(t)
7589
}
7690

7791
func TestNoEmit(t *testing.T) {

internal/tsoptions/commandlineparser.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,17 @@ func ParseCommandLine(
4646
}
4747
parser := parseCommandLineWorker(CompilerOptionsDidYouMeanDiagnostics, commandLine, host.FS())
4848
optionsWithAbsolutePaths := convertToOptionsWithAbsolutePaths(parser.options, commandLineCompilerOptionsMap, host.GetCurrentDirectory())
49-
compilerOptions, d1 := convertOptionsFromJson(commandLineCompilerOptionsMap, optionsWithAbsolutePaths, host.GetCurrentDirectory(), &compilerOptionsParser{&core.CompilerOptions{}, true})
50-
watchOptions, d2 := convertOptionsFromJson(commandLineCompilerOptionsMap, optionsWithAbsolutePaths, host.GetCurrentDirectory(), &watchOptionsParser{&core.WatchOptions{}, true})
49+
compilerOptions := convertMapToOptions(commandLineCompilerOptionsMap, optionsWithAbsolutePaths, host.GetCurrentDirectory(), &compilerOptionsParser{&core.CompilerOptions{}}).CompilerOptions
50+
watchOptions := convertMapToOptions(commandLineCompilerOptionsMap, optionsWithAbsolutePaths, host.GetCurrentDirectory(), &watchOptionsParser{&core.WatchOptions{}}).WatchOptions
5151
return &ParsedCommandLine{
5252
ParsedConfig: &core.ParsedOptions{
53-
CompilerOptions: compilerOptions.CompilerOptions,
54-
WatchOptions: watchOptions.WatchOptions,
53+
CompilerOptions: compilerOptions,
54+
WatchOptions: watchOptions,
5555
FileNames: parser.fileNames,
5656
},
5757
ConfigFile: nil,
58-
Errors: append(append(parser.errors, d1...), d2...),
59-
Raw: parser.options, // todo: keep optionsBase incase needed later
58+
Errors: parser.errors,
59+
Raw: parser.options, // !!! keep optionsBase incase needed later. todo: figure out if this is still needed
6060
CompileOnSave: nil,
6161
}
6262
}
@@ -301,8 +301,8 @@ func ParseListTypeOption(opt *CommandLineOption, value string) ([]string, []*ast
301301
case "string":
302302
elements := core.Filter(core.Map(values, func(v string) string {
303303
val, err := validateJsonOptionValue(opt.Elements(), v, nil, nil)
304-
if _, ok := val.(string); ok {
305-
return val.(string)
304+
if s, ok := val.(string); ok && len(err) == 0 && s != "" {
305+
return s
306306
}
307307
errors = append(errors, err...)
308308
return ""
@@ -315,8 +315,8 @@ func ParseListTypeOption(opt *CommandLineOption, value string) ([]string, []*ast
315315
default:
316316
result := core.Filter(core.Map(values, func(v string) string {
317317
val, err := convertJsonOptionOfEnumType(opt.Elements(), strings.TrimFunc(v, stringutil.IsWhiteSpaceLike), nil, nil)
318-
if _, ok := val.(string); ok {
319-
return val.(string)
318+
if s, ok := val.(string); ok && len(err) == 0 && s != "" {
319+
return s
320320
}
321321
errors = append(errors, err...)
322322
return ""

internal/tsoptions/enummaps.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,17 @@ var libMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, an
102102
{Key: "decorators.legacy", Value: "lib.decorators.legacy.d.ts"},
103103
})
104104

105-
var Libs = slices.Collect(libMap.Keys())
105+
var (
106+
Libs = slices.Collect(libMap.Keys())
107+
LibFilesSet = core.NewSetFromItems(core.Map(slices.Collect(libMap.Values()), func(s any) string { return s.(string) })...)
108+
)
106109

107110
func GetLibFileName(libName string) (string, bool) {
111+
// checks if the libName is a valid lib name or file name and converts the lib name to the filename if needed
108112
libName = tspath.ToFileNameLowerCase(libName)
113+
if LibFilesSet.Has(libName) {
114+
return libName, true
115+
}
109116
lib, ok := libMap.Get(libName)
110117
if !ok {
111118
return "", false

internal/tsoptions/parsinghelpers.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ func parseTristate(value any) core.Tristate {
1313
if value == nil {
1414
return core.TSUnknown
1515
}
16+
if v, ok := value.(core.Tristate); ok {
17+
return v
18+
}
1619
if value == true {
1720
return core.TSTrue
1821
} else {
@@ -128,32 +131,28 @@ func parseJsonToStringKey(json any) *collections.OrderedMap[string, any] {
128131

129132
type optionParser interface {
130133
ParseOption(key string, value any) []*ast.Diagnostic
131-
CommandLine() bool
132134
}
133135

134136
type compilerOptionsParser struct {
135137
*core.CompilerOptions
136-
commandLine bool
137138
}
138139

139140
func (o *compilerOptionsParser) ParseOption(key string, value any) []*ast.Diagnostic {
140141
return ParseCompilerOptions(key, value, o.CompilerOptions)
141142
}
142143

143-
func (o *compilerOptionsParser) CommandLine() bool { return o.commandLine }
144-
145144
type watchOptionsParser struct {
146145
*core.WatchOptions
147-
commandLine bool
148146
}
149147

150148
func (o *watchOptionsParser) ParseOption(key string, value any) []*ast.Diagnostic {
151149
return ParseWatchOptions(key, value, o.WatchOptions)
152150
}
153151

154-
func (o *watchOptionsParser) CommandLine() bool { return o.commandLine }
155-
156152
func ParseCompilerOptions(key string, value any, allOptions *core.CompilerOptions) []*ast.Diagnostic {
153+
if value == nil {
154+
return nil
155+
}
157156
if allOptions == nil {
158157
return nil
159158
}
@@ -400,11 +399,17 @@ func ParseWatchOptions(key string, value any, allOptions *core.WatchOptions) []*
400399
}
401400
switch key {
402401
case "watchFile":
403-
allOptions.FileKind = value.(core.WatchFileKind)
402+
if value != nil {
403+
allOptions.FileKind = value.(core.WatchFileKind)
404+
}
404405
case "watchDirectory":
405-
allOptions.DirectoryKind = value.(core.WatchDirectoryKind)
406+
if value != nil {
407+
allOptions.DirectoryKind = value.(core.WatchDirectoryKind)
408+
}
406409
case "fallbackPolling":
407-
allOptions.FallbackPolling = value.(core.PollingKind)
410+
if value != nil {
411+
allOptions.FallbackPolling = value.(core.PollingKind)
412+
}
408413
case "synchronousWatchDirectory":
409414
allOptions.SyncWatchDir = parseTristate(value)
410415
case "excludeDirectories":

internal/tsoptions/tsconfigparsing.go

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func parseOwnConfigOfJsonSourceFile(
135135
// Ensure value is verified except for extends which is handled in its own way for error reporting
136136
var propertySetErrors []*ast.Diagnostic
137137
if option != nil && option != extendsOptionDeclaration {
138-
value, propertySetErrors = convertTsConfigJsonOption(option, value, basePath, propertyAssignment, propertyAssignment.Initializer, sourceFile)
138+
value, propertySetErrors = convertJsonOption(option, value, basePath, propertyAssignment, propertyAssignment.Initializer, sourceFile)
139139
}
140140
if parentOption != nil && parentOption.Name != "undefined" && value != nil {
141141
if option != nil && option.Name != "" {
@@ -317,7 +317,7 @@ func convertJsonOptionOfListType(
317317
if valueExpression != nil {
318318
expression = valueExpression.AsArrayLiteralExpression().Elements.Nodes[index]
319319
}
320-
result, err := convertTsConfigJsonOption(option.Elements(), v, basePath, propertyAssignment, expression, sourceFile)
320+
result, err := convertJsonOption(option.Elements(), v, basePath, propertyAssignment, expression, sourceFile)
321321
errors = append(errors, err...)
322322
return result
323323
})
@@ -355,7 +355,7 @@ func normalizeNonListOptionValue(option *CommandLineOption, basePath string, val
355355
return value
356356
}
357357

358-
func convertTsConfigJsonOption(
358+
func convertJsonOption(
359359
opt *CommandLineOption,
360360
value any,
361361
basePath string,
@@ -374,45 +374,27 @@ func convertTsConfigJsonOption(
374374
return nil, []*ast.Diagnostic{createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, nodeValue, diagnostics.Option_0_can_only_be_specified_on_command_line, opt.Name)}
375375
}
376376
}
377-
return convertJsonOption(opt, value, basePath, propertyAssignment, valueExpression, sourceFile)
378-
}
379-
380-
func convertJsonOption(
381-
opt *CommandLineOption,
382-
value any,
383-
basePath string,
384-
propertyAssignment *ast.PropertyAssignment,
385-
valueExpression *ast.Expression,
386-
sourceFile *ast.SourceFile,
387-
) (any, []*ast.Diagnostic) {
388-
var errors []*ast.Diagnostic
389377
if isCompilerOptionsValue(opt, value) {
390378
optType := opt.Kind
391379
if optType == "list" {
392-
list, err := convertJsonOptionOfListType(opt, value, basePath, propertyAssignment, valueExpression, sourceFile) // as ArrayLiteralExpression | undefined
393-
return list, append(errors, err...)
380+
return convertJsonOptionOfListType(opt, value, basePath, propertyAssignment, valueExpression, sourceFile) // as ArrayLiteralExpression | undefined
394381
} else if optType == "listOrElement" {
395382
if reflect.TypeOf(value).Kind() == reflect.Slice {
396-
listOrElement, err := convertJsonOptionOfListType(opt, value, basePath, propertyAssignment, valueExpression, sourceFile)
397-
errors = append(errors, err...)
398-
return listOrElement, errors
383+
return convertJsonOptionOfListType(opt, value, basePath, propertyAssignment, valueExpression, sourceFile)
399384
} else {
400-
return convertTsConfigJsonOption(opt.Elements(), value, basePath, propertyAssignment, valueExpression, sourceFile)
385+
return convertJsonOption(opt.Elements(), value, basePath, propertyAssignment, valueExpression, sourceFile)
401386
}
402387
} else if !(reflect.TypeOf(optType).Kind() == reflect.String) {
403-
val, err := convertJsonOptionOfEnumType(opt, value.(string), valueExpression, sourceFile)
404-
return val, append(errors, err...)
388+
return convertJsonOptionOfEnumType(opt, value.(string), valueExpression, sourceFile)
405389
}
406-
validatedValue, err := validateJsonOptionValue(opt, value, valueExpression, sourceFile)
407-
errors = append(errors, err...)
408-
if err != nil {
390+
validatedValue, errors := validateJsonOptionValue(opt, value, valueExpression, sourceFile)
391+
if len(errors) > 0 || validatedValue == nil {
409392
return validatedValue, errors
410393
} else {
411394
return normalizeNonListOptionValue(opt, basePath, validatedValue), errors
412395
}
413396
} else {
414-
errors = append(errors, createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, valueExpression, diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.Name, getCompilerOptionValueTypeString(opt)))
415-
return nil, errors
397+
return nil, []*ast.Diagnostic{createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, valueExpression, diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.Name, getCompilerOptionValueTypeString(opt))}
416398
}
417399
}
418400

@@ -430,15 +412,15 @@ func getExtendsConfigPathOrArray(
430412
if configFileName != "" {
431413
newBase = directoryOfCombinedPath(configFileName, basePath)
432414
}
433-
434-
var errors []*ast.Diagnostic
435415
if reflect.TypeOf(value).Kind() == reflect.String {
436416
val, err := getExtendsConfigPath(value.(string), host, newBase, valueExpression, sourceFile)
437417
if val != "" {
438418
extendedConfigPathArray = append(extendedConfigPathArray, val)
439419
}
440-
errors = append(errors, err...)
441-
} else if reflect.TypeOf(value).Kind() == reflect.Slice {
420+
return extendedConfigPathArray, err
421+
}
422+
var errors []*ast.Diagnostic
423+
if reflect.TypeOf(value).Kind() == reflect.Slice {
442424
for index, fileName := range value.([]any) {
443425
var expression *ast.Expression = nil
444426
if valueExpression != nil {
@@ -451,13 +433,12 @@ func getExtendsConfigPathOrArray(
451433
}
452434
errors = append(errors, err...)
453435
} else {
454-
var err []*ast.Diagnostic
455-
_, err = convertTsConfigJsonOption(extendsOptionDeclaration.Elements(), value, basePath, propertyAssignment, expression, sourceFile)
436+
_, err := convertJsonOption(extendsOptionDeclaration.Elements(), value, basePath, propertyAssignment, expression, sourceFile)
456437
errors = append(errors, err...)
457438
}
458439
}
459440
} else {
460-
_, errors = convertTsConfigJsonOption(extendsOptionDeclaration, value, basePath, propertyAssignment, valueExpression, sourceFile)
441+
_, errors = convertJsonOption(extendsOptionDeclaration, value, basePath, propertyAssignment, valueExpression, sourceFile)
461442
}
462443
return extendedConfigPathArray, errors
463444
}
@@ -515,34 +496,41 @@ func commandLineOptionsToMap(options []*CommandLineOption) map[string]*CommandLi
515496

516497
var commandLineCompilerOptionsMap map[string]*CommandLineOption = commandLineOptionsToMap(OptionsDeclarations)
517498

499+
func convertMapToOptions[O optionParser](optionsNameMap map[string]*CommandLineOption, options *collections.OrderedMap[string, any], basePath string, result O) O {
500+
// this assumes any `key`, `value` pair in `options` will have `value` already be the correct type. this function should no error handling
501+
for key, value := range options.Entries() {
502+
result.ParseOption(key, value)
503+
}
504+
return result
505+
}
506+
518507
func convertOptionsFromJson[O optionParser](optionsNameMap map[string]*CommandLineOption, jsonOptions any, basePath string, result O) (O, []*ast.Diagnostic) {
519508
if jsonOptions == nil {
520509
return result, nil
521510
}
522-
convertOption := convertTsConfigJsonOption
523-
if result.CommandLine() {
524-
convertOption = convertJsonOption
511+
jsonMap, ok := jsonOptions.(*collections.OrderedMap[string, any])
512+
if !ok {
513+
// !!! probably should be an error
514+
return result, nil
525515
}
526516
var errors []*ast.Diagnostic
527-
if _, ok := jsonOptions.(*collections.OrderedMap[string, any]); ok {
528-
for key, value := range jsonOptions.(*collections.OrderedMap[string, any]).Entries() {
529-
opt, ok := optionsNameMap[key]
530-
commandLineOptionEnumMapVal := opt.EnumMap()
531-
if commandLineOptionEnumMapVal != nil {
532-
val, ok := commandLineOptionEnumMapVal.Get(strings.ToLower(value.(string)))
533-
if ok {
534-
errors = result.ParseOption(key, val)
535-
}
536-
} else if ok {
537-
convertJson, err := convertOption(opt, value, basePath, nil, nil, nil)
538-
errors = append(errors, err...)
539-
compilerOptionsErr := result.ParseOption(key, convertJson)
540-
errors = append(errors, compilerOptionsErr...)
517+
for key, value := range jsonMap.Entries() {
518+
opt, ok := optionsNameMap[key]
519+
commandLineOptionEnumMapVal := opt.EnumMap()
520+
if commandLineOptionEnumMapVal != nil {
521+
val, ok := commandLineOptionEnumMapVal.Get(strings.ToLower(value.(string)))
522+
if ok {
523+
errors = result.ParseOption(key, val)
541524
}
542-
// else {
543-
// errors.push(createUnknownOptionError(id, diagnostics));
544-
// }
525+
} else if ok {
526+
convertJson, err := convertJsonOption(opt, value, basePath, nil, nil, nil)
527+
errors = append(errors, err...)
528+
compilerOptionsErr := result.ParseOption(key, convertJson)
529+
errors = append(errors, compilerOptionsErr...)
545530
}
531+
// else {
532+
// errors.push(createUnknownOptionError(id, diagnostics));
533+
// }
546534
}
547535
return result, errors
548536
}
@@ -773,7 +761,7 @@ func getDefaultCompilerOptions(configFileName string) *core.CompilerOptions {
773761

774762
func convertCompilerOptionsFromJsonWorker(jsonOptions any, basePath string, configFileName string) (*core.CompilerOptions, []*ast.Diagnostic) {
775763
options := getDefaultCompilerOptions(configFileName)
776-
_, errors := convertOptionsFromJson(commandLineCompilerOptionsMap, jsonOptions, basePath, &compilerOptionsParser{options, false /*commandLine*/})
764+
_, errors := convertOptionsFromJson(commandLineCompilerOptionsMap, jsonOptions, basePath, &compilerOptionsParser{options})
777765
if configFileName != "" {
778766
options.ConfigFilePath = tspath.NormalizeSlashes(configFileName)
779767
}

0 commit comments

Comments
 (0)