Skip to content

Commit bcb0b02

Browse files
committed
properly handle references config changes
1 parent 6ae64b4 commit bcb0b02

3 files changed

Lines changed: 108 additions & 4 deletions

File tree

tsc/internal/project/configfileregistrybuilder.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,23 @@ func (c *configFileRegistryBuilder) releaseConfigForProject(configFilePath tspat
353353
}
354354
}
355355

356+
func (c *configFileRegistryBuilder) retainConfigForProject(configFilePath tspath.Path, projectPath tspath.Path) {
357+
if entry, ok := c.configs.Load(configFilePath); ok {
358+
entry.ChangeIf(
359+
func(config *configFileEntry) bool {
360+
_, exists := config.retainingProjects[projectPath]
361+
return !exists
362+
},
363+
func(config *configFileEntry) {
364+
if config.retainingProjects == nil {
365+
config.retainingProjects = make(map[tspath.Path]struct{})
366+
}
367+
config.retainingProjects[projectPath] = struct{}{}
368+
},
369+
)
370+
}
371+
}
372+
356373
// didCloseFile removes the open file from the config entry. Once no projects
357374
// or files are associated with the config entry, it will be removed on the next call to `cleanup`.
358375
func (c *configFileRegistryBuilder) didCloseFile(path tspath.Path) {

tsc/internal/project/projectcollectionbuilder.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -763,8 +763,13 @@ func (b *ProjectCollectionBuilder) markProjectsAffectedByConfigChanges(
763763
logger *logging.LogTree,
764764
) bool {
765765
for projectPath := range configChangeResult.affectedProjects {
766-
project, ok := b.configuredProjects.Load(projectPath)
767-
if !ok {
766+
var project dirty.Value[*Project]
767+
if projectPath == inferredProjectName {
768+
project = b.inferredProject
769+
} else {
770+
project, _ = b.configuredProjects.Load(projectPath)
771+
}
772+
if project == nil || project.Value() == nil {
768773
panic(fmt.Sprintf("project %s affected by config change not found", projectPath))
769774
}
770775
project.ChangeIf(
@@ -1126,12 +1131,17 @@ func (b *ProjectCollectionBuilder) updateInferredProjectRoots(rootFileNames []st
11261131
return b.updateInferredProject(rootFileNames, b.compilerOptionsForInferredProjects, projectReferences, configFileParsingDiagnostics, b.inferredContentMappers, logger)
11271132
}
11281133

1129-
// seedInferredProjectForProgram adapts one selected project into the isolated synthetic-project slot used by createProgram.
1134+
// seedInferredProjectForProgram copies the specified project into the synthetic inferred project used by createProgram.
11301135
func (b *ProjectCollectionBuilder) seedInferredProjectForProgram(project *Project, logger *logging.LogTree) {
11311136
if project == nil || project.Program == nil {
11321137
return
11331138
}
1134-
b.inferredProject.Set(newInferredProjectFromProject(project, b, logger))
1139+
inferredProject := newInferredProjectFromProject(project, b, logger)
1140+
project.Program.RangeResolvedProjectReference(func(referencePath tspath.Path, _ *tsoptions.ParsedCommandLine, _ *tsoptions.ParsedCommandLine, _ int) bool {
1141+
b.configFileRegistryBuilder.retainConfigForProject(referencePath, inferredProject.configFilePath)
1142+
return true
1143+
})
1144+
b.inferredProject.Set(inferredProject)
11351145
}
11361146

11371147
// updateInferredProject preserves the current command line when roots/options are unchanged.

tsc/internal/project/refcountcache_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/microsoft/TypeScript/tsc/internal/ast"
99
"github.com/microsoft/TypeScript/tsc/internal/bundled"
10+
"github.com/microsoft/TypeScript/tsc/internal/collections"
1011
"github.com/microsoft/TypeScript/tsc/internal/compiler"
1112
"github.com/microsoft/TypeScript/tsc/internal/contentmapper"
1213
"github.com/microsoft/TypeScript/tsc/internal/core"
@@ -491,5 +492,81 @@ func TestRefCountingCaches(t *testing.T) {
491492
_, ok = session.extendedConfigCache.entries.Load(extendedConfigPath)
492493
assert.Assert(t, !ok)
493494
})
495+
496+
t.Run("createProgram retains and reloads extended configs from referenced projects", func(t *testing.T) {
497+
t.Parallel()
498+
499+
const (
500+
appConfigPath = "/user/username/projects/app/tsconfig.json"
501+
appFilePath = "/user/username/projects/app/index.ts"
502+
libConfigPath = "/user/username/projects/lib/tsconfig.json"
503+
libBaseConfigPath = "/user/username/projects/lib/tsconfig.base.json"
504+
libFilePath = "/user/username/projects/lib/index.ts"
505+
)
506+
session := setup(map[string]any{
507+
appConfigPath: `{"compilerOptions":{"noLib":true},"files":["index.ts"],"references":[{"path":"../lib"}]}`,
508+
appFilePath: "export const app = 1;",
509+
libConfigPath: `{"extends":"./tsconfig.base.json","files":["index.ts"]}`,
510+
libBaseConfigPath: `{"compilerOptions":{"composite":true,"noLib":true}}`,
511+
libFilePath: "export const lib = 1;",
512+
})
513+
defer session.Close()
514+
ctx := context.Background()
515+
516+
baseSnapshot, err := session.APIUpdate(ctx, FileChangeSummary{}, &APISnapshotRequest{
517+
OpenProjects: collections.NewSetFromItems(appConfigPath),
518+
})
519+
assert.NilError(t, err)
520+
defer baseSnapshot.Deref(session)
521+
appProject := baseSnapshot.ProjectCollection.GetProjectByPath(baseSnapshot.toPath(appConfigPath))
522+
assert.Assert(t, appProject != nil)
523+
524+
programSnapshot := session.APICreateProgram(
525+
ctx,
526+
appProject.CommandLine.FileNames(),
527+
appProject.CommandLine.CompilerOptions(),
528+
appProject.CommandLine.ProjectReferences(),
529+
appProject.CommandLine.Errors,
530+
baseSnapshot,
531+
appProject,
532+
FileChangeSummary{},
533+
)
534+
defer programSnapshot.Deref(session)
535+
programProject := programSnapshot.ProjectCollection.InferredProject()
536+
assert.Assert(t, programProject != nil)
537+
assert.Assert(t, programProject.Program == appProject.Program)
538+
539+
extendedConfigEntry, ok := session.extendedConfigCache.entries.Load(tspath.Path(libBaseConfigPath))
540+
assert.Assert(t, ok)
541+
extendedConfigEntry.mu.Lock()
542+
_, ownedByBaseSnapshot := extendedConfigEntry.owners[baseSnapshot.id]
543+
_, ownedByProgramSnapshot := extendedConfigEntry.owners[programSnapshot.id]
544+
ownerCount := len(extendedConfigEntry.owners)
545+
extendedConfigEntry.mu.Unlock()
546+
assert.Assert(t, ownedByBaseSnapshot)
547+
assert.Assert(t, ownedByProgramSnapshot)
548+
assert.Equal(t, ownerCount, 2)
549+
550+
assert.NilError(t, session.fs.fs.WriteFile(libBaseConfigPath, `{"compilerOptions":{"composite":true,"noLib":true,"strict":true}}`))
551+
var fileChanges FileChangeSummary
552+
fileChanges.Changed.Add(lsproto.DocumentUri("file://" + libBaseConfigPath))
553+
updatedProgramSnapshot := session.APICreateProgram(
554+
ctx,
555+
programProject.CommandLine.FileNames(),
556+
programProject.CommandLine.CompilerOptions(),
557+
programProject.CommandLine.ProjectReferences(),
558+
programProject.CommandLine.Errors,
559+
programSnapshot,
560+
programProject,
561+
fileChanges,
562+
)
563+
defer updatedProgramSnapshot.Deref(session)
564+
updatedProgramProject := updatedProgramSnapshot.ProjectCollection.InferredProject()
565+
assert.Assert(t, updatedProgramProject != nil)
566+
assert.Assert(t, updatedProgramProject.Program != programProject.Program)
567+
updatedReferences := updatedProgramProject.Program.GetResolvedProjectReferences()
568+
assert.Equal(t, len(updatedReferences), 1)
569+
assert.Equal(t, updatedReferences[0].CompilerOptions().Strict, core.TSTrue)
570+
})
494571
})
495572
}

0 commit comments

Comments
 (0)