Repository navigation
fix(lsp): pyright server ignores a project-local pyright-langserver in node_modules/.bin #147
Description
Problem
PyrightServer.spawn (packages/lsp/src/server/pyright.ts) has only two steps: a pyright-langserver on PATH, then the pinned bunx fallback from #146. It never looks for a project-local install:
const pyright = Bun.which('pyright-langserver', { PATH: process.env.PATH ?? '' })
if (!pyright) {
// bunx -p pyright@1.1.414 pyright-langserver --stdio
}The npm package pyright is a common devDependency, often added so CI can run npx pyright. Its pyright-langserver bin lands in node_modules/.bin, which is not on PATH outside an npm script. So for a project that pins pyright locally:
- With no global install, the server ignores the project's copy. It runs bunx, which downloads pyright 1.1.414 from the registry. That needs the network and can be a different version from the one the project pins.
- With a global install, the server uses that copy instead of the project's version.
Either way the editor's diagnostics can differ from what the project's own pyright run reports.
The other npm-distributed servers check the project first:
| Server | Project-local lookup |
|---|---|
biome.ts |
<root>/node_modules/.bin/biome, checked with fs.access before PATH |
oxlint.ts, eslint.ts |
<root>/node_modules/.bin/<bin> |
typescript.ts |
findPackageBin(root, 'typescript-language-server', 'typescript-language-server') walks up from root to the nearest node_modules/<package>. It checks that the .bin shim really belongs to that package (resolvePackageBin), then falls back to PATH |
pyright.ts |
none |
Expected
Lookup order: project-local, then PATH, then bunx.
const pyright = findPackageBin(root, 'pyright', 'pyright-langserver')
?? Bun.which('pyright-langserver', { PATH: process.env.PATH ?? '' })- Walk up from the root, as
typescript.tsdoes, rather than checking only<root>/node_modules. Pyright's root comes frompyproject.toml,setup.py,requirements.txtorpyrightconfig.json. In a mixed repo the Python project is often a subdirectory such asbackend/, whilepackage.jsonandnode_modulessit at the repository root. A<root>/node_modules/.bincheck likebiome.ts's would miss that layout. findPackageBinandresolvePackageBinare private totypescript.tstoday. Moving them toutils.tslets both servers share them.- Check existence only; don't probe the binary.
pyright-langserverhas no--versionflag (it errors with "Connection input stream is not set"), sorunsVersioncan't vet it.
Tests
Add these to the PyrightServer fallback describe in packages/lsp/test/unit/server.test.ts, using spawnServerWith / recorder:
- Project-local wins: prefer a project-local
pyright-langserverover one onPATHand over bunx. Expectlocal --stdio. - Walk-up: find a
node_modulesin a parent of the root, for example a root at<tmp>/backendwithnode_modulesat<tmp>. - Shim ownership: ignore a
node_modules/.bin/pyright-langservershim that doesn't belong to thepyrightpackage. This applies only ifresolvePackageBinis reused.
Notes
- Follow-up to fix(lsp): pyright bunx fallback requests a nonexistent npm package, is unpinned, and never detects a missing bunx #137 / fix(lsp): run a pinned pyright in the pyright bunx fallback #146. fix(lsp): pyright bunx fallback requests a nonexistent npm package, is unpinned, and never detects a missing bunx #137 noted this gap as out of scope.
- Out of scope: handling a broken mise shim on
PATH. That needs a probe other than--version.
Activity