Skip to content

Port lib replacement and --libReplacement #1262

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 7 commits into from
Jun 24, 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
78 changes: 75 additions & 3 deletions internal/compiler/fileloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type fileLoader struct {

projectReferenceFileMapper *projectReferenceFileMapper
dtsDirectories collections.Set[tspath.Path]

pathForLibFileCache collections.SyncMap[string, string]
pathForLibFileResolutions collections.SyncMap[tspath.Path, module.ModeAwareCache[*module.ResolvedModule]]
}

type processedFiles struct {
Expand All @@ -59,7 +62,6 @@ type jsxRuntimeImportSpecifier struct {

func processAllProgramFiles(
opts ProgramOptions,
libs []string,
singleThreaded bool,
) processedFiles {
compilerOptions := opts.Config.CompilerOptions()
Expand All @@ -83,11 +85,27 @@ func processAllProgramFiles(
projectReferenceParseTasks: &fileLoaderWorker[*projectReferenceParseTask]{
wg: core.NewWorkGroup(singleThreaded),
},
rootTasks: make([]*parseTask, 0, len(rootFiles)+len(libs)),
rootTasks: make([]*parseTask, 0, len(rootFiles)+len(compilerOptions.Lib)),
supportedExtensions: core.Flatten(tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions)),
}
loader.addProjectReferenceTasks()
loader.resolver = module.NewResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName)

var libs []string
if compilerOptions.NoLib.IsFalseOrUnknown() {
if compilerOptions.Lib == nil {
name := tsoptions.GetDefaultLibFileName(compilerOptions)
libs = append(libs, loader.pathForLibFile(name))
} else {
for _, lib := range compilerOptions.Lib {
if name, ok := tsoptions.GetLibFileName(lib); ok {
libs = append(libs, loader.pathForLibFile(name))
}
// !!! error on unknown name
}
}
}

loader.addRootTasks(rootFiles, false)
loader.addRootTasks(libs, true)
loader.addAutomaticTypeDirectiveTasks()
Expand All @@ -105,7 +123,7 @@ func processAllProgramFiles(
libFiles := make([]*ast.SourceFile, 0, totalFileCount) // totalFileCount here since we append files to it later to construct the final list

filesByPath := make(map[tspath.Path]*ast.SourceFile, totalFileCount)
resolvedModules := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule], totalFileCount)
resolvedModules := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule], totalFileCount+1)
typeResolutionsInFile := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], totalFileCount)
sourceFileMetaDatas := make(map[tspath.Path]ast.SourceFileMetaData, totalFileCount)
var jsxRuntimeImportSpecifiers map[tspath.Path]*jsxRuntimeImportSpecifier
Expand Down Expand Up @@ -162,6 +180,11 @@ func processAllProgramFiles(

allFiles := append(libFiles, files...)

loader.pathForLibFileResolutions.Range(func(key tspath.Path, value module.ModeAwareCache[*module.ResolvedModule]) bool {
resolvedModules[key] = value
return true
})

return processedFiles{
resolver: loader.resolver,
files: allFiles,
Expand Down Expand Up @@ -463,6 +486,55 @@ func (p *fileLoader) createSyntheticImport(text string, file *ast.SourceFile) *a
return externalHelpersModuleReference
}

func (p *fileLoader) pathForLibFile(name string) string {
if cached, ok := p.pathForLibFileCache.Load(name); ok {
return cached
}

path := tspath.CombinePaths(p.defaultLibraryPath, name)
if p.opts.Config.CompilerOptions().LibReplacement.IsTrue() {
libraryName := getLibraryNameFromLibFileName(name)
resolveFrom := getInferredLibraryNameResolveFrom(p.opts.Config.CompilerOptions(), p.opts.Host.GetCurrentDirectory(), name)
resolution := p.resolver.ResolveModuleName(libraryName, resolveFrom, core.ModuleKindCommonJS, nil)
if resolution.IsResolved() {
path = resolution.ResolvedFileName
p.pathForLibFileResolutions.LoadOrStore(p.toPath(resolveFrom), module.ModeAwareCache[*module.ResolvedModule]{
module.ModeAwareCacheKey{Name: libraryName, Mode: core.ModuleKindCommonJS}: resolution,
})
}
}

path, _ = p.pathForLibFileCache.LoadOrStore(name, path)
return path
}

func getLibraryNameFromLibFileName(libFileName string) string {
// Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and
// lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable
// lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown
components := strings.Split(libFileName, ".")
var path string
if len(components) > 1 {
path = components[1]
}
i := 2
for i < len(components) && components[i] != "" && components[i] != "d" {
path += core.IfElse(i == 2, "/", "-") + components[i]
i++
}
return "@typescript/lib-" + path
}

func getInferredLibraryNameResolveFrom(options *core.CompilerOptions, currentDirectory string, libFileName string) string {
var containingDirectory string
if options.ConfigFilePath != "" {
containingDirectory = tspath.GetDirectoryPath(options.ConfigFilePath)
} else {
containingDirectory = currentDirectory
}
return tspath.CombinePaths(containingDirectory, "__lib_node_modules_lookup_"+libFileName+"__.ts")
}

type resolution struct {
node *ast.Node
resolvedModule *module.ResolvedModule
Expand Down
6 changes: 2 additions & 4 deletions internal/compiler/parsetask.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,9 @@ func (t *parseTask) load(loader *fileLoader) {

if compilerOptions.NoLib != core.TSTrue {
for _, lib := range file.LibReferenceDirectives {
name, ok := tsoptions.GetLibFileName(lib.FileName)
if !ok {
continue
if name, ok := tsoptions.GetLibFileName(lib.FileName); ok {
t.addSubTask(resolvedRef{fileName: loader.pathForLibFile(name)}, true)
}
t.addSubTask(resolvedRef{fileName: tspath.CombinePaths(loader.defaultLibraryPath, name)}, true)
}
}

Expand Down
32 changes: 2 additions & 30 deletions internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,37 +172,9 @@ func (p *Program) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.Fi
}

func NewProgram(opts ProgramOptions) *Program {
p := &Program{
opts: opts,
}
compilerOptions := p.opts.Config.CompilerOptions()
if compilerOptions == nil {
panic("compiler options required")
}
if p.opts.Host == nil {
panic("host required")
}
p := &Program{opts: opts}
p.initCheckerPool()

var libs []string

if compilerOptions.NoLib != core.TSTrue {
if compilerOptions.Lib == nil {
name := tsoptions.GetDefaultLibFileName(compilerOptions)
libs = append(libs, tspath.CombinePaths(p.Host().DefaultLibraryPath(), name))
} else {
for _, lib := range compilerOptions.Lib {
name, ok := tsoptions.GetLibFileName(lib)
if ok {
libs = append(libs, tspath.CombinePaths(p.Host().DefaultLibraryPath(), name))
}
// !!! error on unknown name
}
}
}

p.processedFiles = processAllProgramFiles(p.opts, libs, p.singleThreaded())

p.processedFiles = processAllProgramFiles(p.opts, p.singleThreaded())
return p
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
index.ts(6,1): error TS2304: Cannot find name 'window'.


==== /node_modules/@typescript/lib-dom/index.d.ts (0 errors) ====
interface ABC { abc: string }
==== index.ts (1 errors) ====
/// <reference lib="dom" />
const a: ABC = { abc: "Hello" }

// This should fail because libdom has been replaced
// by the module above ^
window.localStorage
~~~~~~
!!! error TS2304: Cannot find name 'window'.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,4 @@ const a: ABC = { abc: "Hello" }
// This should fail because libdom has been replaced
// by the module above ^
window.localStorage
>window.localStorage : Symbol(localStorage, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
>localStorage : Symbol(localStorage, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))

Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,4 @@
+>abc : Symbol(abc, Decl(index.d.ts, 0, 15))

=== index.ts ===
/// <reference lib="dom" />
@@= skipped -12, +12 lines =@@
// This should fail because libdom has been replaced
// by the module above ^
window.localStorage
+>window.localStorage : Symbol(localStorage, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
+>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
+>localStorage : Symbol(localStorage, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
/// <reference lib="dom" />
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const a: ABC = { abc: "Hello" }
// This should fail because libdom has been replaced
// by the module above ^
window.localStorage
>window.localStorage : Storage
>window : Window & typeof globalThis
>localStorage : Storage
>window.localStorage : any
>window : any
>localStorage : any

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/somepath/index.ts(2,10): error TS2304: Cannot find name 'ABC'.
/somepath/index.ts(6,1): error TS2304: Cannot find name 'window'.


==== /somepath/tsconfig.json (0 errors) ====
{ }
==== /somepath/index.ts (1 errors) ====
/// <reference lib="dom" />
const a: ABC = { abc: "Hello" }
~~~
!!! error TS2304: Cannot find name 'ABC'.

// This should fail because libdom has been replaced
// by the module above ^
window.localStorage
~~~~~~
!!! error TS2304: Cannot find name 'window'.

==== /somepath/node_modules/@typescript/lib-dom/index.d.ts (0 errors) ====
interface ABC { abc: string }

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
/// <reference lib="dom" />
const a: ABC = { abc: "Hello" }
>a : Symbol(a, Decl(index.ts, 1, 5))
>ABC : Symbol(ABC)
>ABC : Symbol(ABC, Decl(index.d.ts, 0, 0))
>abc : Symbol(abc, Decl(index.ts, 1, 16))

// This should fail because libdom has been replaced
// by the module above ^
window.localStorage
>window.localStorage : Symbol(localStorage, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
>localStorage : Symbol(localStorage, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))

=== /somepath/node_modules/@typescript/lib-dom/index.d.ts ===
interface ABC { abc: string }
>ABC : Symbol(ABC, Decl(index.d.ts, 0, 0))
>abc : Symbol(abc, Decl(index.d.ts, 0, 15))

Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
--- old.libTypeScriptOverrideSimpleConfig.symbols
+++ new.libTypeScriptOverrideSimpleConfig.symbols
@@= skipped -3, +3 lines =@@
/// <reference lib="dom" />
const a: ABC = { abc: "Hello" }
>a : Symbol(a, Decl(index.ts, 1, 5))
->ABC : Symbol(ABC, Decl(index.d.ts, 0, 0))
+>ABC : Symbol(ABC)
>abc : Symbol(abc, Decl(index.ts, 1, 16))

// This should fail because libdom has been replaced
// by the module above ^
window.localStorage
-
-=== /somepath/node_modules/@typescript/lib-dom/index.d.ts ===
-interface ABC { abc: string }
->ABC : Symbol(ABC, Decl(index.d.ts, 0, 0))
@@= skipped -13, +13 lines =@@
=== /somepath/node_modules/@typescript/lib-dom/index.d.ts ===
interface ABC { abc: string }
>ABC : Symbol(ABC, Decl(index.d.ts, 0, 0))
->abc : Symbol(ABC.abc, Decl(index.d.ts, 0, 15))
+>window.localStorage : Symbol(localStorage, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
+>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
+>localStorage : Symbol(localStorage, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
+>abc : Symbol(abc, Decl(index.d.ts, 0, 15))
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ const a: ABC = { abc: "Hello" }
// This should fail because libdom has been replaced
// by the module above ^
window.localStorage
>window.localStorage : Storage
>window : Window & typeof globalThis
>localStorage : Storage
>window.localStorage : any
>window : any
>localStorage : any

=== /somepath/node_modules/@typescript/lib-dom/index.d.ts ===
interface ABC { abc: string }
>abc : string

This file was deleted.

Loading