Skip to content

feat: precompile workspace packages, remove tsx from production runtime - #10

Merged
stubbi merged 3 commits into
mainfrom
fix/precompile-workspace-packages
Mar 21, 2026
Merged

feat: precompile workspace packages, remove tsx from production runtime#10
stubbi merged 3 commits into
mainfrom
fix/precompile-workspace-packages

Conversation

@stubbi

@stubbi stubbi commented Mar 21, 2026

Copy link
Copy Markdown

Summary

Eliminates tsx runtime transpilation from the Docker image. All workspace packages now export precompiled dist/*.js instead of raw src/*.ts.

What changed

Every workspace package had a dual export pattern:

// Development (before): raw TypeScript, needs tsx to transpile at runtime
"exports": { ".": "./src/index.ts" }

// Now: precompiled JavaScript, runs directly with node
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }

The publishConfig.exports (which already pointed to dist/) was promoted to the main exports field in all 11 packages.

Dockerfile changes

  • CMD: node --import tsx/dist/loader.mjs server/dist/index.jsnode server/dist/index.js
  • Prune: pnpm prune --prod now works (tsx was the blocker before)
  • Selective COPY: only dist/ and package.json per package (no source code)

Benefits

Before After
tsx transpiles TypeScript at runtime Precompiled JS runs directly
devDependencies shipped in image Pruned — prod-only deps
Source code in production image Only compiled output
Slower cold start Faster startup

Verified locally

$ node server/dist/index.js
Server listening on 127.0.0.1:3100

No tsx, no errors.

Test plan

  • pnpm -r build succeeds
  • node server/dist/index.js starts without tsx
  • PR CI passes (build + e2e)
  • Docker image builds and server starts in container

🤖 Generated with Claude Code

stubbi and others added 3 commits March 21, 2026 22:23
Switch all workspace package exports from src/*.ts to dist/*.js so
the server runs on precompiled JavaScript without needing tsx as a
runtime transpiler.

Changes:
- All package.json exports fields now point to dist/ (previously
  pointed to src/ for development convenience, with publishConfig
  overriding for npm publish)
- Dockerfile CMD changed from node --import tsx/loader to node directly
- Dockerfile now prunes devDependencies (tsx no longer needed at runtime)
- Selective dist-only COPY in production stage works correctly now

Benefits:
- Faster startup (no runtime transpilation)
- Smaller image (no tsx, no TypeScript source in production)
- No devDependencies shipped
- More secure (no source code in production image)
- Reproducible (compiled output is deterministic)

Verified: server starts and runs correctly with node server/dist/index.js
without tsx loader.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TypeScript resolves types via the "types" condition (pointing to
src/*.ts for zero-build typechecking), while Node resolves runtime
imports via the "import" condition (pointing to dist/*.js).

This lets pnpm -r typecheck work without building first, while
the runtime still uses precompiled output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use Node's --conditions flag to switch between source and compiled
output:

- "default" → ./src/*.ts (dev, tests, typecheck — no build needed)
- "production" → ./dist/*.js (Docker, deployed environments)

Dockerfile CMD uses --conditions=production so Node resolves
workspace imports to precompiled dist/ output. Dev tools (vitest,
tsx, tsc) use the default condition and resolve to source.

Verified: typecheck passes, 569 tests pass, server starts with
--conditions=production without tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@stubbi
stubbi merged commit d66aeb6 into main Mar 21, 2026
3 checks passed
@stubbi
stubbi deleted the fix/precompile-workspace-packages branch March 21, 2026 21:39
github-actions Bot pushed a commit that referenced this pull request Mar 23, 2026
…me (#10)

* feat: precompile all workspace packages — eliminate tsx from runtime

Switch all workspace package exports from src/*.ts to dist/*.js so
the server runs on precompiled JavaScript without needing tsx as a
runtime transpiler.

Changes:
- All package.json exports fields now point to dist/ (previously
  pointed to src/ for development convenience, with publishConfig
  overriding for npm publish)
- Dockerfile CMD changed from node --import tsx/loader to node directly
- Dockerfile now prunes devDependencies (tsx no longer needed at runtime)
- Selective dist-only COPY in production stage works correctly now

Benefits:
- Faster startup (no runtime transpilation)
- Smaller image (no tsx, no TypeScript source in production)
- No devDependencies shipped
- More secure (no source code in production image)
- Reproducible (compiled output is deterministic)

Verified: server starts and runs correctly with node server/dist/index.js
without tsx loader.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use conditional exports — types from src/, import from dist/

TypeScript resolves types via the "types" condition (pointing to
src/*.ts for zero-build typechecking), while Node resolves runtime
imports via the "import" condition (pointing to dist/*.js).

This lets pnpm -r typecheck work without building first, while
the runtime still uses precompiled output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use custom "production" condition for dist/ exports

Use Node's --conditions flag to switch between source and compiled
output:

- "default" → ./src/*.ts (dev, tests, typecheck — no build needed)
- "production" → ./dist/*.js (Docker, deployed environments)

Dockerfile CMD uses --conditions=production so Node resolves
workspace imports to precompiled dist/ output. Dev tools (vitest,
tsx, tsc) use the default condition and resolve to source.

Verified: typecheck passes, 569 tests pass, server starts with
--conditions=production without tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stubbi added a commit that referenced this pull request Apr 17, 2026
…me (#10)

* feat: precompile all workspace packages — eliminate tsx from runtime

Switch all workspace package exports from src/*.ts to dist/*.js so
the server runs on precompiled JavaScript without needing tsx as a
runtime transpiler.

Changes:
- All package.json exports fields now point to dist/ (previously
  pointed to src/ for development convenience, with publishConfig
  overriding for npm publish)
- Dockerfile CMD changed from node --import tsx/loader to node directly
- Dockerfile now prunes devDependencies (tsx no longer needed at runtime)
- Selective dist-only COPY in production stage works correctly now

Benefits:
- Faster startup (no runtime transpilation)
- Smaller image (no tsx, no TypeScript source in production)
- No devDependencies shipped
- More secure (no source code in production image)
- Reproducible (compiled output is deterministic)

Verified: server starts and runs correctly with node server/dist/index.js
without tsx loader.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use conditional exports — types from src/, import from dist/

TypeScript resolves types via the "types" condition (pointing to
src/*.ts for zero-build typechecking), while Node resolves runtime
imports via the "import" condition (pointing to dist/*.js).

This lets pnpm -r typecheck work without building first, while
the runtime still uses precompiled output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use custom "production" condition for dist/ exports

Use Node's --conditions flag to switch between source and compiled
output:

- "default" → ./src/*.ts (dev, tests, typecheck — no build needed)
- "production" → ./dist/*.js (Docker, deployed environments)

Dockerfile CMD uses --conditions=production so Node resolves
workspace imports to precompiled dist/ output. Dev tools (vitest,
tsx, tsc) use the default condition and resolve to source.

Verified: typecheck passes, 569 tests pass, server starts with
--conditions=production without tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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