Skip to content

fix(lsp): pyright server ignores a project-local pyright-langserver in node_modules/.bin #147

Closed

Description

@amondnet

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.ts does, rather than checking only <root>/node_modules. Pyright's root comes from pyproject.toml, setup.py, requirements.txt or pyrightconfig.json. In a mixed repo the Python project is often a subdirectory such as backend/, while package.json and node_modules sit at the repository root. A <root>/node_modules/.bin check like biome.ts's would miss that layout.
  • findPackageBin and resolvePackageBin are private to typescript.ts today. Moving them to utils.ts lets both servers share them.
  • Check existence only; don't probe the binary. pyright-langserver has no --version flag (it errors with "Connection input stream is not set"), so runsVersion can'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-langserver over one on PATH and over bunx. Expect local --stdio.
  • Walk-up: find a node_modules in a parent of the root, for example a root at <tmp>/backend with node_modules at <tmp>.
  • Shim ownership: ignore a node_modules/.bin/pyright-langserver shim that doesn't belong to the pyright package. This applies only if resolvePackageBin is reused.

Notes

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    lsptype:bugSomething isn't working

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions