GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fix(language-server): recover project for external templates in solution-style workspaces by ShayanAbbas1 · Pull Request #69769 · angular/angular

Conversation

@ShayanAbbas1

@ShayanAbbas1 ShayanAbbas1 commented Jul 14, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

PR Checklist

  • The commit message follows our guidelines
  • Tests for the changes have been added (see note below)
  • Docs have been added / updated (not applicable)

PR Type

  • Bugfix

What is the current behavior?

In solution-style workspaces (the default Nx layout, where an app's tsconfig.json contains only project references), an external template that loses its configured-project association after being opened — e.g. because its component .ts file was closed and the project graph updated — permanently loses all language service features. Every subsequent request on the template hits getDefaultProjectForScriptInfo, whose bare openClientFile lookup can never resolve a config file for an HTML file, so it logs No config file for ... and returns null indefinitely until the user manually reopens the component file.

angular/vscode-ng-language-service#2165 fixed this same failure mode for the document-open path only; the request-time recovery path was left unpatched.

Reproduced in VS Code itself (Angular extension v22.0.1, server 22.x) with a template-opened-first session, in Zed, and with a raw LSP stdio client — details and Angular Language Service logs in #69768.

Issue Number: Fixes #69768

What is the new behavior?

getDefaultProjectForScriptInfo applies the same best-effort as onDidOpenTextDocument when the config lookup comes back empty for an external template: briefly open/close the sibling .ts file (guarded by projectService.openFiles, same as #2165) and retry. If the lookup still fails — openClientFile does not repeat the config search for an already-open file — the template's script info is attached directly to the configured project of its component.

Validated against a large Nx monorepo (Angular 19, ~30 projects) by driving @angular/language-server over raw LSP: with stock 21.1.5, definition requests on an orphaned template fail with No config file indefinitely (40+ consecutive failures reproduced); with this change the template re-attaches on the next request and features recover every time. This patch has also been in daily editor use (Zed) on that workspace.

Note on tests: server/src/session.ts currently has no unit spec, and the integration fixtures under integration/project are not solution-style. Happy to add a solution-style fixture + integration test if the reviewers can point me at the preferred shape.

Does this PR introduce a breaking change?

  • No

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@google-cla

google-cla Bot commented Jul 14, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@pullapprove
pullapprove Bot requested a review from kirjs July 14, 2026 15:30
@JeanMeche
JeanMeche requested review from atscott and removed request for kirjs July 14, 2026 16:04
@pkozlowski-opensource pkozlowski-opensource added the area: language-service Issues related to Angular's VS Code language service label Jul 22, 2026
@ngbot ngbot Bot added this to the Backlog milestone Jul 22, 2026
@ShayanAbbas1

This comment was marked as outdated.

Sign in to view
atscott
atscott reviewed Aug 11, 2026
View reviewed changes

@atscott atscott left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

AGENT: I have left an inline suggestion for refactoring to share code and avoid duplication.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread vscode-ng-language-service/server/src/session.ts
// not needed.
if (!project || project.projectKind !== ts.server.ProjectKind.Configured) {
const {configFileName} = this.projectService.openClientFile(scriptInfo.fileName);
let {configFileName} = this.projectService.openClientFile(scriptInfo.fileName);

@atscott atscott Aug 11, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

AGENT: Thanks for the fix! This is indeed necessary to recover the project association when it is lost after the template is already open.

However, the logic to recover the project by opening/closing the sibling TS file is duplicated from onDidOpenTextDocument. We should extract this into a shared helper method to keep the code clean and ensure consistent behavior (including the manual attachment fallback, which might also be beneficial for onDidOpenTextDocument).

I have verified this refactoring locally. Here is the suggested helper method and how it would simplify the code:

  /**
   * Attempt to recover a configured project for an external template by opening
   * its sibling TS file. This is a best-effort recovery for solution-style workspaces.
   */
  private tryRecoverProjectForExternalTemplate(
    scriptInfo: ts.server.ScriptInfo,
  ): ts.server.Project | null {
    if (!isExternalTemplate(scriptInfo.fileName)) {
      return null;
    }
    const maybeComponentTsPath = scriptInfo.fileName.replace(/\.html$/, '.ts');
    if (!this.projectService.openFiles.has(this.projectService.toPath(maybeComponentTsPath))) {
      this.projectService.openClientFile(maybeComponentTsPath);
      this.projectService.closeClientFile(maybeComponentTsPath);
    }

    const componentProject = this.projectService
      .getScriptInfo(maybeComponentTsPath)
      ?.containingProjects.find(isConfiguredProject);
    if (componentProject) {
      scriptInfo.detachAllProjects();
      scriptInfo.attachToProject(componentProject);
      return componentProject;
    }
    return null;
  }

You can then call this in getDefaultProjectForScriptInfo when configFileName is not found, and in onDidOpenTextDocument when result.configFileName is undefined.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ShayanAbbas1 ShayanAbbas1 Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Extracted, though as two methods rather than one: loadProjectForExternalTemplate(templatePath): boolean
for the open/close of the sibling TS file, and attachToComponentProject(scriptInfo) for the manual
attachment. The split is what let me share the logic without changing onDidOpenTextDocument's
existing behavior:

  • The retry (result = openClientFile(filePath, text, scriptKind)) is still there. That's the
    mechanism from fix(language-server): Do best-effort to initialize composite project … vscode-ng-language-service#2165 and it works on the open path — replacing
    it with the manual attachment would be a behavior change on a path this PR isn't fixing, and one
    the repro for this issue wouldn't catch. Both call sites now have the same shape: open the
    sibling, retry the lookup, attach directly only if the lookup still comes back empty.
  • loadProjectForExternalTemplate returns whether it actually opened the component file, so the
    retry stays coupled to the "component not already open" guard. With a void helper the retry
    would start running in cases where it previously didn't.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ShayanAbbas1
ShayanAbbas1 force-pushed the fix-external-template-project-recovery branch 4 times, most recently from d7d0fd9 to 2836d12 Compare August 12, 2026 07:04
@ShayanAbbas1
ShayanAbbas1 requested a review from atscott August 12, 2026 07:07
atscott
atscott reviewed Aug 14, 2026
View reviewed changes
Comment thread vscode-ng-language-service/server/src/session.ts
*
* Returns whether the component file was opened, i.e. whether the config lookup for the template
* is worth retrying.
*/

@atscott atscott Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

AGENT: Consider checking !this.host.fileExists(maybeComponentTsPath) here to avoid attempting to open/close non-existent files (and walking directory trees looking for configs) when editing non-component HTML files like src/index.html:

Suggested change
*/
if (
this.projectService.openFiles.has(this.projectService.toPath(maybeComponentTsPath)) ||
!this.host.fileExists(maybeComponentTsPath)
) {

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ShayanAbbas1 ShayanAbbas1 Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Done

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ShayanAbbas1
fix(language-server): recover project for external templates in solut…
2b05055
…ion-style workspaces

In a composite/solution-style workspace (e.g. an Nx monorepo, where an
app's tsconfig.json only contains project references), TypeScript can
never resolve a config file for an HTML file, since HTML files are not
listed in any referenced project. angular/vscode-ng-language-service#2165
worked around this in onDidOpenTextDocument by briefly opening the
sibling TS file so the right project loads when a template is opened
first.

However, getDefaultProjectForScriptInfo - the recovery path used by
getLSAndScriptInfo and onDidChangeTextDocument when a script info has no
configured project - did not receive the same workaround. When an open
template loses its project association (e.g. its component file is
closed and the project graph updates), every subsequent request on the
template fails with "No config file" and returns null indefinitely,
until the user manually reopens the component file.

Apply the same sibling-TS best effort in getDefaultProjectForScriptInfo,
and additionally attach the template's script info to the configured
project of its component when the config lookup still comes back empty
(openClientFile does not repeat the config lookup for already-open
files).

Also skip the sibling lookup when the .ts file does not exist, so
non-component HTML files (e.g. src/index.html) do not trigger an
open/close and config search that cannot succeed.

Fixes angular#69768
@ShayanAbbas1
ShayanAbbas1 force-pushed the fix-external-template-project-recovery branch from 2836d12 to 2b05055 Compare August 14, 2026 21:10
@ShayanAbbas1
ShayanAbbas1 requested a review from atscott August 14, 2026 21:26
atscott
atscott approved these changes Aug 14, 2026
View reviewed changes
@atscott atscott added action: merge The PR is ready for merge by the caretaker target: patch This PR is targeted for the next patch release labels Aug 14, 2026
@thePunderWoman
thePunderWoman merged commit 3f8d9d6 into angular:main Aug 17, 2026
27 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@thePunderWoman

thePunderWoman commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

This PR was merged into the repository. The changes were merged into the following branches:

  • main: 3f8d9d6
  • 22.1.x: 14fbe04
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@angular-automatic-lock-bot

angular-automatic-lock-bot Bot commented Oct 21, 2026

Copy link
Copy Markdown

This pull request has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@angular-automatic-lock-bot angular-automatic-lock-bot Bot locked and limited conversation to collaborators Oct 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@atscott atscott atscott approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

action: merge The PR is ready for merge by the caretaker area: language-service Issues related to Angular's VS Code language service target: patch This PR is targeted for the next patch release

Projects

None yet

Milestone

Backlog

Development

Successfully merging this pull request may close these issues.

language-server: external templates permanently lose language service in solution-style (Nx) workspaces — getDefaultProjectForScriptInfo lacks the #2165 fallback

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@ShayanAbbas1 @thePunderWoman @atscott @pkozlowski-opensource
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.