Skip to content

research: can webjs db/test/typecheck run under bun zero-install? #719

Description

@vivek7405

Question

Can webjs db / test / typecheck run under bun zero-install (no node_modules), and how? This is the deep research for #704 after its first approach (a runner that imports the tool bin + a spawn pin preload) was found broken.

Verdict

The spawned dev tools (drizzle-kit, the bun test runner, tsc) fundamentally need a materialized node_modules; bun's auto-install + global cache + plugins cannot substitute. The reconciling fix is to auto-materialize node_modules via a fast bun install (cheap because the deps are already in bun's global cache) and run the tool from the app context (the existing resolveBin path), NOT the runner / preload / bun x approach. The server (dev / start) stays true zero-install via the pin shim; only the spawned tooling materializes.

Findings (verified empirically on bun 1.3.14)

  1. bun x <tool> runs the tool from an ISOLATED temp dir (/tmp/bunx-.../node_modules/<tool>/), so drizzle-kit cannot resolve the APP's drizzle-orm. This fails EVEN WITH a full bun install present, so bun x was always the wrong tool, independent of zero-install. drizzle-kit must run from the app's own node_modules context.

  2. The db failure is a version mismatch surfaced by auto-install. drizzle-kit@1.0.0-rc.3 requires drizzle-orm/_relations, which exists in drizzle-orm@1.0.0-rc.3 but NOT in drizzle-orm@0.45.2. npm latest is 0.45.2 (the rc line is a prerelease under a non-latest tag), so bun's bare auto-install resolves drizzle-orm to 0.45.2 and the subpath is missing.

  3. Bun plugins (Bun.plugin onLoad / onResolve) do NOT intercept CommonJS require() resolution, only ESM imports. drizzle-kit's bin.cjs is CommonJS, so the pin shim cannot reach its internal require('drizzle-orm/_relations'). A bunfig preload DOES run under bun x (verified), but its onResolve never fires for the CJS require. onLoad on a cached CJS file also breaks it (returning { contents, loader } forces ESM parsing of a CJS module).

  4. package.json overrides and bunfig [install.overrides] do NOT reach the bun x temp process, so they cannot pin the version there.

  5. The fix works end to end. With the deps already in bun's global cache (zero-install DOES download to the global cache on disk), bun install materializes node_modules in ~57ms (measured). Running drizzle-kit from the app context then fully generates a real migration ([✓] Your SQL migration -> drizzle/<ts>_init/migration.sql). bun test (which has no auto-install at all) and tsc also work once node_modules exists.

Recommendation for #704 (separate implementation issue)

Drop the runner + the keystone-preload-for-tooling + the bin-import wiring (it cannot work). Instead: webjs db / test / typecheck under bun zero-install auto-materialize node_modules via a fast bun install, then run the existing resolveBin path. This is "no MANUAL install" (webjs does it transparently and fast), the honest reconciliation of the zero-install goal with the reality that CommonJS + esbuild tools and bun test need node_modules.

Tradeoff to document: after the first tool invocation node_modules exists, so the app is "installed" thereafter (everything keeps working; the user never typed an install command). The seamless progression is: start zero-install (instant dev), and the first time you need tooling, webjs materializes in tens of milliseconds.

Relates to #704, #684/#698 (the pin shim, which stays for the server), #669 (the zero-install vision), #718 (lockfile reproducibility, which the materialized lock would also serve).

Metadata

Metadata

Assignees

No one assigned

    Labels

    researchResearch/design/decision record (no code); filter these to read design history

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions