Skip to content

fix(lsp): skip an unrunnable typescript-language-server and fall back to bunx - #131

Merged
amondnet merged 3 commits into
mainfrom
amondnet/127-vue-integration
Oct 22, 2026
Merged

amondnet merged 3 commits into
mainfrom
amondnet/127-vue-integration

Conversation

@amondnet

@amondnet amondnet commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

In the Vue test fixture, Bun.which('typescript-language-server') resolved to a mise shim that exits 1 ("mise ERROR No version is set for shim: typescript-language-server") because no Node version is active there. TypescriptServer.spawn launched it, it exited at once, touchFile waited for diagnostics that never came, and three Vue integration tests timed out at the 5s per-test limit. The last one failed with ConnectionError: Connection is closed.

The server now probes the binary with --version and skips it when it cannot run. The existing unversioned bunx typescript-language-server fallback would not help, because bunx checks PATH first and runs the same shim. The fallback therefore runs typescript-language-server@latest, so bunx installs from the registry (printed 6.0.2).

What changed

  • The --version probe from fix(lsp): fall back to downloaded dart sdk when system dart cannot run #129 moves into an internal helper findRunnableBin(name, root) in packages/lsp/src/server/utils.ts. It is not added to the public re-export list.
  • TypescriptServer uses it for typescript-language-server and falls back to bunx typescript-language-server@latest. DartServer calls the same helper; its behavior is unchanged.
  • The probe unit tests move from findSystemDart to findRunnableBin and use a generic fake binary.

Scope

Covers the typescript-language-server path and the shared probe. It does not touch the biome fallback (see Follow-up).

Blast radius

packages/lsp only. Machines where typescript-language-server runs take the same path as before. Only an unrunnable binary now falls back to bunx.

Verification

Run locally with Bun 1.4.3:

  • vue.integration.test.ts: 8 pass / 3 fail before, 11 pass / 0 fail after. The typescript server connects in the fixture in about 3.6s and workspaceSymbol('add') finds it.
  • bun test ./test/unit: 164 pass / 0 fail.
  • Full bun test in packages/lsp: 191 pass / 9 skip / 0 fail. The 9 skips are the Dart integration suite, skipped because dart here is only a broken mise shim.
  • bun run typecheck passes and bun run lint exits 0.

Follow-up (not fixed here)

biome's bunx biome fallback installs the unrelated npm package biome (0.3.3) instead of @biomejs/biome. That package does not understand lsp-proxy, so biome initialize times out after 45s in the Vue fixture. It slows the suite by about 50s but fails no test.

Related issue

Closes #127

Checklist

  • PR title follows Conventional Commits
  • Tests added or updated (probe unit tests moved to findRunnableBin; packages/lsp suite passes locally)
  • Full repo-wide bun run test (left to CI)
  • Lint/format pass (bun run lint)
  • Documentation updated if behavior changed (no user-facing behavior change)
  • No breaking change

Summary by cubic

Fixes Vue integration tests timing out when typescript-language-server on PATH is an unrunnable mise shim that exits 1 before the server can connect.

  • Introduces findRunnableBin, a shared helper that probes each PATH entry with --version and skips any that fail to run.
  • TypescriptServer now prefers a project-local typescript-language-server (probed with --version), falls back to findRunnableBin, then to bunx typescript-language-server@4.3.3, which installs from the registry instead of reusing the shim.
  • Launches the classic server through a shell on Windows so .cmd shims run.
  • DartServer uses the same helper; its behavior is unchanged.
  • Probe unit tests moved to findRunnableBin and made generic; fallback order is tested.

Closes #127.

Written for commit f43239f. Summary will update on new commits.

Turn on auto-fix

… to bunx

A mise shim for typescript-language-server resolved on PATH but exited 1
("No version is set for shim") in the Vue fixture, so the server died and
three Vue integration tests timed out. Unversioned bunx also picked up the
shim. Share the dart --version probe as findRunnableBin and run the bunx
fallback as typescript-language-server@latest so it installs from the
registry.

Closes #127
Comment thread packages/lsp/src/server/typescript.ts Fixed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the LSP server implementations by replacing the Dart-specific findSystemDart helper with a generic findRunnableBin utility. This utility is then used by both the Dart and TypeScript servers to locate and verify runnable binaries. The review feedback suggests prioritizing project-local installations of typescript-language-server before falling back to global or bunx executions, and making the log messages in findRunnableBin more generic to support non-PATH binary resolutions.

Comment thread packages/lsp/src/server/typescript.ts Outdated
Comment thread packages/lsp/src/server/utils.ts
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.79245% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/lsp/src/server/typescript.ts 71.42% 4 Missing ⚠️
packages/lsp/src/server/utils.ts 94.28% 2 Missing ⚠️
packages/lsp/src/server/dart.ts 50.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@amondnet
amondnet marked this pull request as ready for review October 22, 2026 10:12
@greptile-apps

greptile-apps Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with no outstanding findings.

Summary

The PR skips language-server binaries that cannot run and gives TypeScript a pinned bunx fallback.

  • TypeScript skips unrunnable servers and falls back to pinned Bunx.
  • Dart finds a working system binary later in PATH.

Reviews (3) · Last reviewed commit: "chore(lsp): word runsVersion warnings fo..." · Reviewed by Greptile

Comment thread packages/lsp/test/unit/server.test.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 4 files

Architecture diagram
sequenceDiagram
    participant TS as TypescriptServer
    participant FRB as findRunnableBin()
    participant PATH as PATH Binary
    participant BUNX as bunx CLI
    participant REG as npm Registry
    participant LSP as Language Server Process

    Note over TS,LSP: LSP Server Discovery and Launch Flow

    TS->>FRB: findRunnableBin('typescript-language-server', root)
    FRB->>PATH: Bun.which('typescript-language-server', PATH)
    alt Binary found on PATH
        PATH-->>FRB: /path/to/typescript-language-server
        FRB->>PATH: spawn --version (cwd=root, 10s timeout)
        alt --version exit 0
            PATH-->>FRB: exit 0
            FRB-->>TS: Return binary path
            TS->>LSP: spawn(server, ['--stdio'])
            LSP-->>TS: LSP over stdio
        else --version exit non-zero
            PATH-->>FRB: exit 1 (mise shim, no version set)
            FRB-->>TS: Return undefined
            TS->>BUNX: spawn('bunx', ['typescript-language-server@latest', '--stdio'])
            BUNX->>REG: Install typescript-language-server@latest
            REG-->>BUNX: Package (e.g. 6.0.2)
            BUNX-->>TS: Spawned LSP
            TS->>LSP: LSP over stdio
        end
    else Binary not found
        PATH-->>FRB: undefined
        FRB-->>TS: Return undefined
        TS->>BUNX: spawn('bunx', ['typescript-language-server@latest', '--stdio'])
        BUNX->>REG: Install typescript-language-server@latest
        REG-->>BUNX: Package
        BUNX-->>TS: Spawned LSP
        TS->>LSP: LSP over stdio
    end

    Note over TS,LSP: DartServer shares the same probe helper

    TS->>FRB: findRunnableBin('dart', root)
    alt Runnable dart found
        FRB-->>TS: /path/to/dart
        TS->>LSP: spawn(dart, ['language-server', ...])
    else Not runnable
        FRB-->>TS: undefined
        TS->>LSP: Fallback to dart download
    end
Loading

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread packages/lsp/src/server/typescript.ts Outdated
Comment thread packages/lsp/src/server/utils.ts Outdated
Comment thread packages/lsp/src/server/typescript.ts Outdated
- Prefer a project-local typescript-language-server, probing it with --version first
- Probe every PATH match in findRunnableBin instead of only the first
- Pin the bunx fallback to the typescript-language-server version the Vue server installs
- Launch the classic server through a shell on Windows so .cmd shims run
- Test the TypescriptServer.spawn fallback order
@amondnet

Copy link
Copy Markdown
Contributor Author

/gemini review

Comment thread packages/lsp/src/server/typescript.ts Dismissed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refactors binary resolution across the LSP servers by introducing a generic findRunnableBin utility in utils.ts that probes binaries on the PATH using version checks. It replaces the custom findSystemDart logic, improves the TypeScript server fallback mechanism to prefer project-local installations, and pins the fallback typescript-language-server version. Feedback on the changes highlights a critical issue in the new runsVersion utility, where spawning the version probe on Windows can fail or crash the application without enabling the shell option for .cmd/.bat shims and wrapping the Bun.spawn call in a try/catch block.

Comment thread packages/lsp/src/server/utils.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 4 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread packages/lsp/src/server/utils.ts
Comment thread packages/lsp/src/server/utils.ts
@sonarqubecloud

sonarqubecloud Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Quality Gate Passed Quality Gate passed

Issues
0 New issues
1 Accepted issue

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

@amondnet

Copy link
Copy Markdown
Contributor Author

/gemini review

@amondnet
amondnet merged commit 15954b7 into main Oct 22, 2026
10 checks passed
@amondnet
amondnet deleted the amondnet/127-vue-integration branch October 22, 2026 10:42

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refactors binary resolution across LSP servers by introducing a generic findRunnableBin utility that verifies binaries on the PATH using a --version probe. It also updates the TypeScript server to prioritize project-local installations before falling back to the system path or a pinned version via bunx. Feedback highlights a high-severity compatibility issue on Windows within the new runsVersion helper, where spawning Node-based batch file shims directly without a shell will fail, and recommends wrapping the spawn call in a try/catch block to handle synchronous OS-level errors.

Comment on lines +79 to +84
const proc = Bun.spawn([bin, '--version'], {
cwd: root,
stdout: 'ignore',
stderr: 'ignore',
timeout: VERSION_PROBE_TIMEOUT_MS,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

[HIGH] Windows compatibility and error handling issues in runsVersion

Problem: On Windows, typescript-language-server and other Node-based tools are installed as .cmd or .bat batch file shims. Spawning these files directly via Bun.spawn without a shell will fail, causing runsVersion to always return false on Windows. Additionally, spawning process calls (such as Bun.spawn) when probing system binaries on the PATH can throw synchronous OS-level errors (e.g., ENOEXEC due to invalid ELF headers, or permission issues) which must be caught to prevent application crashes.

Rationale: Correctness, cross-platform compatibility, and robustness.

Suggestion: Pass shell: process.platform === 'win32' to Bun.spawn to ensure batch file shims can be executed on Windows, and wrap the Bun.spawn call in a try/catch block to handle synchronous errors gracefully.

Suggested change
const proc = Bun.spawn([bin, '--version'], {
cwd: root,
stdout: 'ignore',
stderr: 'ignore',
timeout: VERSION_PROBE_TIMEOUT_MS,
})
let proc;
try {
proc = Bun.spawn([bin, '--version'], {
cwd: root,
stdout: 'ignore',
stderr: 'ignore',
timeout: VERSION_PROBE_TIMEOUT_MS,
shell: process.platform === 'win32',
});
} catch (e) {
return false;
}
References
  1. Always wrap process spawning calls (such as Bun.spawn) in a try/catch block when probing system binaries on the PATH. OS-level errors (e.g., ENOEXEC due to invalid ELF headers, or permission issues) can cause the spawn call to throw synchronous errors, which must be caught to prevent application crashes and allow graceful fallbacks.

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.

VueServer and DartServer integration tests fail locally in packages/lsp

2 participants