IDE Setup¶
Live Resolution (recommended)¶
rules_typescript ships a tsserver hook that resolves modules directly from Bazel's build graph — npm packages, internal packages, and path aliases. No manual tsconfig.json maintenance needed.
Setup¶
This generates:
- .bazel/tsserver-hook.js — the resolution hook
- .bazel/tsserver-hook-worker.js — background worker
- .bazel/tsserver-launch.json — editor config snippet
- tsconfig.json — minimal compiler options (no paths — the hook handles resolution)
VS Code¶
Add to .vscode/settings.json:
Restart the TS server: Cmd+Shift+P → TypeScript: Restart TS Server.
Neovim (coc-tsserver)¶
Add to coc-settings.json:
Neovim (nvim-lspconfig + typescript-language-server)¶
require('lspconfig').ts_ls.setup({
init_options = {
tsserver = {
nodeOptions = "--require .bazel/tsserver-hook.js",
},
},
})
Emacs (lsp-mode)¶
(setq lsp-clients-typescript-server-args
'("--stdio" "--tsserver-path" "tsserver"
"--tsserver-log-verbosity" "off"
"--tsserver-nodeOptions" "--require .bazel/tsserver-hook.js"))
Any editor with tsserver¶
The hook works with any editor that runs tsserver through Node.js. Pass --require .bazel/tsserver-hook.js as a Node flag when starting tsserver.
How It Works¶
The hook is TypeScript's equivalent of Go's GOPACKAGESDRIVER:
- Worker thread runs
bazel queryin the background to find allts_compiletargets - npm packages resolved by scanning the
@npmexternal repo (already fetched by any previousbazelcommand) - Internal packages resolved from
bazel-bin(.d.tsafter build) or source tree (.tsbefore build) - Path aliases read from
# gazelle:ts_path_aliasdirectives in BUILD files - File watcher monitors BUILD files,
pnpm-lock.yaml, andbazel-bin— re-resolves automatically when they change
The main thread is never blocked — the worker runs bazel query asynchronously and posts results back. tsserver returns "unresolved" briefly on first load, then resolves once the worker completes (~1-2 seconds).
Resolution priority¶
.d.tsinbazel-bin— fast, precise (available afterbazel build).tssource file — always available, slower for tsserver to process- npm package types from external repo — always available after first
bazelcommand
No build required¶
Basic resolution works without bazel build. The source .ts files are always on disk, and npm packages are fetched by the repository rule (triggered by any bazel command, including bazel run //:gazelle). Running bazel build improves resolution by providing .d.ts files, but is not required for the IDE to work.
Debugging¶
Set TSSERVER_HOOK_DEBUG=1 in your environment to see resolution decisions in the tsserver log.
Debugging Tests in VS Code¶
To attach a debugger to vitest running inside the Bazel sandbox:
ts_test(
name = "my_test_debug",
srcs = ["my.test.ts"],
deps = [":my_lib"],
tags = ["manual"],
env = {"NODE_OPTIONS": "--inspect-brk=9229"},
)
Vitest pauses before executing, waiting for a debugger on port 9229. Attach VS Code via "Attach to Node Process" or use chrome://inspect. Source maps are configured automatically.