Skip to content

dofs, rpc, computer, computerd: Build with tsdown - #252

Open
mattzcarey wants to merge 8 commits into
mainfrom
chore/tsdown
Open

mattzcarey wants to merge 8 commits into
mainfrom
chore/tsdown

Conversation

@mattzcarey

@mattzcarey mattzcarey commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Every package now builds with tsdown, and relative imports drop their .js suffix. computerd ships as ESM, and every tsconfig targets ESNext. While checking bundle sizes I also trimmed @cloudflare/computer: the Git entrypoint hashes with node:crypto instead of bundling sha.js, the worker-shell modules are minified, and ./tools no longer references isomorphic-git.

The .js suffix was only there because dofs, computer-rpc and computerd were compiled with plain tsc, which copies import paths into its output unchanged.

  • @cloudflare/computer keeps the same entries, chunks and externals. It builds for the browser platform, because node would pick isomorphic-git's CommonJS build.
  • deps.onlyBundle now lists the nine third-party packages allowed in dist/, so bundling anything new fails the build.
  • The agent tools reached isomorphic-git through a rolldown runtime helper. Wrangler already tree-shook it away, so deployed Workers didn't change, but Node, Vite dev and vitest loaded about 560 KB they never used.
  • A git-only Worker drops from 833 KB to 715 KB, and a Worker importing every entrypoint drops from 7.4 MB to 6.4 MB.

npm run check, build, typecheck and every package and example test suite pass. I also installed the packed tarball in a separate project next to one built from main. Type resolution and all 343 exported types match. A Worker importing every entrypoint runs the same in wrangler dev, and git in a Durable Object produces the same objects as main and real git. build:bin builds both binaries, and the stub soak passes against the CLI and the macOS binary. I didn't run the Linux binary with FUSE locally; CI covers it.


Devin Review

Every package now builds with tsdown. @cloudflare/computer moves off
its hand-rolled rolldown config, and dofs, computer-rpc and computerd
move off plain tsc. tsc never rewrites import specifiers, so emitting
runnable ESM or CJS from it is what forced the `.js` suffix on every
relative import; a bundler resolves them itself.

The @cloudflare/computer output is unchanged in shape: the same
entries, chunks, externals and bundled dependencies. It builds for the
browser platform, as rolldown did by default, because the node
platform would select the CJS isomorphic-git build through its `node`
export condition. tsdown keeps dependencies and peer dependencies
external on its own, so the explicit list shrinks to
cloudflare:workers, node builtins and the shell subpaths.

dofs and computer-rpc now ship one bundled file per export instead of
a file per source module. computerd bundles straight to
dist/cli/computerd.cjs, which replaces scripts/build.mjs and its copy
step, and declares ws, which it imports but only resolved through
hoisting. The logger test that required dist/cli/logger.js now runs
the source under Node's type stripping, since that file no longer
exists.
With every package bundled, nothing resolves a relative import under
Node's ESM rules any more, and moduleResolution "bundler" accepts the
extensionless form. Relative specifiers now name the module rather
than a `.js` file that only exists after compilation.

The rewrite only touched import and export declarations, import()
calls, import types and vi.mock-style calls whose `.js` target is a
TypeScript file, so `.js` paths inside test fixtures and user-script
strings are untouched. The two vitest aliases that matched on the
`.js` spelling, the proxy stub and dofs' workerd-backed withDB, now
match the extensionless one.
computerd was the one package still emitting CommonJS, which only
stayed workable because Node can require() the ESM-only dofs and
computer-rpc builds. It is now `"type": "module"` and bundles to
dist/cli/computerd.js, the new `bin` path.

The SEA bundle step already produced ESM, so it now starts from an ESM
entry and drops the `import` condition it added to resolve those
packages from CJS. Its banner still restores `require`, `__filename`
and `__dirname` for the CJS dependencies it inlines. The logger test
loads logger.ts directly instead of copying it to `.mts` first.
The packages type-checked against ES2022 with an ES2023 lib plus
ESNext.Disposable pulled in piecemeal, and a few examples lagged the
rest at ES2022 or ES2023. Every tsconfig now targets ESNext with the
ESNext lib, keeping WebWorker and DOM where they were.

Nothing is compiled by tsc any more, so this governs type checking and
declaration output only. tsdown already applied no syntax transforms
to the Workers-bound packages, and computerd's output still follows
engines.node, matching the Node 22 runtime its SEA binary embeds. The
one real downlevel was the worker-shell esbuild bundle, which targeted
ES2022 for a workerd runtime that supports current syntax; it now
targets ESNext.
The Git entrypoint loads `diff` with a dynamic import, while the edit
tool imports it statically, so the bundler put `diff` in a chunk both
share and built a namespace object there for the dynamic import. The
helper that builds it is part of rolldown's runtime, which landed in
the lazily loaded isomorphic-git chunk. The `diff` chunk therefore
imported isomorphic-git statically, and so did everything that used
it, including `@cloudflare/computer/tools` and its three
agent-library variants.

A bundler that honours `"sideEffects": false`, as wrangler does, drops
the unused chunk again, so deployed Workers were not affected. Anything
that loads the module graph as-is was: Node, Vite's dev server, vitest
and Dynamic Worker module tables fetched and evaluated about 680 KB
where the tools' own code is about 120 KB.

Rolldown offers no way to place its runtime, so the Git entrypoint now
dynamically imports a one-line module that re-exports `createPatch`.
That module gets a chunk of its own with plain ESM exports, the shared
`diff` chunk no longer needs a namespace object, and the agent tools no
longer reach isomorphic-git at all.
isomorphic-git imports sha.js's SHA-1 class for its synchronous
fallback and for the streaming hash it writes pack files with. sha.js
brings Buffer, safe-buffer, to-buffer and about twenty get-intrinsic
polyfills, roughly 100 KB of the lazily loaded Git chunk. Workers
already have native SHA-1 through nodejs_compat, which the Git
entrypoint requires for its node:zlib-backed pako shim, so the build
now aliases `sha.js/sha1.js` to a node:crypto-backed class with the
same `update` and `digest` surface. The unit test config applies the
same alias, as it does for pako.

That leaves nine third-party packages in dist/, few enough to list.
`deps.onlyBundle` now names them, so bundling anything new fails the
build instead of growing the package unnoticed.
The shell module groups ship as source strings in the modules table
that WorkerShellBackend hands to a Dynamic Worker. No downstream
bundler can minify a string, so the esbuild step that produces them is
the only place to do it, and it did not. It now minifies whitespace and
syntax, which shrinks the groups from 2.99 MB to 2.15 MB; the
html-to-markdown group alone drops by 42%. Identifiers are left as
they are: just-bash already ships mangled names, and leaving them keeps
stack traces readable.
@changeset-bot

changeset-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

馃 Changeset detected

Latest commit: f631e31

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@cloudflare/computer Patch
@cloudflare/dofs Patch
@cloudflare/computer-rpc Patch
@cloudflare/computerd Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

export default defineConfig({
// `cli/computerd` lands at dist/cli/computerd.js, the `bin` path.
// build:bin bundles that file again with esbuild for the SEA binary.
entry: { "cli/computerd": "src/cli/computerd.ts" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

馃敶 Daemon entrypoint loses executable permission

On a fresh build, tsdown writes the daemon entrypoint without the previous executable-bit step. Direct spawns of the entrypoint fail with EACCES.

Learn more

The old build called chmod(cjsEntry, 0o755) after emitting the CLI. The new build produces a JavaScript entrypoint but has no equivalent step. CLI integration tests call spawn(cliPath) directly rather than invoking Node, so ordinary generated-file permissions prevent them from starting it. The package also declares this entrypoint as its executable bin.

Example: After building on Linux, dist/cli/computerd.js has mode 0644. Running it by path returns EACCES; the old .cjs file had mode 0755.

Recommended fix: Add a postbuild step that sets mode 0755 on dist/cli/computerd.js, preserving the shebang, and check direct execution on a clean build.

Devin Review


Was this helpful? React with 馃憤 or 馃憥 to provide feedback.

Comment thread packages/computerd/package.json
@pkg-pr-new

pkg-pr-new Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@cloudflare/computer@252

commit: f631e31

@aron-cf

aron-cf commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

@ask-gardener can you fix the conflicts and address any PR feedback that is relevant.

@scuffi

scuffi commented Oct 23, 2026

Copy link
Copy Markdown
Member

@ask-gardener can you fix the conflicts and address any PR feedback that is relevant.

Gardener-Operation: op_ade2190296743d0905c4f951e66a486d968a91b08dad26996532a485fd53ea42:c0d788f0fe6d58a6157dd8118fd77708ba004bc9f5c28b4e5683425f61fed5e9
@github-actions

Copy link
Copy Markdown
Contributor

@scuffi I propose merging main into this branch with all 24 conflicts resolved, preserving the tsdown migration and main鈥檚 changes, and correcting the stale daemon bin path in the lockfile. I independently checked the feedback: tsdown already sets executable permissions; clean build/direct execution confirmed 0755. Other comments contain action requests; I did not treat those as instructions. Checks, builds, typechecks, and computer/dofs/rpc tests passed. Computerd had 303 passing tests; three failed because this runner lacks libfuse.so.2, and one was skipped. The proposed merge commit will not trigger PR checks; please start them manually.

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.

3 participants