Repository navigation
build: migrate from tsc to tsdown - #2448
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
orpc | 8b607e9 | Commit Preview URL Branch Preview URL |
Nov 09 2026, 06:56 AM |
|
Important Review skippedToo many files! This PR contains 722 files, which is 422 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files.
|
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/experimental-lock
@orpc/mcp
@orpc/msw
@orpc/nest
@orpc/next
@orpc/node
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Reviewed the complete 722-file diff at c2845c1, plus the build output it produces.
- One shared
tsdown.config.ts— entries derived from each package'sexports,unbundle+deps.neverBundleto mirror tsc's per-module output, oxc-generated declarations, and therestoreReexportsrenderChunkhook that keepsexport { a } from './x'for Expo tree shaking. All 29prepackscripts now calltsdown. - Bundler module resolution — root tsconfig moves to
module: preserve/moduleResolution: bundler, dropsrewriteRelativeImportExtensions, and ~700 files drop the.ts/.tsxsuffix from relative imports. Emit-only options leavetsconfig.lib.json; the threetsconfig.build.jsonfiles are gone. - Three non-mechanical source edits — explicit
: RegExpon the shared regex constants (oxc isolated-declaration requirement), a documentedTmpFile.pathfield, and theexport/@Module({})reorder that keepsORPCModule's JSDoc in the emitted declaration.
Verified locally against the pinned tsdown@0.23.7: pnpm install --frozen-lockfile succeeds, all 29 packages build, every publishConfig.exports entry imports under Node (cloudflare aside, which needs workerd), every relative specifier in the emitted .js and .d.ts resolves, and the public export-name surface per entry matches tsc-generated declarations for every package. pnpm check, tsc -p tsconfig.json --noEmit, and the full vitest run (301 files, 4585 tests) all pass.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
3f3ab94 to
37d3fa9
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Re-reviewed the delta from 1ec051f to 3f3ab94 (one commit, tsdown.config.ts only), which adds a build-time guard against undeclared imports.
- Undeclared-import guard —
deps.onlyImportnow lists each package'sdependenciesandpeerDependenciesplusbunand/^(bun|node|cloudflare):/, so a runtime or declaration import of any other package fails the build instead of shipping silently. cloudflare:*marked external up front —inputOptions.external: [/^cloudflare:/]removes theUNRESOLVED_IMPORTwarnings forcloudflare:workersin@orpc/cloudflare.
Verified against tsdown@0.23.7: all 29 packages build, and the emitted dist is byte-identical to the config without these options (621 files, per-file SHA-256). The guard fires for both an undeclared runtime import and a type-only import surfaced in a .d.ts (tested by temporarily adding import 'arktype' and import type { Type } from 'arktype' to @orpc/shared, then reverting). cloudflare:workers warnings drop from 3 to 0, pnpm check passes, and the working tree is clean.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
37d3fa9 to
af6ee51
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Re-reviewed the delta from 3f3ab94 to af6ee51 (a squash onto current main; the net change is one line in tsdown.config.ts).
bun:*externalized alongsidecloudflare:*—inputOptions.externalis now/^(bun|cloudflare):/, aligning the external list withdeps.onlyImport's^(bun|node|cloudflare):so a Bun-native import such asbun:sqlitestays an import instead of falling through to resolution and emitting anUNRESOLVED_IMPORTwarning.
No built entry imports a bun: specifier today — bun:test appears only in test files, which are not export entries, and the bare bun type import in @orpc/bun was already external via neverBundle — so the emitted dist is unchanged from the previously verified state.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Re-reviewed the delta from 3f3ab94 to af6ee51 on the rebased branch: one line changed in tsdown.config.ts.
bun:*marked external up front —inputOptions.externalnow matches/^(bun|cloudflare):/instead of onlycloudflare:, so Bun's built-in scheme imports are externalized before resolution rather than falling through and warning as unresolvable.
Verified against tsdown@0.23.7: all 29 packages build with no warnings, and a temporary import 'bun:sqlite' in @orpc/bun is preserved verbatim in dist/index.js (reverted afterwards). The deps.onlyImport allow-list already permits bun:*, so the undeclared-import guard is unaffected.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
36edd90 to
adf1177
Compare
|
Your OpenRouter API key was rejected. Issue a new key with the provider, then update the copy Pullfrog uses, in Pullfrog or in this repo's GitHub Actions secrets. The provider's responsePullfrog provider settings → · GitHub Actions secrets →
|
|
The runner image changed under
openapi-spec already uses Generated by Claude Code |
Build every package with tsdown from one shared root tsdown.config.ts.
Each package's prepack runs `tsdown`, which finds the root config; entries
come from the package's exports. unbundle keeps one output file per source
module, as tsc did, relative imports such as ../package.json are bundled
in, and packages stay external.
- Generate declarations with oxc. tsdown's default tsgo generator drops
the DecoratedProcedure import from @orpc/effect's extensions/effect.d.ts,
used only inside a module augmentation, so .effect() procedures lost
their types for consumers. The regex constants in @orpc/shared get
explicit types, and the ORPCModule and TmpFile.path JSDoc move to where
oxc keeps them.
- Turn rolldown's `import { a } from 'x'; export { a }` back into
`export { a } from 'x'` with a small renderChunk hook, so Expo's tree
shaking can drop unused re-exports again.
- Fail the build when dist imports a package that isn't in the package's
dependencies or peerDependencies (deps.onlyImport).
- Switch the root tsconfig to module preserve with bundler resolution and
drop the .ts extension from relative imports.
- Remove the tsc-only tsconfig options and the bun, cloudflare and nest
tsconfig.build.json files.
- Bump oxlint, oxlint-tsgolint, oxfmt and @oxc-project/runtime.
- Bump CodSpeedHQ/action to v5. v4 installs a CodSpeed runner that exits
with "Unsupported system" on the ubuntu-26.04 image ubuntu-latest now
uses; openapi-spec already runs v5 there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVaw7RpPRpasxuSAB8fbND
adf1177 to
8b607e9
Compare
Hooray! CodSpeed harness just leveled up!The base and head of this comparison were measured with different runner settings, so their benchmark values are not directly comparable. What changed between base and head:
Re-run the base with the same settings to get a valid performance comparison. Comparing |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Re-reviewed the delta from 36edd90 to 8b607e9 (four files: the two package.json files, the CodSpeed workflow, and the lockfile), which lands the tooling bumps the PR body already described.
- Dev tooling bumped —
oxfmt0.76 → 0.77,oxlint1.91 → 1.92, andoxlint-tsgolint7.0.2005 → 7.0.2006, with the lockfile regenerated to match. @oxc-project/runtimebumped in@orpc/nest— ^0.155.0 → ^0.157.0, so the bundled decorator helpers now live atdist/_virtual/_@oxc-project_runtime@0.157.0/…, consistent with the behavior change documented in the PR body.- CodSpeed workflow moved to
CodSpeedHQ/action@v5— v5 still accepts the configuredmode: simulationandruninputs.
Verified locally: pnpm install --frozen-lockfile succeeds with the regenerated lockfile, pnpm check reports 0 warnings and 0 errors on 936 files under oxlint 1.92, and @orpc/nest's prepack builds cleanly with the new runtime, emitting the _virtual/_@oxc-project_runtime@0.157.0 helpers with design:paramtypes metadata intact. The lockfile fully drops the 0.155.0 runtime entries; the remaining @oxc-project/types@0.155.0 reference belongs to the unrelated Scalar/Unhead docs toolchain.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
## Why Builds move from per-package `tsc` to tsdown with one shared root config, the same migration as middleapi/orpc#2448 in a simpler form. This repo needs neither oRPC's re-export hook for Expo tree shaking nor its runtime-module list. Output keeps tsc's shape (one `.js` and `.d.ts` per source module), so `publishConfig.exports` stays as it is. ## Changes - **One root `tsdown.config.ts`.** tsdown finds it from each package directory, so every `prepack` is just `tsdown`. Entries come from each package's `exports`; `unbundle` keeps one file per module, and packages stay external (`neverBundle: true`). - **Declarations come from oxc** (`dts: { generator: 'oxc' }`), which needs explicit types on exports. All sources already had them, and a missing one now fails the build. - **Undeclared imports fail the build.** `deps.onlyImport` lists each package's `dependencies` and `peerDependencies`. `src` has no `node:` imports, so the platform stays `neutral`. - **Bundler module resolution.** The root tsconfig uses `module: preserve` with `moduleResolution: bundler`, and the `.ts` extension is dropped from relative imports in packages. `dist` still imports with `.js` extensions. `verbatimModuleSyntax` stays: without it `pnpm check` misses type-only re-exports, which then only fail at build time. - **Cleanup and bumps.** The emit-only options in `tsconfig.lib.json` are gone; oxlint, oxlint-tsgolint and oxfmt are bumped. ## Verification - `pnpm check`, `pnpm test` (377 tests) and all 3 package builds pass without warnings. - Against the published 0.7.1 tarballs: - All 7 entry points export the same names and value types at runtime. - The public API is semantically unchanged, checked with the TypeScript compiler API. - JSDoc is preserved (704 blocks before and after). - The emitted declarations type-check for a `nodenext` consumer with `skipLibCheck: false` and `exactOptionalPropertyTypes`. ## Behavior changes - `@openapi-spec/downgrader` no longer ships `dist/shared.d.ts`, which no public type refers to. --- _Generated by [Claude Code](https://claude.ai/code/session_01VVaw7RpPRpasxuSAB8fbND)_ Co-authored-by: Claude <noreply@anthropic.com>
## Why Builds move from per-package `tsc` to tsdown with one shared root config, the same migration as middleapi/orpc#2448 in a simpler form. This repo needs neither oRPC's re-export hook for Expo tree shaking nor its runtime-module list. Output keeps tsc's shape (one `.js` and `.d.ts` per source module), so `publishConfig.exports` stays as it is. ## Changes - **One root `tsdown.config.ts`.** tsdown finds it from each package directory, so every `prepack` is just `tsdown`. Entries come from each package's `exports`; `unbundle` keeps one file per module, and packages stay external (`neverBundle: true`). `platform: 'neutral'`, as in the other two repos. - **Declarations come from oxc** (`dts: { generator: 'oxc' }`), which needs explicit types on exports. A missing one now fails the build. - **`AsyncIteratorClass[asyncDisposeSymbol]`.** Isolated declarations can't infer a class member keyed by `asyncDisposeSymbol`, whose type is conditional. So the member is declared on a merged interface, and the method is defined after the class with `Object.defineProperty`. It calls `this.return()`, as the spec's `AsyncIterator.prototype[Symbol.asyncDispose]` does; the old body was a copy of `return()` minus the return value. It is still a non-enumerable, writable, configurable prototype method. - **Undeclared imports fail the build.** `deps.onlyImport` lists each package's `dependencies` and `peerDependencies`, plus `node:` built-ins (the `node` and `aws-lambda` packages import them). - **Bundler module resolution.** The root tsconfig uses `module: preserve` with `moduleResolution: bundler`, and the `.ts` extension is dropped from relative imports in packages, tests and benches. `dist` still imports with `.js` extensions. `verbatimModuleSyntax` stays: without it `pnpm check` misses type-only re-exports, which then only fail at build time. - **Cleanup and bumps.** The emit-only options in `tsconfig.lib.json` are gone; oxlint, oxlint-tsgolint and oxfmt are bumped. `CodSpeedHQ/action` moves to v5, because v4's runner exits with `Unsupported system` on the `ubuntu-26.04` image that `ubuntu-latest` now uses. ## Verification - `pnpm check`, `pnpm test` (1,487 vitest + 52 bun) and all 7 package builds pass without warnings. - Against the published 0.15.0 tarballs: - Every entry point exports the same names and value types at runtime. - The public API is semantically unchanged, checked with the TypeScript compiler API. - JSDoc is preserved; the only declaration missing is the unexported `anyAbortSignal`. - The emitted declarations type-check for a `nodenext` consumer with `skipLibCheck: false`. - `await using` and `const d: AsyncDisposable = iterator` type-check for consumers, and the `Symbol.for('Symbol.asyncDispose')` fallback test passes. - `platform: 'neutral'` produces output byte-identical to `'node'` for all 7 packages. An undeclared import (`srvx` in `@standard-server/fetch`) fails the build, while `node:` and declared imports pass. - Every emitted `.js` compiles under `@react-native/babel-preset`. ## Behavior changes - `dist` no longer contains empty `.js` files for type-only modules, nor `.d.ts` files that no public type refers to. Nothing imports them. - `@standard-server/core` no longer ships `dist/event-stream/index.*`; the root entry re-exports from the modules directly. It isn't an export path. --- _Generated by [Claude Code](https://claude.ai/code/session_01VVaw7RpPRpasxuSAB8fbND)_ Co-authored-by: Claude <noreply@anthropic.com>

Why
Builds move from per-package
tscto tsdown, driven by one shared config at the root. Output keeps tsc's shape (one.jsand.d.tsper source module), sopublishConfig.exportsandsideEffectsstay as they are. Same migration as middleapi/standard-server#188 and middleapi/openapi-spec#395, which use a simpler config.Changes
tsdown.config.ts. tsdown finds it from each package directory, so everyprepackis justtsdown. Entries come from each package'sexports.unbundlekeeps one file per module; relative imports like../package.jsonare still bundled in (@orpc/opentelemetryinlines onlynameandversion), and packages stay external.dts: { generator: 'oxc' }). The tsgo generator drops theDecoratedProcedureimport from@orpc/effect'sextensions/effect.d.ts, because it is used only inside adeclare module '@orpc/server'augmentation. That is the bug reported in oRPC v2 migration feedback: what hurt, and what should change before stable? #2446 (comment 19474307). oxc needs explicit types on exports. Only@orpc/shared's regex constants lacked them; a missing annotation now fails the build.import { a } from 'x'; export { a }, which Expo's tree shaking can't see through (the problem behind build: migrate from unbuild to obuild transform mode #2362). A smallrenderChunkhook in the config turns them back intoexport { a } from 'x', in the import's position so evaluation order is unchanged. tsdown and rolldown have no option for this.deps.onlyImportlists each package'sdependenciesandpeerDependenciesplus the runtime modules (bun,bun:*,node:*,cloudflare:*). Any.jsor.d.tsindistthat imports something else fails the build, including CommonJS-only packages and type-only imports.externaloption, using the same list.neverBundle: truekeeps Node built-ins and npm-style names as written, but otherprefix:modules (e.g.cloudflare:workers) fall through to resolution and warn that they can't be resolved.module: preservewithmoduleResolution: bundler, and the.ts/.tsxextension is dropped from relative imports across packages, tests, benches and the docs site.diststill imports with.jsextensions.verbatimModuleSyntaxstays: without itpnpm checkmisses type-only re-exports, which then only fail at build time.tsconfig.lib.jsonand the bun/cloudflare/nesttsconfig.build.jsonfiles are gone. oxlint, oxlint-tsgolint, oxfmt and@orpc/nest's@oxc-project/runtimeare bumped.ORPCModuleis written asexport @Module({}) class, since oxc drops a class's JSDoc that sits above its decorators.TmpFile.pathis now a documented field rather than a documented constructor parameter.Verification
pnpm check,pnpm testandpnpm docs:validatepass, and all 29 packages build without warnings.distresolves, and everypublishConfig.exportsandsideEffectspath exists.nodenextconsumer withskipLibCheck: false.dist: with tsgo declarations, a missing required context and a wrong input type both compile. With oxc both are rejected, matching the source types.overridemodifiers, quoted member names, and three file-level comments injson-schemathat aren't attached to any declaration..jsfiles and no.d.ts. Loading each rewritten file in Node gives the same export names and value types as a build without the hook.onlyImport: adding an undeclaredvalibot(runtime and type-only) orchange-caseimport to@orpc/zodfails the build with… is imported in index.js but is not included in deps.onlyImport option. Abun:sqliteimport in@orpc/bunbuilds cleanly and stays an import.EXPO_UNSTABLE_TREE_SHAKING=1), importing onlyOpenAPILinkand the client:Behavior changes
distno longer contains empty.jsfiles for type-only modules, nor.d.tsfiles that no public type refers to (for examplerouter-hidden.d.ts). Nothing imports them.@orpc/nestbundles its decorator helpers underdist/_virtual/_@oxc-project_runtime@0.157.0/….🤖 Generated with Claude Code
https://claude.ai/code/session_01VVaw7RpPRpasxuSAB8fbND