Return an updated program for which it is known that only the file with the given path has changed. In addition to a new program, return a boolean indicating whether the data of the old program was reused. createCheckerPool, if non-nil, overrides the CreateCheckerPool stored in the old program's opt
(changedFilePath tspath.Path, newHost CompilerHost, createCheckerPool func(*Program) CheckerPool)
| 291 | // host-side parse caches must release this exact pointer when the old program could not be |
| 292 | // reused, since it was acquired speculatively before that decision was made. |
| 293 | func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHost, createCheckerPool func(*Program) CheckerPool) (*Program, *ast.SourceFile, bool) { |
| 294 | newOpts := p.opts |
| 295 | newOpts.Host = newHost |
| 296 | if createCheckerPool != nil { |
| 297 | newOpts.CreateCheckerPool = createCheckerPool |
| 298 | } |
| 299 | |
| 300 | oldFile := p.filesByPath[changedFilePath] |
| 301 | newFile := newHost.GetSourceFile(oldFile.ParseOptions()) |
| 302 | |
| 303 | // If this file is part of a package redirect group (same package installed in multiple |
| 304 | // node_modules locations), we need to rebuild the program because the redirect targets |
| 305 | // might need recalculation. |
| 306 | _, inRedirectFiles := p.redirectFilesByPath[changedFilePath] |
| 307 | _, isRedirectTarget := p.redirectTargetsMap[changedFilePath] |
| 308 | if inRedirectFiles || isRedirectTarget { |
| 309 | return NewProgram(newOpts), newFile, false |
| 310 | } |
| 311 | |
| 312 | if !canReplaceFileInProgram(oldFile, newFile) { |
| 313 | return NewProgram(newOpts), newFile, false |
| 314 | } |
| 315 | if oldNeedsImportHelpers := p.importHelpersImportSpecifiers[oldFile.Path()] != nil; oldNeedsImportHelpers != p.needsImportHelpersImportSpecifier(newFile) { |
| 316 | return NewProgram(newOpts), newFile, false |
| 317 | } |
| 318 | // TODO: reverify compiler options when config has changed? |
| 319 | result := &Program{ |
| 320 | opts: newOpts, |
| 321 | comparePathsOptions: p.comparePathsOptions, |
| 322 | processedFiles: p.processedFiles, |
| 323 | usesUriStyleNodeCoreModules: p.usesUriStyleNodeCoreModules, |
| 324 | programDiagnostics: p.programDiagnostics, |
| 325 | hasEmitBlockingDiagnostics: p.hasEmitBlockingDiagnostics, |
| 326 | } |
| 327 | result.unresolvedImports.tryReuse(&p.unresolvedImports) |
| 328 | result.knownSymlinks.tryReuse(&p.knownSymlinks) |
| 329 | result.packageNames.tryReuse(&p.packageNames) |
| 330 | result.initCheckerPool() |
| 331 | index := core.FindIndex(result.files, func(file *ast.SourceFile) bool { return file.Path() == newFile.Path() }) |
| 332 | result.files = slices.Clone(result.files) |
| 333 | result.files[index] = newFile |
| 334 | result.filesByPath = maps.Clone(result.filesByPath) |
| 335 | result.filesByPath[newFile.Path()] = newFile |
| 336 | updateFileIncludeProcessor(result) |
| 337 | return result, newFile, true |
| 338 | } |
| 339 | |
| 340 | func (p *Program) initCheckerPool() { |
| 341 | if !p.finishedProcessing { |
no test coverage detected