fix(nitro): use workspaceDir for monorepos#2713
Conversation
🦋 Changeset detectedLatest commit: 5403ebe The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
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 |
📊 Benchmark Results
workflow with no steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 25 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 50 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.all with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.race with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 10 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express workflow with 25 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 50 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 10 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 25 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 50 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) stream pipeline with 5 transform steps (1MB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) 10 parallel streams (1MB each)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) fan-out fan-in 10 streams (1MB each)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|
🧪 E2E Test Results✅ All tests passed Summary
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
✅ 📋 Other
|
243c906 to
3a0c001
Compare
TooTallNate
left a comment
There was a problem hiding this comment.
Approve — correct monorepo fix with the right fallback; canary stabilization is conservative
Clean fix for #2074. Routing the Workflow builder's projectRoot to Nitro's workspaceDir (with rootDir as fallback) is exactly the right lever:
projectRootfeedstransformProjectRoot(base-builder.ts:242), which the SWC plugin consumes asprojectRootfor module-specifier / workflow-ID generation. In a monorepo, computing those relative to the app dir (rootDir) instead of the workspace root is precisely what breaks sibling-workspace-package imports — so usingworkspaceDiraligns the transform root with the real workspace.moduleSpecifierRootfalls back totransformProjectRoot, so it stays consistent.- No behavior change for single-repo apps: Nitro defaults
workspaceDirtorootDirwhen there's no enclosing workspace, soprojectRootis unchanged in the common case. The fix only diverges in actual monorepos, which is the targeted scenario. - The
?? rootDirfallback correctly handles older/incomplete Nitro option stubs that don't populateworkspaceDir.
Applied to both VercelBuilder and LocalBuilder, so dev and build paths agree.
Verified: built @workflow/nitro and @workflow/nuxt; the full Nitro test suite passes (25), including the new uses nitro workspaceDir as the workflow projectRoot test which asserts projectRoot === workspaceDir when it differs from rootDir. Docs (v4+v5, nitro+nuxt) accurately describe the zero-config monorepo behavior; changeset correctly covers both packages (@workflow/nuxt benefits since it runs through Nitro). CI is clean (93 pass / 0 fail).
On the bundled canary e2e stabilization
The dev.test.ts changes are unrelated to the Nitro fix but clearly motivated ("was blocking this PR"), and the stabilization is appropriately conservative:
- The larger timeouts (120s/360s vs 50s/240s) and the exact-count → lower-bound relaxation are both gated on
finalConfig.canary. The stable (released-Next) path keeps the strict budgets and the exactexpect(actual).toBe(expected)HMR-count assertions untouched. - The relaxation is well-justified (canary webpack can emit duplicate watcher events per edit, and can queue rediscovery behind route compilation) and only loosens assertions for the unreleased
next@16.3.0-canarylane — it doesn't weaken coverage for the version users actually run.
So it's the right way to keep a flaky canary lane from blocking unrelated work.
Minor (non-blocking)
- The
workspaceDir: undefined → rootDirfallback isn't directly unit-tested (the stub always setsworkspaceDir). It's a trivial??, but a one-line test passingworkspaceDir: undefinedand assertingprojectRoot === rootDirwould lock the fallback in. - Bundling the canary-e2e change into a Nitro PR is slightly awkward for git archaeology — a separate PR would've been cleaner — but it's low-risk and reasonably scoped here.
LGTM.
|
Backport PR opened against |
Summary
workspaceDiras the Workflow builderprojectRootrootDiras a fallback for older/incomplete Nitro option stubsFixes #2074.
Tests
vitest run packages/nitro/src/index.test.tspnpm --filter @workflow/nitro --filter @workflow/nuxt buildpnpm vitest run packages/next/src/watch-rebuild.test.tsnext@16.3.0-canary.2biome checkon touched filesgit diff --checkgpt-5.5xhigh clean; Claudeopus-4.8xhigh was unavailable/no-access (404)Docs Preview