Skip to content

Commit 104aea5

Browse files
authored
Implement tryLoadInputFileForPath, file loader diags (#1302)
1 parent af0fe4a commit 104aea5

File tree

164 files changed

+6524
-1810
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

164 files changed

+6524
-1810
lines changed

internal/compiler/fileloader.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type processedFiles struct {
5454
// List of present unsupported extensions
5555
unsupportedExtensions []string
5656
sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path]
57+
fileLoadDiagnostics *ast.DiagnosticsCollection
5758
}
5859

5960
type jsxRuntimeImportSpecifier struct {
@@ -132,6 +133,7 @@ func processAllProgramFiles(
132133
var unsupportedExtensions []string
133134
var sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path]
134135
var libFileSet collections.Set[tspath.Path]
136+
fileLoadDiagnostics := &ast.DiagnosticsCollection{}
135137

136138
loader.parseTasks.collect(&loader, loader.rootTasks, func(task *parseTask, _ []tspath.Path) {
137139
if task.isRedirected {
@@ -159,6 +161,7 @@ func processAllProgramFiles(
159161
resolvedModules[path] = task.resolutionsInFile
160162
typeResolutionsInFile[path] = task.typeResolutionsInFile
161163
sourceFileMetaDatas[path] = task.metadata
164+
162165
if task.jsxRuntimeImportSpecifier != nil {
163166
if jsxRuntimeImportSpecifiers == nil {
164167
jsxRuntimeImportSpecifiers = make(map[tspath.Path]*jsxRuntimeImportSpecifier, totalFileCount)
@@ -183,8 +186,28 @@ func processAllProgramFiles(
183186

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

189+
for _, resolutions := range resolvedModules {
190+
for _, resolvedModule := range resolutions {
191+
for _, diag := range resolvedModule.ResolutionDiagnostics {
192+
fileLoadDiagnostics.Add(diag)
193+
}
194+
}
195+
}
196+
for _, typeResolutions := range typeResolutionsInFile {
197+
for _, resolvedTypeRef := range typeResolutions {
198+
for _, diag := range resolvedTypeRef.ResolutionDiagnostics {
199+
fileLoadDiagnostics.Add(diag)
200+
}
201+
}
202+
}
203+
186204
loader.pathForLibFileResolutions.Range(func(key tspath.Path, value module.ModeAwareCache[*module.ResolvedModule]) bool {
187205
resolvedModules[key] = value
206+
for _, resolvedModule := range value {
207+
for _, diag := range resolvedModule.ResolutionDiagnostics {
208+
fileLoadDiagnostics.Add(diag)
209+
}
210+
}
188211
return true
189212
})
190213

@@ -201,6 +224,7 @@ func processAllProgramFiles(
201224
unsupportedExtensions: unsupportedExtensions,
202225
sourceFilesFoundSearchingNodeModules: sourceFilesFoundSearchingNodeModules,
203226
libFiles: libFileSet,
227+
fileLoadDiagnostics: fileLoadDiagnostics,
204228
}
205229
}
206230

@@ -372,6 +396,7 @@ func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) {
372396
resolutionMode := getModeForTypeReferenceDirectiveInFile(ref, file, meta, module.GetCompilerOptionsWithRedirect(p.opts.Config.CompilerOptions(), redirect))
373397
resolved := p.resolver.ResolveTypeReferenceDirective(ref.FileName, file.FileName(), resolutionMode, redirect)
374398
typeResolutionsInFile[module.ModeAwareCacheKey{Name: ref.FileName, Mode: resolutionMode}] = resolved
399+
375400
if resolved.IsResolved() {
376401
t.addSubTask(resolvedRef{
377402
fileName: resolved.ResolvedFileName,

internal/compiler/parsetask.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type parseTask struct {
2222
metadata ast.SourceFileMetaData
2323
resolutionsInFile module.ModeAwareCache[*module.ResolvedModule]
2424
typeResolutionsInFile module.ModeAwareCache[*module.ResolvedTypeReferenceDirective]
25+
resolutionDiagnostics []*ast.Diagnostic
2526
importHelpersImportSpecifier *ast.Node
2627
jsxRuntimeImportSpecifier *jsxRuntimeImportSpecifier
2728
increaseDepth bool

internal/compiler/program.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@ func (p *Program) GetSuggestionDiagnostics(ctx context.Context, sourceFile *ast.
330330
return p.getDiagnosticsHelper(ctx, sourceFile, true /*ensureBound*/, true /*ensureChecked*/, p.getSuggestionDiagnosticsForFile)
331331
}
332332

333+
func (p *Program) GetProgramDiagnostics() []*ast.Diagnostic {
334+
// !!!
335+
return SortAndDeduplicateDiagnostics(p.fileLoadDiagnostics.GetDiagnostics())
336+
}
337+
333338
func (p *Program) GetGlobalDiagnostics(ctx context.Context) []*ast.Diagnostic {
334339
var globalDiagnostics []*ast.Diagnostic
335340
checkers, done := p.checkerPool.GetAllCheckers(ctx)

internal/execute/tsc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ func emitFilesAndReportErrors(sys System, program *compiler.Program, reportDiagn
296296
configFileParsingDiagnosticsLength := len(allDiagnostics)
297297

298298
allDiagnostics = append(allDiagnostics, program.GetSyntacticDiagnostics(ctx, nil)...)
299+
allDiagnostics = append(allDiagnostics, program.GetProgramDiagnostics()...)
299300

300301
if len(allDiagnostics) == configFileParsingDiagnosticsLength {
301302
// Options diagnostics include global diagnostics (even though we collect them separately),

internal/module/resolver.go

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ func continueSearching() *resolved {
3434
return nil
3535
}
3636

37+
func unresolved() *resolved {
38+
return &resolved{}
39+
}
40+
3741
type resolutionKindSpecificLoader = func(extensions extensions, candidate string, onlyRecordFailures bool) *resolved
3842

3943
type resolutionState struct {
@@ -54,7 +58,7 @@ type resolutionState struct {
5458
resolvedPackageDirectory bool
5559
failedLookupLocations []string
5660
affectingLocations []string
57-
diagnostics []ast.Diagnostic
61+
diagnostics []*ast.Diagnostic
5862
}
5963

6064
func newResolutionState(
@@ -748,10 +752,104 @@ func (r *resolutionState) loadModuleFromTargetExportOrImport(extensions extensio
748752
}
749753

750754
func (r *resolutionState) tryLoadInputFileForPath(finalPath string, entry string, packagePath string, isImports bool) *resolved {
751-
// !!!
755+
// Replace any references to outputs for files in the program with the input files to support package self-names used with outDir
756+
if !r.isConfigLookup &&
757+
(r.compilerOptions.DeclarationDir != "" || r.compilerOptions.OutDir != "") &&
758+
!strings.Contains(finalPath, "/node_modules/") &&
759+
(r.compilerOptions.ConfigFilePath == "" || tspath.ContainsPath(
760+
tspath.GetDirectoryPath(packagePath),
761+
r.compilerOptions.ConfigFilePath,
762+
tspath.ComparePathsOptions{
763+
UseCaseSensitiveFileNames: r.resolver.host.FS().UseCaseSensitiveFileNames(),
764+
CurrentDirectory: r.resolver.host.GetCurrentDirectory(),
765+
},
766+
)) {
767+
768+
// Note: this differs from Strada's tryLoadInputFileForPath in that it
769+
// does not attempt to perform "guesses", instead requring a clear root indicator.
770+
771+
var rootDir string
772+
if r.compilerOptions.RootDir != "" {
773+
// A `rootDir` compiler option strongly indicates the root location
774+
rootDir = r.compilerOptions.RootDir
775+
} else if r.compilerOptions.Composite.IsTrue() && r.compilerOptions.ConfigFilePath != "" {
776+
// A `composite` project is using project references and has it's common src dir set to `.`, so it shouldn't need to check any other locations
777+
rootDir = r.compilerOptions.ConfigFilePath
778+
} else {
779+
diagnostic := ast.NewDiagnostic(
780+
nil,
781+
core.TextRange{},
782+
core.IfElse(isImports,
783+
diagnostics.The_project_root_is_ambiguous_but_is_required_to_resolve_import_map_entry_0_in_file_1_Supply_the_rootDir_compiler_option_to_disambiguate,
784+
diagnostics.The_project_root_is_ambiguous_but_is_required_to_resolve_export_map_entry_0_in_file_1_Supply_the_rootDir_compiler_option_to_disambiguate,
785+
),
786+
core.IfElse(entry == "", ".", entry), // replace empty string with `.` - the reverse of the operation done when entries are built - so main entrypoint errors don't look weird
787+
packagePath,
788+
)
789+
r.diagnostics = append(r.diagnostics, diagnostic)
790+
return unresolved()
791+
}
792+
793+
candidateDirectories := r.getOutputDirectoriesForBaseDirectory(rootDir)
794+
for _, candidateDir := range candidateDirectories {
795+
if tspath.ContainsPath(candidateDir, finalPath, tspath.ComparePathsOptions{
796+
UseCaseSensitiveFileNames: r.resolver.host.FS().UseCaseSensitiveFileNames(),
797+
CurrentDirectory: r.resolver.host.GetCurrentDirectory(),
798+
}) {
799+
// The matched export is looking up something in either the out declaration or js dir, now map the written path back into the source dir and source extension
800+
pathFragment := finalPath[len(candidateDir)+1:] // +1 to also remove directory separator
801+
possibleInputBase := tspath.CombinePaths(rootDir, pathFragment)
802+
jsAndDtsExtensions := []string{tspath.ExtensionMjs, tspath.ExtensionCjs, tspath.ExtensionJs, tspath.ExtensionJson, tspath.ExtensionDmts, tspath.ExtensionDcts, tspath.ExtensionDts}
803+
for _, ext := range jsAndDtsExtensions {
804+
if tspath.FileExtensionIs(possibleInputBase, ext) {
805+
inputExts := r.getPossibleOriginalInputExtensionForExtension(possibleInputBase)
806+
for _, possibleExt := range inputExts {
807+
if !extensionIsOk(r.extensions, possibleExt) {
808+
continue
809+
}
810+
possibleInputWithInputExtension := tspath.ChangeExtension(possibleInputBase, possibleExt)
811+
if r.resolver.host.FS().FileExists(possibleInputWithInputExtension) {
812+
resolved := r.loadFileNameFromPackageJSONField(r.extensions, possibleInputWithInputExtension, "", false)
813+
if !resolved.shouldContinueSearching() {
814+
return resolved
815+
}
816+
}
817+
}
818+
}
819+
}
820+
}
821+
}
822+
}
752823
return continueSearching()
753824
}
754825

826+
func (r *resolutionState) getOutputDirectoriesForBaseDirectory(commonSourceDirGuess string) []string {
827+
// Config file output paths are processed to be relative to the host's current directory, while
828+
// otherwise the paths are resolved relative to the common source dir the compiler puts together
829+
currentDir := core.IfElse(r.compilerOptions.ConfigFilePath != "", r.resolver.host.GetCurrentDirectory(), commonSourceDirGuess)
830+
var candidateDirectories []string
831+
if r.compilerOptions.DeclarationDir != "" {
832+
candidateDirectories = append(candidateDirectories, tspath.GetNormalizedAbsolutePath(tspath.CombinePaths(currentDir, r.compilerOptions.DeclarationDir), r.resolver.host.GetCurrentDirectory()))
833+
}
834+
if r.compilerOptions.OutDir != "" && r.compilerOptions.OutDir != r.compilerOptions.DeclarationDir {
835+
candidateDirectories = append(candidateDirectories, tspath.GetNormalizedAbsolutePath(tspath.CombinePaths(currentDir, r.compilerOptions.OutDir), r.resolver.host.GetCurrentDirectory()))
836+
}
837+
return candidateDirectories
838+
}
839+
840+
func (r *resolutionState) getPossibleOriginalInputExtensionForExtension(path string) []string {
841+
if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDmts, tspath.ExtensionMjs, tspath.ExtensionMts}) {
842+
return []string{tspath.ExtensionMts, tspath.ExtensionMjs}
843+
}
844+
if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDcts, tspath.ExtensionCjs, tspath.ExtensionCts}) {
845+
return []string{tspath.ExtensionCts, tspath.ExtensionCjs}
846+
}
847+
if tspath.FileExtensionIs(path, ".d.json.ts") {
848+
return []string{tspath.ExtensionJson}
849+
}
850+
return []string{tspath.ExtensionTsx, tspath.ExtensionTs, tspath.ExtensionJsx, tspath.ExtensionJs}
851+
}
852+
755853
func (r *resolutionState) loadModuleFromNearestNodeModulesDirectory(typesScopeOnly bool) *resolved {
756854
mode := core.ResolutionModeCommonJS
757855
if r.esmMode || r.conditionMatches("import") {
@@ -1377,7 +1475,7 @@ func (r *resolutionState) loadFileNameFromPackageJSONField(extensions extensions
13771475
return &resolved{
13781476
path: path,
13791477
extension: extension,
1380-
resolvedUsingTsExtension: !strings.HasSuffix(packageJSONValue, extension),
1478+
resolvedUsingTsExtension: packageJSONValue != "" && !strings.HasSuffix(packageJSONValue, extension),
13811479
}
13821480
}
13831481
return continueSearching()

internal/module/resolver_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,21 @@ var skip = []string{
7474
"moduleResolutionWithSymlinks_referenceTypes.ts",
7575
"moduleResolutionWithSymlinks_withOutDir.ts",
7676
"moduleResolutionWithSymlinks.ts",
77+
"nodeAllowJsPackageSelfName(module=node16).ts",
78+
"nodeAllowJsPackageSelfName(module=nodenext).ts",
7779
"nodeAllowJsPackageSelfName2.ts",
7880
"nodeModulesAllowJsConditionalPackageExports(module=node16).ts",
7981
"nodeModulesAllowJsConditionalPackageExports(module=nodenext).ts",
8082
"nodeModulesAllowJsPackageExports(module=node16).ts",
8183
"nodeModulesAllowJsPackageExports(module=nodenext).ts",
84+
"nodeModulesAllowJsPackageImports(module=node16).ts",
85+
"nodeModulesAllowJsPackageImports(module=nodenext).ts",
8286
"nodeModulesAllowJsPackagePatternExports(module=node16).ts",
8387
"nodeModulesAllowJsPackagePatternExports(module=nodenext).ts",
8488
"nodeModulesAllowJsPackagePatternExportsTrailers(module=node16).ts",
8589
"nodeModulesAllowJsPackagePatternExportsTrailers(module=nodenext).ts",
90+
"nodeModulesConditionalPackageExports(module=node16).ts",
91+
"nodeModulesConditionalPackageExports(module=nodenext).ts",
8692
"nodeModulesDeclarationEmitWithPackageExports(module=node16).ts",
8793
"nodeModulesDeclarationEmitWithPackageExports(module=nodenext).ts",
8894
"nodeModulesExportsBlocksTypesVersions(module=node16).ts",

internal/module/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func (p *PackageId) PackageName() string {
6363
type LookupLocations struct {
6464
FailedLookupLocations []string
6565
AffectingLocations []string
66-
ResolutionDiagnostics []ast.Diagnostic
66+
ResolutionDiagnostics []*ast.Diagnostic
6767
}
6868

6969
type ResolvedModule struct {

internal/testutil/harnessutil/harnessutil.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ func compileFilesWithHost(
575575
ctx := context.Background()
576576
program := createProgram(host, config)
577577
var diagnostics []*ast.Diagnostic
578+
diagnostics = append(diagnostics, program.GetProgramDiagnostics()...)
578579
diagnostics = append(diagnostics, program.GetSyntacticDiagnostics(ctx, nil)...)
579580
diagnostics = append(diagnostics, program.GetSemanticDiagnostics(ctx, nil)...)
580581
diagnostics = append(diagnostics, program.GetGlobalDiagnostics(ctx)...)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// [tests/cases/compiler/subpathImportsJS.ts] ////
2+
3+
//// [package.json]
4+
{
5+
"name": "pkg",
6+
"type": "module",
7+
"imports": {
8+
"#subpath": "./dist/subpath.js"
9+
}
10+
}
11+
12+
//// [subpath.ts]
13+
export const foo = "foo";
14+
15+
//// [index.ts]
16+
import { foo } from "#subpath";
17+
foo;
18+
19+
20+
//// [subpath.js]
21+
export const foo = "foo";
22+
//// [index.js]
23+
import { foo } from "#subpath";
24+
foo;
25+
26+
27+
//// [subpath.d.ts]
28+
export declare const foo = "foo";
29+
//// [index.d.ts]
30+
export {};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//// [tests/cases/compiler/subpathImportsJS.ts] ////
2+
3+
=== /src/subpath.ts ===
4+
export const foo = "foo";
5+
>foo : Symbol(foo, Decl(subpath.ts, 0, 12))
6+
7+
=== /src/index.ts ===
8+
import { foo } from "#subpath";
9+
>foo : Symbol(foo, Decl(index.ts, 0, 8))
10+
11+
foo;
12+
>foo : Symbol(foo, Decl(index.ts, 0, 8))
13+

0 commit comments

Comments
 (0)