Skip to content

Port IsSourceFileFromExternalLibrary, fix node18 emit #1234

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 12 commits into from
Jun 20, 2025
6 changes: 6 additions & 0 deletions internal/compiler/emitHost.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,9 @@ func (host *emitHost) GetEmitResolver(file *ast.SourceFile, skipDiagnostics bool
defer done()
return checker.GetEmitResolver(file, skipDiagnostics)
}

func (host *emitHost) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool {
// Use the Program's proper tracking mechanism which mirrors the TypeScript implementation
// This tracks files that were found while searching node_modules during module resolution
return host.program.IsSourceFileFromExternalLibrary(file)
}
6 changes: 1 addition & 5 deletions internal/compiler/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package compiler

import (
"encoding/base64"
"strings"

"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
Expand Down Expand Up @@ -305,10 +304,7 @@ func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host printer.EmitHost, f
return false
}

// !!! Source file from node_modules are not emitted. In Strada, this depends on module resolution and uses
// `sourceFilesFoundSearchingNodeModules` in `createProgram`. For now, we will just check for `/node_modules/` in
// the file name.
if strings.Contains(sourceFile.FileName(), "/node_modules/") {
if host.IsSourceFileFromExternalLibrary(sourceFile) {
return false
}

Expand Down
51 changes: 34 additions & 17 deletions internal/compiler/fileloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ type processedFiles struct {
importHelpersImportSpecifiers map[tspath.Path]*ast.Node
// List of present unsupported extensions
unsupportedExtensions []string
// Track which source files were found while searching node_modules
// Similar to TypeScript's sourceFilesFoundSearchingNodeModules map
sourceFilesFoundSearchingNodeModules map[tspath.Path]bool
}

type jsxRuntimeImportSpecifier struct {
Expand Down Expand Up @@ -111,6 +114,7 @@ func processAllProgramFiles(
var jsxRuntimeImportSpecifiers map[tspath.Path]*jsxRuntimeImportSpecifier
var importHelpersImportSpecifiers map[tspath.Path]*ast.Node
var unsupportedExtensions []string
var sourceFilesFoundSearchingNodeModules map[tspath.Path]bool

loader.parseTasks.collect(&loader, loader.rootTasks, func(task *parseTask, _ []tspath.Path) {
file := task.file
Expand Down Expand Up @@ -148,22 +152,30 @@ func processAllProgramFiles(
if slices.Contains(tspath.SupportedJSExtensionsFlat, extension) {
unsupportedExtensions = core.AppendIfUnique(unsupportedExtensions, extension)
}
// Track files from external libraries using the proper module resolution flag
if task.isFromExternalLibrary {
if sourceFilesFoundSearchingNodeModules == nil {
sourceFilesFoundSearchingNodeModules = make(map[tspath.Path]bool, totalFileCount)
}
sourceFilesFoundSearchingNodeModules[path] = true
}
})
loader.sortLibs(libFiles)

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

return processedFiles{
resolver: loader.resolver,
files: allFiles,
filesByPath: filesByPath,
projectReferenceFileMapper: loader.projectReferenceFileMapper,
resolvedModules: resolvedModules,
typeResolutionsInFile: typeResolutionsInFile,
sourceFileMetaDatas: sourceFileMetaDatas,
jsxRuntimeImportSpecifiers: jsxRuntimeImportSpecifiers,
importHelpersImportSpecifiers: importHelpersImportSpecifiers,
unsupportedExtensions: unsupportedExtensions,
resolver: loader.resolver,
files: allFiles,
filesByPath: filesByPath,
projectReferenceFileMapper: loader.projectReferenceFileMapper,
resolvedModules: resolvedModules,
typeResolutionsInFile: typeResolutionsInFile,
sourceFileMetaDatas: sourceFileMetaDatas,
jsxRuntimeImportSpecifiers: jsxRuntimeImportSpecifiers,
importHelpersImportSpecifiers: importHelpersImportSpecifiers,
unsupportedExtensions: unsupportedExtensions,
sourceFilesFoundSearchingNodeModules: sourceFilesFoundSearchingNodeModules,
}
}

Expand Down Expand Up @@ -301,7 +313,10 @@ func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containi
referencedFileName = tspath.CombinePaths(basePath, moduleName)
}
return resolvedRef{
fileName: tspath.NormalizePath(referencedFileName),
fileName: tspath.NormalizePath(referencedFileName),
increaseDepth: false,
elideOnDepth: false,
isFromExternalLibrary: false, // Triple-slash references are always local files
}
}

Expand All @@ -319,9 +334,10 @@ func (p *fileLoader) resolveTypeReferenceDirectives(file *ast.SourceFile, meta a
typeResolutionsInFile[module.ModeAwareCacheKey{Name: ref.FileName, Mode: resolutionMode}] = resolved
if resolved.IsResolved() {
toParse = append(toParse, resolvedRef{
fileName: resolved.ResolvedFileName,
increaseDepth: resolved.IsExternalLibraryImport,
elideOnDepth: false,
fileName: resolved.ResolvedFileName,
increaseDepth: resolved.IsExternalLibraryImport,
elideOnDepth: false,
isFromExternalLibrary: resolved.IsExternalLibraryImport,
})
}
}
Expand Down Expand Up @@ -412,9 +428,10 @@ func (p *fileLoader) resolveImportsAndModuleAugmentations(file *ast.SourceFile,

if shouldAddFile {
toParse = append(toParse, resolvedRef{
fileName: resolvedFileName,
increaseDepth: resolvedModule.IsExternalLibraryImport,
elideOnDepth: isJsFileFromNodeModules,
fileName: resolvedFileName,
increaseDepth: resolvedModule.IsExternalLibraryImport,
elideOnDepth: isJsFileFromNodeModules,
isFromExternalLibrary: isFromNodeModulesSearch,
})
}
}
Expand Down
19 changes: 15 additions & 4 deletions internal/compiler/parsetask.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ type parseTask struct {
jsxRuntimeImportSpecifier *jsxRuntimeImportSpecifier
increaseDepth bool
elideOnDepth bool

// Track if this file is from an external library (node_modules)
// This mirrors the TypeScript currentNodeModulesDepth > 0 check
isFromExternalLibrary bool
}

func (t *parseTask) FileName() string {
Expand Down Expand Up @@ -98,14 +102,21 @@ func (t *parseTask) redirect(loader *fileLoader, fileName string) {
}

type resolvedRef struct {
fileName string
increaseDepth bool
elideOnDepth bool
fileName string
increaseDepth bool
elideOnDepth bool
isFromExternalLibrary bool
}

func (t *parseTask) addSubTask(ref resolvedRef, isLib bool) {
normalizedFilePath := tspath.NormalizePath(ref.fileName)
subTask := &parseTask{normalizedFilePath: normalizedFilePath, isLib: isLib, increaseDepth: ref.increaseDepth, elideOnDepth: ref.elideOnDepth}
subTask := &parseTask{
normalizedFilePath: normalizedFilePath,
isLib: isLib,
increaseDepth: ref.increaseDepth,
elideOnDepth: ref.elideOnDepth,
isFromExternalLibrary: ref.isFromExternalLibrary || t.isFromExternalLibrary, // Propagate external library status
}
t.subTasks = append(t.subTasks, subTask)
}

Expand Down
31 changes: 26 additions & 5 deletions internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
commonSourceDirectoryOnce sync.Once

declarationDiagnosticCache collections.SyncMap[*ast.SourceFile, []*ast.Diagnostic]

sourceFilesFoundSearchingNodeModules collections.SyncMap[tspath.Path, bool]
}

// FileExists implements checker.Program.
Expand Down Expand Up @@ -203,6 +205,14 @@

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

// Populate sourceFilesFoundSearchingNodeModules from the processed files
// This mirrors the TypeScript implementation's tracking behavior
if p.processedFiles.sourceFilesFoundSearchingNodeModules != nil {
for path, isExternal := range p.processedFiles.sourceFilesFoundSearchingNodeModules {
p.sourceFilesFoundSearchingNodeModules.Store(path, isExternal)
}
}

return p
}

Expand All @@ -215,11 +225,12 @@
return NewProgram(p.opts), false
}
result := &Program{
opts: p.opts,
nodeModules: p.nodeModules,
comparePathsOptions: p.comparePathsOptions,
processedFiles: p.processedFiles,
usesUriStyleNodeCoreModules: p.usesUriStyleNodeCoreModules,
opts: p.opts,
nodeModules: p.nodeModules,
comparePathsOptions: p.comparePathsOptions,
processedFiles: p.processedFiles,
usesUriStyleNodeCoreModules: p.usesUriStyleNodeCoreModules,
sourceFilesFoundSearchingNodeModules: p.sourceFilesFoundSearchingNodeModules,

Check failure on line 233 in internal/compiler/program.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest)

copylocks: literal copies lock value from p.sourceFilesFoundSearchingNodeModules: github.com/microsoft/typescript-go/internal/collections.SyncMap[github.com/microsoft/typescript-go/internal/tspath.Path, bool] contains sync.Map contains sync.noCopy (govet)
}
result.initCheckerPool()
index := core.FindIndex(result.files, func(file *ast.SourceFile) bool { return file.Path() == newFile.Path() })
Expand Down Expand Up @@ -820,6 +831,16 @@
return p.GetDefaultResolutionModeForFile(sourceFile)
}

// IsSourceFileFromExternalLibrary returns true if the source file is from an external library.
// This mirrors the TypeScript implementation which tracks files found while searching node_modules.
func (p *Program) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool {
if file == nil {
return false
}
isExternal, exists := p.sourceFilesFoundSearchingNodeModules.Load(file.Path())
return exists && isExternal
}

type FileIncludeKind int

const (
Expand Down
1 change: 1 addition & 0 deletions internal/printer/emithost.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ type EmitHost interface {
GetEmitModuleFormatOfFile(file ast.HasFileName) core.ModuleKind
GetEmitResolver(file *ast.SourceFile, skipDiagnostics bool) EmitResolver
GetOutputAndProjectReference(path tspath.Path) *tsoptions.OutputDtsAndProjectReference
IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool
}
1 change: 1 addition & 0 deletions internal/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,7 @@ func (p *Project) GetFileNames(excludeFilesFromExternalLibraries bool, excludeCo
result := []string{}
sourceFiles := p.program.GetSourceFiles()
for _, sourceFile := range sourceFiles {
// !! This is probably ready to be implemented now?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we have IsSourceFileFromExternalLibrary

// if excludeFilesFromExternalLibraries && p.program.IsSourceFileFromExternalLibrary(sourceFile) {
// continue;
// }
Expand Down
Loading