Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
30 changes: 30 additions & 0 deletions internal/project/configfilechanges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,34 @@ func TestConfigFileChanges(t *testing.T) {
defer release()
assert.Equal(t, len(snapshot.ProjectCollection.Projects()), 1)
})

t.Run("should update project when missing extended config is created", func(t *testing.T) {
t.Parallel()
// Start with a project whose tsconfig extends a base config that doesn't exist yet
missingBaseFiles := map[string]any{}
for k, v := range files {
if k == "/tsconfig.base.json" {
continue
}
missingBaseFiles[k] = v
}

session, utils := projecttestutil.Setup(missingBaseFiles)
session.DidOpenFile(context.Background(), "file:///src/index.ts", 1, missingBaseFiles["/src/index.ts"].(string), lsproto.LanguageKindTypeScript)

// Create the previously-missing base config file that is extended by /src/tsconfig.json
err := utils.FS().WriteFile("/tsconfig.base.json", `{"compilerOptions": {"strict": true}}`, false /*writeByteOrderMark*/)
assert.NilError(t, err)
session.DidChangeWatchedFiles(context.Background(), []*lsproto.FileEvent{
{
Uri: lsproto.DocumentUri("file:///tsconfig.base.json"),
Type: lsproto.FileChangeTypeCreated,
},
})

// Accessing the language service should trigger project update
ls, err := session.GetLanguageService(context.Background(), lsproto.DocumentUri("file:///src/index.ts"))
assert.NilError(t, err)
assert.Equal(t, ls.GetProgram().Options().Strict, core.TSTrue)
})
}
8 changes: 6 additions & 2 deletions internal/project/extendedconfigcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ type extendedConfigCacheEntry struct {
func (c *extendedConfigCache) Acquire(fh FileHandle, path tspath.Path, parse func() *tsoptions.ExtendedConfigCacheEntry) *tsoptions.ExtendedConfigCacheEntry {
entry, loaded := c.loadOrStoreNewLockedEntry(path)
defer entry.mu.Unlock()
if !loaded || entry.hash != fh.Hash() {
var hash xxh3.Uint128
if fh != nil {
hash = fh.Hash()
}
if !loaded || entry.hash != hash {
// Reparse the config if the hash has changed, or parse for the first time.
entry.entry = parse()
entry.hash = fh.Hash()
entry.hash = hash
}
return entry.entry
}
Expand Down
43 changes: 43 additions & 0 deletions internal/project/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,46 @@
assert.Equal(t, configured.ProgramUpdateKind, project.ProgramUpdateKindSameFileNames)
})
}

func TestProject(t *testing.T) {

Check failure on line 134 in internal/project/project_test.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest)

TestProject's subtests should call t.Parallel (tparallel)
t.Parallel()
if !bundled.Embedded {
t.Skip("bundled files are not embedded")
}

t.Run("commandLineWithTypingsFiles is reset on CommandLine change", func(t *testing.T) {
files := map[string]any{
"/user/username/projects/project1/app.js": ``,
"/user/username/projects/project1/package.json": `{"name":"p1","dependencies":{"jquery":"^3.1.0"}}`,
"/user/username/projects/project2/app.js": ``,
}

session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{
PackageToFile: map[string]string{
// Provide typings content to be installed for jquery so ATA actually installs something
"jquery": `declare const $: { x: number }`,
},
})

// 1) Open an inferred project file that triggers ATA
uri1 := lsproto.DocumentUri("file:///user/username/projects/project1/app.js")
session.DidOpenFile(context.Background(), uri1, 1, files["/user/username/projects/project1/app.js"].(string), lsproto.LanguageKindJavaScript)

// 2) Wait for ATA/background tasks to finish, then get a language service for the first file
session.WaitForBackgroundTasks()
// Sanity check: ensure ATA performed at least one install
npmCalls := utils.NpmExecutor().NpmInstallCalls()
assert.Assert(t, len(npmCalls) > 0, "expected at least one npm install call from ATA")
_, err := session.GetLanguageService(context.Background(), uri1)

Check failure on line 163 in internal/project/project_test.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest)

ineffectual assignment to err (ineffassign)

// 3) Open another inferred project file
uri2 := lsproto.DocumentUri("file:///user/username/projects/project2/app.js")
session.DidOpenFile(context.Background(), uri2, 1, ``, lsproto.LanguageKindJavaScript)

// 4) Get a language service for the second file
// If commandLineWithTypingsFiles was not reset, the new program command line
// won't include the newly opened file and this will fail.
_, err = session.GetLanguageService(context.Background(), uri2)
assert.NilError(t, err)
})
}
6 changes: 5 additions & 1 deletion internal/project/projectcollectionbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ func (b *projectCollectionBuilder) updateInferredProjectRoots(rootFileNames []st
logger.Log(fmt.Sprintf("Updating inferred project config with %d root files", len(rootFileNames)))
}
p.CommandLine = newCommandLine
p.commandLineWithTypingsFiles = nil
p.dirty = true
p.dirtyFilePath = ""
},
Expand Down Expand Up @@ -753,7 +754,10 @@ func (b *projectCollectionBuilder) updateProgram(entry dirty.Value[*Project], lo
filesChanged = true
return
}
entry.Change(func(p *Project) { p.CommandLine = commandLine })
entry.Change(func(p *Project) {
p.CommandLine = commandLine
p.commandLineWithTypingsFiles = nil
})
}
}
if !updateProgram {
Expand Down
20 changes: 11 additions & 9 deletions internal/tsoptions/tsconfigparsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -961,20 +961,22 @@ func getExtendedConfig(
cacheEntry = parse()
}

if cacheEntry != nil && len(cacheEntry.errors) > 0 {
if len(cacheEntry.errors) > 0 {
errors = append(errors, cacheEntry.errors...)
}

if sourceFile != nil {
result.extendedSourceFiles.Add(cacheEntry.extendedResult.SourceFile.FileName())
for _, extendedSourceFile := range cacheEntry.extendedResult.ExtendedSourceFiles {
result.extendedSourceFiles.Add(extendedSourceFile)
if cacheEntry.extendedResult != nil {
if sourceFile != nil {
result.extendedSourceFiles.Add(cacheEntry.extendedResult.SourceFile.FileName())
for _, extendedSourceFile := range cacheEntry.extendedResult.ExtendedSourceFiles {
result.extendedSourceFiles.Add(extendedSourceFile)
}
}
}

if len(cacheEntry.extendedResult.SourceFile.Diagnostics()) != 0 {
errors = append(errors, cacheEntry.extendedResult.SourceFile.Diagnostics()...)
return nil, errors
if len(cacheEntry.extendedResult.SourceFile.Diagnostics()) != 0 {
errors = append(errors, cacheEntry.extendedResult.SourceFile.Diagnostics()...)
return nil, errors
}
}
return cacheEntry.extendedConfig, errors
}
Expand Down
Loading