Skip to content

Fallback to built-in TypeScript when project lacks typescript dependency#1105

Open
NullVoxPopuli-ai-agent wants to merge 3 commits intotyped-ember:mainfrom
NullVoxPopuli-ai-agent:nvp/fallback-builtin-typescript
Open

Fallback to built-in TypeScript when project lacks typescript dependency#1105
NullVoxPopuli-ai-agent wants to merge 3 commits intotyped-ember:mainfrom
NullVoxPopuli-ai-agent:nvp/fallback-builtin-typescript

Conversation

@NullVoxPopuli-ai-agent
Copy link
Copy Markdown

Summary

  • When a project has @glint/ember-tsc (or @glint/template) but not typescript as a direct dependency, the language server crashes on startup because the static ESM import * as ts from 'typescript' fails
  • This adds a fallback chain: try the project's TypeScript first, then fall back to VS Code's built-in TypeScript via a GLINT_TYPESCRIPT_PATH env var set by the extension
  • ConfigLoader and findConfig() now accept an optional fallback TypeScript instance, so config loading (tsconfig resolution) also works without a project-level TypeScript
  • The tsserver plugin passes its host ts to findConfig(), ensuring proper config loading in that path too

Test plan

  • pnpm build succeeds
  • pnpm lint passes
  • Create a test project: pnpm dlx ember-cli@latest new my-app --pnpm
  • Add @glint/ember-tsc to package.json but do NOT add typescript
  • Open in VS Code with the Glint extension — language server should start without crashing
  • Verify existing projects with typescript installed still work as before

🤖 Generated with Claude Code

When a project has @glint/ember-tsc but not typescript as a direct
dependency, the language server would crash on startup because the
static `import * as ts from 'typescript'` fails. This adds a fallback
chain so Glint can still work using VS Code's built-in TypeScript.

Changes:
- Language server resolves TypeScript dynamically via createRequire with
  fallback to GLINT_TYPESCRIPT_PATH env var
- ConfigLoader accepts an optional fallback TypeScript instance
- findConfig() accepts an optional fallback TypeScript parameter
- TSServer plugin passes host ts to findConfig for config loading
- VS Code extension sets GLINT_TYPESCRIPT_PATH to VS Code's built-in
  TypeScript when launching the language server

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@NullVoxPopuli
Copy link
Copy Markdown
Contributor

This seems relevant, #1102 (comment) -- would this work / clean up anything in this PR?

Adds support for Eclipse Theia in addition to VS Code, and validates
paths with fs.existsSync before setting the env var.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@NullVoxPopuli-ai-agent
Copy link
Copy Markdown
Author

Good call! Updated the extension to use a resolveTsdkPath() helper based on that approach — validates paths with fs.existsSync and also supports Eclipse Theia's TypeScript location. See eec8f0f.


throw new Error(
'TypeScript could not be resolved. Install `typescript` as a dependency, ' +
'or ensure the Glint VS Code extension provides a fallback TypeScript path.',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"ensure glint VS Code extension provides a fallback TypeScript path"

this is our job, we should not tell the user to do this

Remove reference to internal extension mechanism — users just need to
install typescript.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@NullVoxPopuli-ai-agent
Copy link
Copy Markdown
Author

Good question! These two functions aren't duplicated — they serve different roles in different processes:

  • resolveTsdkPath() (VS Code extension process) — finds the editor's built-in TypeScript SDK path using vscode.env.appRoot and vscode.extensions.getExtension(). This is the only place that has access to the vscode API.
  • resolveTypeScript() (language server child process) — does require('typescript') with a fallback to GLINT_TYPESCRIPT_PATH. It has no access to the vscode API, so the env var is the bridge between the two.

The flow is: extension finds the path → passes it via env var → language server consumes it as a fallback.

@evoactivity
Copy link
Copy Markdown
Contributor

@NullVoxPopuli is this in a reviewable state? your bot didn't check of it's plan so not sure if it considers this finished.

@NullVoxPopuli
Copy link
Copy Markdown
Contributor

I haven't reviewed it myself yet, but anyone is welcome to attempt to continue to work until i get around to finishing it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants