Skip to content

feat(ts): npm packages in ts_bundle, ts_binary, Vitest and browser tests - #77

Merged
walterjgsp merged 2 commits into
tsfrom
feat/ts-npm-wiring
Oct 4, 2026
Merged

walterjgsp merged 2 commits into
tsfrom
feat/ts-npm-wiring

Conversation

@walterjgsp

Copy link
Copy Markdown
Contributor

Part of #61: the wiring that #62 left out. ts_npm_module packages now work everywhere a target can use them, not only in type checks and Deno tests.

What changed

  • ts_bundle / ts_browser_test: with npm packages, deno bundle (Deno resolves exports, CommonJS and subpaths itself, as it does for ts_test; esbuild cannot resolve npm:). Deno would download its own esbuild, so the new esbuild_toolchain provides the pinned binary (sha256 per platform; byte-identical to the one Deno fetches), placed where Deno looks for it. If Deno downloads one anyway the build fails, rather than going non-hermetic. Without npm packages the old esbuild path is unchanged.
  • ts_binary: merges the slices, deno compile --cached-only. The executable embeds the packages and runs with no cache or network (checked with an empty DENO_DIR).
  • Vitest: Vitest cannot import npm: specifiers and Vite cannot resolve them. The runner declares the packages (exact versions) in a package.json and deno run --node-modules-dir=auto --cached-only materializes node_modules offline from the merged cache. Both exist only in the test's working directory under plz-out/tmp (deleted after the run; verified, and nothing appears in the source tree). This is the mechanism the coverage mode already used.

Two bugs found on the way (fixed here)

  1. Vitest targets wrote into the shared vitest_toolchain output. Their type check ran against the shared cache without the slices and without --cached-only, so Deno downloaded the packages into it. And the unversioned npm:vitest resolves to the registry's latest (5.0.3), not the cached 5.0.1, so Deno also downloaded that into the shared cache at run time, silently. They now use a per-run copy when slices are involved, and the cached version (npmcache.CachedVersion) for vitest and @vitest/coverage-v8.
  2. Bundles were not reproducible: the random temp directory name ended up in the bundle's comments. The per-run DENO_DIR is now a fixed name in the working directory; three forced rebuilds give identical hashes.

Known limits (documented in usage.md)

  • deno bundle is marked experimental by Deno, and the directory it looks in for esbuild is internal to it, so the plugin knows it per Deno version (esbuildCacheVersions; --esbuild-cache-version for others). A Deno bump fails the npm bundle fixtures until it is updated.
  • deno compile downloads its denort runtime from dl.deno.land on every build, with or without npm packages. Pre-existing and not pinned here.
  • The non-npm ts_bundle path still runs deno run npm:esbuild, which fetches esbuild at run time. Pre-existing.
  • esbuild hashes for linux-arm64 and both darwin platforms come from the registry tarballs, but only linux-x64 was run.
  • Not done: the helper that prints pinned declarations (automatic resolution makes it much less necessary).

Tests

  • Go: 216 tests in tools/please_ts (new: bundle with a fake deno covering the esbuild placement, arguments, per-run cache, "Deno downloaded its own esbuild" detection and failures; binary; compile on the Vitest path; CachedVersion; the Vitest package.json, dependencies and cache directory).
  • Integration, through the real rules: bundles of highlight.js (CommonJS, subpaths), of the exports-only legacy-modes with its declared and its automatically resolved 12-package closure; a compiled binary that runs with an empty DENO_DIR; a browser test that bundles highlight.js and runs in headless Chromium; Vitest tests for both packages (also with coverage). Zero Download lines in the Vitest runs.
  • Negative controls: a wrong esbuild lookup version hits the detection error; a bundle without its npm dependency fails.
  • plz build //... and plz test //...: 35 targets, 324 tests passed locally.

🤖 Generated with Claude Code

walterjgsp and others added 2 commits October 4, 2026 15:43
…kpoint)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ts_bundle and ts_browser_test run deno bundle when the target has npm packages, with the pinned esbuild of the new esbuild_toolchain placed where Deno looks for it (the build fails if Deno fetched one). ts_binary merges the slices and compiles with --cached-only. Vitest declares the packages in a package.json and lets deno materialize node_modules offline, in the test's working directory only. Vitest targets also stop writing into the shared vitest_toolchain: the type check used a shared cache without the slices and without --cached-only, and the unversioned npm:vitest resolved to the registry's latest and was downloaded into it; both use the cached version now. Bundles are reproducible (no random temp path in them). Part of #61.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@walterjgsp
walterjgsp merged commit f62ada0 into ts Oct 4, 2026
2 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