Use Node.js and npm. To match the current CI configuration, use Node.js 22.17.0; this is the CI baseline, not a statement of every published package's minimum runtime.
From the repository root:
npm install
npm run symlink
npm run compileThis repository uses separate package manifests and lockfiles with a custom linking script, not npm workspaces. The root install hook installs subpackage and testbed dependencies, links the testbed, and downloads Playwright browsers. Installation requires network access and is heavier than a root-only dependency install. Run npm run symlink to connect the development packages to each other; otherwise tests can resolve installed package versions instead of local changes.
Browser tests need Playwright's Chromium. Client integration tests launch a separate VS Code instance using the insiders version selected by the test launcher, which may require a download. On Linux, editor integration tests need a display; CI supplies Xvfb and native dependencies. See the workflow for the current dependency list and xvfb-run invocation.
| Directory | Responsibility |
|---|---|
| textDocument | Standalone text document implementation. |
| types | Shared LSP data types, helpers, and type guards. |
| jsonrpc | JSON-RPC transport, messages, connections, and cancellation. |
| protocol | LSP requests, notifications, capabilities, and protocol APIs. Depends on types and JSON-RPC. |
| server | Server connection APIs and feature implementations built on the protocol. |
| client | VS Code language client, feature registration, middleware, and VS Code/LSP conversions. |
| client-node-tests | Client converter and integration tests running inside VS Code, with test servers. |
| testbed | Manual client/server experimentation and debugging. |
| tools | Protocol metamodel generation and package-version validation. |
| tsconfig-gen | TypeScript project configuration generator. |
| build | Package orchestration, linking, browser test runner, and release infrastructure. |
Packages with platform-specific implementations use src/common, src/node, and src/browser. Keep common code portable and preserve the package's default, /node, and /browser exports. Types and text documents have separate UMD and ESM outputs. Package manifests define the public entry points; adding an implementation file alone does not expose an API.
Use neighboring implementations and the shared ESLint configuration as the style reference. The TypeScript projects enforce strict typing. Prefer existing helpers and feature abstractions.
For an LSP feature or public API change, check each applicable surface:
- Shared types and type guards in
types. - Protocol request/notification types, parameters, results, capabilities, and exports in
protocol. - Server handlers and public exports in
server. - Client capability negotiation, static/dynamic registration, middleware, and exports in
client. - Both VS Code-to-protocol and protocol-to-VS Code conversions in codeConverter.ts and protocolConverter.ts.
- Relevant type guard, transport, server, converter, and integration tests. Cover unsupported capabilities and cancellation where applicable.
Not every change touches every layer. Follow a comparable feature, such as the inline-completion protocol declaration and client implementation, to determine what is needed.
Preserve existing method names, wire formats, optional-field behavior, public exports, and proposed-feature gates unless the requested change explicitly alters them. Check the LSP specification linked from the protocol documentation when changing protocol behavior. Package versions and dependency pins are coordinated; do not update them incidentally. The root npm run validate:version command checks dependency-version consistency after the tools have been compiled.
Root scripts include compile:types, compile:textDocument, compile:jsonrpc, compile:protocol, compile:server, compile:client, and compile:client-node-tests. They use TypeScript build mode and project references. Compile before testing so tests do not exercise stale JavaScript.
For example, from the root, to build and check a server change:
npm run compile:server
npm --prefix server run lint
npm --prefix server testClient changes generally need npm run compile:client-node-tests and npm --prefix client-node-tests test. The client package delegates its tests to that extension; these are not ordinary standalone Node.js tests.
Tests use Mocha's TDD interface (suite/test) and normally run compiled files:
| Package | Compiled Node.js test location | Package script |
|---|---|---|
types, textDocument |
lib/umd/test |
npm test |
jsonrpc |
lib/common/test, lib/node/test |
npm run test:node |
protocol |
lib/node/test |
npm run test:node |
server |
lib/node/test |
npm test |
Run these scripts from the package directory, or use npm --prefix <package> from the root. For scripts that invoke Mocha directly, append -- --grep "<pattern>" to select test names. For example, npm --prefix jsonrpc run test:node -- --grep "<pattern>". Replace <pattern> with a matching test title and confirm that tests actually ran.
For exactly one test file, bypass the package's default Mocha spec list with --no-config; simply appending a file can also run configured suites. After compiling the server, this example runs from the server directory (Windows path syntax):
node ..\node_modules\mocha\bin\_mocha --no-config --ui tdd lib\node\test\connection.test.jsUse / separators for the equivalent command in a POSIX shell. An optional --grep "<pattern>" narrows that file further.
- For JSON-RPC or protocol browser changes, run the affected package's
npm run test:browser. Itsnpm testruns both Node.js and browser tests; do not assume arguments to that composite script select a single Mocha suite. - Client converter and integration coverage lives in client-node-tests/src. The extension runner discovers its test files; its
npm testdoes not expose a Mocha file/grep selector. - For build-runner changes,
npm run test:buildruns the JavaScript regression tests without an editor or browser. - For manual investigation, use the VS Code launch configurations and testbed.
For full verification, run from the root after setup:
npm run compile
npm run lint
npm testRoot npm test runs build-runner tests and the package suites, including browsers and VS Code integration. The package runner continues through selected packages, retaining the first failing status; spawn errors and signal termination also produce failure. Root npm run all links, compiles, and tests, but does not lint.
State which checks passed, failed, or were not run. Distinguish missing runtime/display/download prerequisites from test failures. Do not infer full-suite success from a focused test run.
-
TypeScript configurations: .tsconfigrc.js describes the projects and variants managed by tsconfig-gen. Change that source (or generator code for a generator bug), then run
npm run tsconfig-genfrom the root after setup. That command compiles the generator before regenerating configurations. Do not hand-edit generated variants as the only fix. -
Protocol metamodel: protocol TypeScript declarations supply
protocol/metaModel.json; tools/src/metaModel.ts defines the schema model used forprotocol/metaModel.schema.json. After protocol/type changes affecting the model, run these commands from the root:npm run compile npm run generate:metaModel npm run generate:metaModelSchema
Compilation builds the generation tooling. Regeneration overwrites the output files; inspect the resulting changes rather than editing the JSON by hand.
-
Compiled outputs: edit TypeScript sources rather than
libordistfiles.
These are not routine verification commands:
- Root
npm run update:lockand packageall:publishscripts invokegit clean -xfd ., deleting untracked and ignored files within their scope, including dependencies and potentially local work. npm packandnpm publishcan triggerprepack/prepublishOnlyhooks that run those destructive publish-validation scripts.- Version/release operations and lockfile updates can change coordinated package metadata or invoke lifecycle hooks. Review the relevant manifest and obtain explicit approval before using them.
npm run audit:all:fixmutates dependencies. Use the non-fixing audit command for inspection unless dependency changes are requested.
Do not delete local work or run release infrastructure merely to verify a code change. Prefer the explicit build, lint, and test commands above.