Skip to content

Port type acquisition parsing from tsconfig.json #852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/core/parsedoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package core
type ParsedOptions struct {
CompilerOptions *CompilerOptions `json:"compilerOptions"`
WatchOptions *WatchOptions `json:"watchOptions"`
TypeAcquisition *TypeAcquisition `json:"typeAcquisition"`

FileNames []string `json:"fileNames"`
ProjectReferences []ProjectReference `json:"projectReferences"`
Expand Down
8 changes: 8 additions & 0 deletions internal/core/typeacquisition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package core

type TypeAcquisition struct {
Enable Tristate `json:"enable,omitzero"`
Include []string `json:"include,omitzero"`
Exclude []string `json:"exclude,omitzero"`
DisableFilenameBasedTypeAcquisition Tristate `json:"disableFilenameBasedTypeAcquisition,omitzero"`
}
27 changes: 27 additions & 0 deletions internal/execute/tsc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,30 @@ func TestExtends(t *testing.T) {
c.verify(t, "extends")
}
}

func TestTypeAcquisition(t *testing.T) {
t.Parallel()
if !bundled.Embedded {
// Without embedding, we'd need to read all of the lib files out from disk into the MapFS.
// Just skip this for now.
t.Skip("bundled files are not embedded")
}
(&tscInput{
subScenario: "parse tsconfig with typeAcquisition",
sys: newTestSys(FileMap{"/home/src/workspaces/project/tsconfig.json": `{
"compilerOptions": {
"composite": true,
"noEmit": true,
},
"typeAcquisition": {
"enable": true,
"include": ["0.d.ts", "1.d.ts"],
"exclude": ["0.js", "1.js"],
"disableFilenameBasedTypeAcquisition": true,
},
}`},
"/home/src/workspaces/project",
),
commandLineArgs: []string{},
}).verify(t, "typeAcquisition")
}
29 changes: 29 additions & 0 deletions internal/tsoptions/declstypeacquisition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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,
},
}
31 changes: 31 additions & 0 deletions internal/tsoptions/parsinghelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ func parseJsonToStringKey(json any) *collections.OrderedMap[string, any] {
if v, ok := m.Get("excludes"); ok {
result.Set("excludes", v)
}
if v, ok := m.Get("typeAcquisition"); ok {
result.Set("typeAcquisition", v)
}
}
return result
}
Expand All @@ -133,6 +136,14 @@ func (o *watchOptionsParser) ParseOption(key string, value any) []*ast.Diagnosti
return ParseWatchOptions(key, value, o.WatchOptions)
}

type typeAcquisitionParser struct {
*core.TypeAcquisition
}

func (o *typeAcquisitionParser) ParseOption(key string, value any) []*ast.Diagnostic {
return ParseTypeAcquisition(key, value, o.TypeAcquisition)
}

func ParseCompilerOptions(key string, value any, allOptions *core.CompilerOptions) []*ast.Diagnostic {
if value == nil {
return nil
Expand Down Expand Up @@ -436,6 +447,26 @@ func ParseWatchOptions(key string, value any, allOptions *core.WatchOptions) []*
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
}

// mergeCompilerOptions merges the source compiler options into the target compiler options.
// Fields in the source options will overwrite the corresponding fields in the target options.
func mergeCompilerOptions(targetOptions, sourceOptions *core.CompilerOptions) *core.CompilerOptions {
Expand Down
106 changes: 66 additions & 40 deletions internal/tsoptions/tsconfigparsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ var tsconfigRootOptionsMap = &CommandLineOption{
Kind: CommandLineOptionTypeObject,
ElementOptions: commandLineOptionsToMap([]*CommandLineOption{
compilerOptionsDeclaration,
// watchOptionsDeclaration,
typeAcquisitionDeclaration,
extendsOptionDeclaration,
{
Name: "references",
Expand Down Expand Up @@ -107,8 +109,8 @@ type ExtendedConfigCacheEntry struct {
type parsedTsconfig struct {
raw any
options *core.CompilerOptions
// watchOptions *compiler.WatchOptions
// typeAcquisition *compiler.TypeAcquisition
// watchOptions *core.WatchOptions
typeAcquisition *core.TypeAcquisition
// Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet
extendedConfigPath any
}
Expand All @@ -119,8 +121,8 @@ func parseOwnConfigOfJsonSourceFile(
basePath string,
configFileName string,
) (*parsedTsconfig, []*ast.Diagnostic) {
options := getDefaultCompilerOptions(configFileName)
// var typeAcquisition *compiler.TypeAcquisition
compilerOptions := getDefaultCompilerOptions(configFileName)
typeAcquisition := getDefaultTypeAcquisition(configFileName)
// var watchOptions *compiler.WatchOptions
var extendedConfigPath any
var rootCompilerOptions []*ast.PropertyName
Expand All @@ -139,7 +141,14 @@ func parseOwnConfigOfJsonSourceFile(
}
if parentOption != nil && parentOption.Name != "undefined" && value != nil {
if option != nil && option.Name != "" {
propertySetErrors = append(propertySetErrors, ParseCompilerOptions(option.Name, value, options)...)
var parseDiagnostics []*ast.Diagnostic
switch parentOption.Name {
case "compilerOptions":
parseDiagnostics = ParseCompilerOptions(option.Name, value, compilerOptions)
case "typeAcquisition":
parseDiagnostics = ParseTypeAcquisition(option.Name, value, typeAcquisition)
}
propertySetErrors = append(propertySetErrors, parseDiagnostics...)
} else if keyText != "" {
if parentOption.ElementOptions != nil {
// !!! TODO: support suggestion
Expand Down Expand Up @@ -178,9 +187,9 @@ func parseOwnConfigOfJsonSourceFile(
// }
return &parsedTsconfig{
raw: json,
options: options,
options: compilerOptions,
// watchOptions: watchOptions,
// typeAcquisition: typeAcquisition,
typeAcquisition: typeAcquisition,
extendedConfigPath: extendedConfigPath,
}, errors
}
Expand Down Expand Up @@ -479,19 +488,19 @@ type tsConfigOptions struct {
notDefined string
}

func commandLineOptionsToMap(options []*CommandLineOption) map[string]*CommandLineOption {
func commandLineOptionsToMap(compilerOptions []*CommandLineOption) map[string]*CommandLineOption {
result := make(map[string]*CommandLineOption)
for i := range options {
result[(options[i]).Name] = options[i]
for i := range compilerOptions {
result[(compilerOptions[i]).Name] = compilerOptions[i]
}
return result
}

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

func convertMapToOptions[O optionParser](options *collections.OrderedMap[string, any], result O) O {
func convertMapToOptions[O optionParser](compilerOptions *collections.OrderedMap[string, any], result O) O {
// this assumes any `key`, `value` pair in `options` will have `value` already be the correct type. this function should no error handling
for key, value := range options.Entries() {
for key, value := range compilerOptions.Entries() {
result.ParseOption(key, value)
}
return result
Expand Down Expand Up @@ -758,6 +767,14 @@ func getDefaultCompilerOptions(configFileName string) *core.CompilerOptions {
return options
}

func getDefaultTypeAcquisition(configFileName string) *core.TypeAcquisition {
options := &core.TypeAcquisition{}
if configFileName != "" && tspath.GetBaseFileName(configFileName) == "jsconfig.json" {
options.Enable = core.TSTrue
}
return options
}

func convertCompilerOptionsFromJsonWorker(jsonOptions any, basePath string, configFileName string) (*core.CompilerOptions, []*ast.Diagnostic) {
options := getDefaultCompilerOptions(configFileName)
_, errors := convertOptionsFromJson(commandLineCompilerOptionsMap, jsonOptions, basePath, &compilerOptionsParser{options})
Expand All @@ -767,6 +784,12 @@ func convertCompilerOptionsFromJsonWorker(jsonOptions any, basePath string, conf
return options, errors
}

func convertTypeAcquisitionFromJsonWorker(jsonOptions any, basePath string, configFileName string) (*core.TypeAcquisition, []*ast.Diagnostic) {
options := getDefaultTypeAcquisition(configFileName)
_, errors := convertOptionsFromJson(typeAcquisitionDeclaration.ElementOptions, jsonOptions, basePath, &typeAcquisitionParser{options})
return options, errors
}

func parseOwnConfigOfJson(
json *collections.OrderedMap[string, any],
host ParseConfigHost,
Expand All @@ -778,8 +801,8 @@ func parseOwnConfigOfJson(
errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.Unknown_option_excludes_Did_you_mean_exclude))
}
options, err := convertCompilerOptionsFromJsonWorker(json.GetOrZero("compilerOptions"), basePath, configFileName)
errors = append(errors, err...)
// typeAcquisition := convertTypeAcquisitionFromJsonWorker(json.typeAcquisition, basePath, errors, configFileName)
typeAcquisition, err2 := convertTypeAcquisitionFromJsonWorker(json.GetOrZero("typeAcquisition"), basePath, configFileName)
errors = append(append(errors, err...), err2...)
// watchOptions := convertWatchOptionsFromJsonWorker(json.watchOptions, basePath, errors)
// json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors)
var extendedConfigPath []string
Expand All @@ -790,6 +813,7 @@ func parseOwnConfigOfJson(
parsedConfig := &parsedTsconfig{
raw: json,
options: options,
typeAcquisition: typeAcquisition,
extendedConfigPath: extendedConfigPath,
}
return parsedConfig, errors
Expand Down Expand Up @@ -1176,7 +1200,9 @@ func parseJsonConfigFileContentWorker(

return &ParsedCommandLine{
ParsedConfig: &core.ParsedOptions{
CompilerOptions: parsedConfig.options,
CompilerOptions: parsedConfig.options,
TypeAcquisition: parsedConfig.typeAcquisition,
// WatchOptions: nil,
FileNames: getFileNames(basePathForFileNames),
ProjectReferences: getProjectReferences(basePathForFileNames),
},
Expand Down Expand Up @@ -1321,43 +1347,43 @@ func substituteStringArrayWithConfigDirTemplate(list []string, basePath string)
}
}

func handleOptionConfigDirTemplateSubstitution(options *core.CompilerOptions, basePath string) {
if options == nil {
func handleOptionConfigDirTemplateSubstitution(compilerOptions *core.CompilerOptions, basePath string) {
if compilerOptions == nil {
return
}

// !!! don't hardcode this; use options declarations?

for v := range options.Paths.Values() {
for v := range compilerOptions.Paths.Values() {
substituteStringArrayWithConfigDirTemplate(v, basePath)
}

substituteStringArrayWithConfigDirTemplate(options.RootDirs, basePath)
substituteStringArrayWithConfigDirTemplate(options.TypeRoots, basePath)
substituteStringArrayWithConfigDirTemplate(compilerOptions.RootDirs, basePath)
substituteStringArrayWithConfigDirTemplate(compilerOptions.TypeRoots, basePath)

if startsWithConfigDirTemplate(options.GenerateCpuProfile) {
options.GenerateCpuProfile = getSubstitutedPathWithConfigDirTemplate(options.GenerateCpuProfile, basePath)
if startsWithConfigDirTemplate(compilerOptions.GenerateCpuProfile) {
compilerOptions.GenerateCpuProfile = getSubstitutedPathWithConfigDirTemplate(compilerOptions.GenerateCpuProfile, basePath)
}
if startsWithConfigDirTemplate(options.GenerateTrace) {
options.GenerateTrace = getSubstitutedPathWithConfigDirTemplate(options.GenerateTrace, basePath)
if startsWithConfigDirTemplate(compilerOptions.GenerateTrace) {
compilerOptions.GenerateTrace = getSubstitutedPathWithConfigDirTemplate(compilerOptions.GenerateTrace, basePath)
}
if startsWithConfigDirTemplate(options.OutFile) {
options.OutFile = getSubstitutedPathWithConfigDirTemplate(options.OutFile, basePath)
if startsWithConfigDirTemplate(compilerOptions.OutFile) {
compilerOptions.OutFile = getSubstitutedPathWithConfigDirTemplate(compilerOptions.OutFile, basePath)
}
if startsWithConfigDirTemplate(options.OutDir) {
options.OutDir = getSubstitutedPathWithConfigDirTemplate(options.OutDir, basePath)
if startsWithConfigDirTemplate(compilerOptions.OutDir) {
compilerOptions.OutDir = getSubstitutedPathWithConfigDirTemplate(compilerOptions.OutDir, basePath)
}
if startsWithConfigDirTemplate(options.RootDir) {
options.RootDir = getSubstitutedPathWithConfigDirTemplate(options.RootDir, basePath)
if startsWithConfigDirTemplate(compilerOptions.RootDir) {
compilerOptions.RootDir = getSubstitutedPathWithConfigDirTemplate(compilerOptions.RootDir, basePath)
}
if startsWithConfigDirTemplate(options.TsBuildInfoFile) {
options.TsBuildInfoFile = getSubstitutedPathWithConfigDirTemplate(options.TsBuildInfoFile, basePath)
if startsWithConfigDirTemplate(compilerOptions.TsBuildInfoFile) {
compilerOptions.TsBuildInfoFile = getSubstitutedPathWithConfigDirTemplate(compilerOptions.TsBuildInfoFile, basePath)
}
if startsWithConfigDirTemplate(options.BaseUrl) {
options.BaseUrl = getSubstitutedPathWithConfigDirTemplate(options.BaseUrl, basePath)
if startsWithConfigDirTemplate(compilerOptions.BaseUrl) {
compilerOptions.BaseUrl = getSubstitutedPathWithConfigDirTemplate(compilerOptions.BaseUrl, basePath)
}
if startsWithConfigDirTemplate(options.DeclarationDir) {
options.DeclarationDir = getSubstitutedPathWithConfigDirTemplate(options.DeclarationDir, basePath)
if startsWithConfigDirTemplate(compilerOptions.DeclarationDir) {
compilerOptions.DeclarationDir = getSubstitutedPathWithConfigDirTemplate(compilerOptions.DeclarationDir, basePath)
}
}

Expand Down Expand Up @@ -1517,8 +1543,8 @@ func getFileNamesFromConfigSpecs(
return files
}

func GetSupportedExtensions(options *core.CompilerOptions, extraFileExtensions []fileExtensionInfo) [][]string {
needJSExtensions := options.GetAllowJS()
func GetSupportedExtensions(compilerOptions *core.CompilerOptions, extraFileExtensions []fileExtensionInfo) [][]string {
needJSExtensions := compilerOptions.GetAllowJS()
if len(extraFileExtensions) == 0 {
if needJSExtensions {
return tspath.AllSupportedExtensions
Expand All @@ -1543,8 +1569,8 @@ func GetSupportedExtensions(options *core.CompilerOptions, extraFileExtensions [
return extensions
}

func GetSupportedExtensionsWithJsonIfResolveJsonModule(options *core.CompilerOptions, supportedExtensions [][]string) [][]string {
if options == nil || !options.GetResolveJsonModule() {
func GetSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions *core.CompilerOptions, supportedExtensions [][]string) [][]string {
if compilerOptions == nil || !compilerOptions.GetResolveJsonModule() {
return supportedExtensions
}
if core.Same(supportedExtensions, tspath.AllSupportedExtensions) {
Expand Down
Loading