Skip to content

perf(dev_server): emit each npm binding set once in the generated config - #263

Merged
mikn merged 1 commit into
mainfrom
mikn/dev-server-config-size
Oct 7, 2026
Merged

mikn merged 1 commit into
mainfrom
mikn/dev-server-config-size

Conversation

@mikn

@mikn mikn commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

Problem

ts_dev_server writes a generated config that oj evaluates at startup. For each declared file, the config inlined that file's full npm binding set (package name → member). A large application's files share only a couple of distinct binding sets, but each file carried its own copy. In one application with 12,459 declared files the generated config reached 52.6 MB, and 48.9 MB of that was the npmContexts table. oj kills a config evaluation that takes longer than 60 s, so the dev server never started (evaluation took over 200 s).

The evaluation also called realpath on paths that did not exist and relied on the thrown error. Each throw inside a multi-megabyte module costs milliseconds.

What changes

  • ts/private/ts_dev_server.bzl emits each distinct binding set once (npmBindingSets); each file entry refers to its set by index.
  • The generated code checks each (binding set, directory) pair once instead of once per file and package. It caches node_modules existence per directory.
  • realpath tests existsSync before resolving instead of catching a throw. A permission or symlink-loop error now reads as "not found" where it used to throw.

Verification

  • In the application above, the generated config shrank from 52.6 MB to 4.6 MB, and evaluation dropped from over 200 s to about 1 s under both node and oj. The dev server then started and served its first page.
  • All 105 tests in //tests/dev_server/..., //tests/codegen_tree/... and //tests/vitest/... pass on remote execution (BuildBuddy invocation fa867a77).

Risks

  • The existsSync guard changes the failure mode for unreadable paths, as noted above.

🤖 Generated with Claude Code

A large application's files share a few npm binding sets, but the generated dev-server config inlined a copy per file, which grew past what oj evaluates in 60 s. The config now emits each distinct set once, checks each (binding set, directory) pair once, and tests existence before realpath instead of throwing on missing paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mikn
mikn merged commit f2998ea into main Oct 7, 2026
29 of 33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant