Repository navigation
feat(lsp): prefer tsrs for the TypeScript LSP when installed - #124
Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…wrapper scripts on PATH
…e quoted wrapper paths
…t exercise ordering
…bient TSRS_BINARY A PATH entry that disappears between Bun.which and realpathSync now falls back instead of throwing out of spawn. The typescript-native tests clear TSRS_BINARY so a developer's exported override cannot change their results, and packages/lsp/CLAUDE.md now describes the single launcher-resolved platform package.
[Medium impact] The PR appears safe to merge; the previously reported Windows fixture issue is fixed. SummaryThe PR prefers usable project-local
Reviews (3) · Last reviewed commit: "test(lsp): mirror the Windows global npm..." · Reviewed by Greptile |
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Architecture diagram
sequenceDiagram
participant LSP as LSPManager (spawn)
participant Resolver as resolveNativeTypeScriptServer
participant FS as File System / node_modules
participant PATH as PATH / Global Bins
participant Proc as Child Process (LSP)
Note over LSP,Proc: TypeScript LSP selection flow (project root)
LSP->>Resolver: resolveNativeTypeScriptServer(root)
alt Project-local tsrs installed
Resolver->>FS: findPackageBin(root, "tsrs")
FS-->>Resolver: tsrs bin path (or undefined)
alt tsrs bin found
Resolver->>FS: hasTsrsBinary(packageRoot, root)
alt TSRS_BINARY set
FS-->>Resolver: exists(path.resolve(root, TSRS_BINARY))
else TSRS_BINARY unset
Resolver->>FS: resolve @tsrs/<platform> package.json
FS-->>Resolver: platform binary exists?
end
alt Binary available
Resolver-->>LSP: { command: bin, label: "tsrs" }
else No binary (e.g. darwin-x64)
Resolver->>Resolver: fall through to tsc
end
end
end
alt Project-local native tsc (TS >= 7)
Resolver->>FS: findTypeScriptPackageRoot(root)
FS-->>Resolver: packageRoot
alt isNativeTypeScriptPackage(packageRoot)
Resolver-->>LSP: { command: .bin/tsc, label: "native tsc" }
end
end
alt Project-local tsgo (@typescript/native-preview)
Resolver->>FS: findPackageBin(root, "@typescript/native-preview", "tsgo")
FS-->>Resolver: tsgo bin path
alt tsgo found
Resolver-->>LSP: { command: tsgo, label: "native tsgo" }
end
end
alt Global tsrs on PATH
Resolver->>PATH: Bun.which("tsrs")
PATH-->>Resolver: global bin path (or undefined)
alt Global tsrs found
Resolver->>FS: realpathSync(bin)
alt realpath fails (disappeared)
Resolver->>Resolver: return undefined (fall back)
else realpath succeeds
alt npm launcher (symlink/global package)
Resolver->>FS: check package.json name === "tsrs"
alt package found
Resolver->>FS: hasTsrsBinary(packageRoot, root)
alt platform binary installed
Resolver-->>LSP: { command: bin, label: "global tsrs" }
else no platform binary
Resolver->>Resolver: skip (fall through)
end
end
else native executable (ELF/Mach-O/PE)
Resolver->>FS: isNativeExecutable(realBin)
alt is native binary
Resolver-->>LSP: { command: bin, label: "global tsrs" }
else wrapper script (pnpm/shim)
Resolver->>FS: findWrapperPackageRoot(script, bin)
alt package root found
Resolver->>FS: hasTsrsBinary(packageRoot, root)
alt platform binary installed
Resolver-->>LSP: { command: bin, label: "global tsrs" }
else no binary
Resolver->>Resolver: skip (fall through)
end
else no package root
Resolver->>Resolver: skip (fall through)
end
end
end
end
end
end
alt Native server selected
LSP->>Proc: spawn(command, ["--lsp", "--stdio"])
Note over Proc: CHANGED: Windows command quoted, shell enabled
Proc-->>LSP: Process running
LSP-->>LSP: log "Using <label> language server"
else No native server found
LSP->>LSP: fall back to classic typescript-language-server --stdio
end
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
Make the libc and platform-package helpers injectable and test them with independent expected names, extract the Windows-quoted spawn arguments, exercise the global npm launcher with an executable target, and align the tsrs docs with the resolver's fallback order and root markers.
There was a problem hiding this comment.
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
… tests npm on Windows writes a global <prefix>\tsrs.cmd beside node_modules, but the fixture put the shim in node_modules\.bin, which resolveGlobalTsrs does not recognize, so the positive global-launcher test failed there.
|



Summary
The
typescriptLSP server now prefers a project-localtsrs, the Rust port of TypeScript 7 (announcement). Per tsrs'sdocs/LSP.md,tsrs --lsp --stdiospeaks the same protocol astsgo --lsp --stdio, including pull diagnostics, which the client already supports.Resolution order: project-local tsrs, then native
tsc(TS ≥ 7), then tsgo (@typescript/native-preview), then atsrsonPATH, then classic typescript-language-server. There is still a single server id and no config toggle, so diagnostics are not duplicated.What changed
TSRS_BINARYpointing at an existing file, or an installed@tsrs/*optional dependency whose os/cpu match the host and which contains thetsrsexecutable. This mirrors the package'slib/getExePath.js.packages/lsp/CLAUDE.md, rootCLAUDE.md.Scope
A
tsrsonPATHis used as a fallback when the project has no local tsrs, nativetsc, or tsgo, so a compiler the project pins still wins. When the PATH entry is a global npm launcher (a symlink intonode_modules/tsrs, or a Windowstsrs.cmdnext tonode_modules/tsrs), it gets the same platform-binary check. Any other PATH entry, such as acargo-built binary, is used as is.Known gap: a pnpm global install puts a wrapper script on PATH that can't be traced back to the package, so on an unsupported platform that case is not guarded.
Verification
packages/lsp/test/unit/typescript-native.test.ts: tsrs preferred over tsc/tsgo, tsrs hoisted in a parentnode_modules, tsrs without a platform binary falling back to tsc, a tsrs binary on PATH used as a fallback, a global npm launcher without a platform binary skipped, and a local native tsc preferred over a global tsrs. The suite runs with an isolated emptyPATH. The native/client unit tests pass (34); typecheck and eslint are clean.oven/bun:1.3.5) with the realtsrs@0.18.0-ts7.1.0-dev.20260929: LSPManager logged "Using tsrs language server" and returned diagnostics ("Type 'string' is not assignable to type 'number'.", "'x' is declared but its value is never read."); hover returnedconst x: number.npm install -g tsrson PATH.bun add -g tsrs: LSPManager logged "Using global tsrs language server" and returned the expected diagnostics.Related issue
None.
Checklist
bun run test)bun run lint)BREAKING CHANGE:note is includedSummary by cubic
The TypeScript LSP now prefers a project-local
tsrs(the Rust port of TypeScript 7) when its native binary is available, falling back to nativetsc,tsgo, a globaltsrsonPATH, then the classic typescript-language-server. Previously only nativetsc/tsgowere auto-selected, and on unsupported platforms like darwin-x64 the tsrs wrapper exited 1 and left TypeScript without an LSP after a 45s timeout.Behavior details
tsc(TS ≥ 7),tsgo, globaltsrsonPATH, then the classic server, all under one server id so diagnostics aren't duplicated.TSRS_BINARYresolves to an existing binary (relative paths against the project root) or an installed@tsrs/*platform package matches the host os/cpu — including the launcher's-musl/-gnuselection on Linux — and contains the executable.<prefix>\tsrs.cmdlayout.packages/lsp/CLAUDE.md; unit tests cover the resolution order, unsupported-platform and missing-binary fallbacks, hoisted monorepo installs, the global PATH fallback, and relativeTSRS_BINARY, and are isolated from an ambientTSRS_BINARY.Written for commit fada9d5. Summary will update on new commits.