Bug Report
Environment
workflow: 4.2.4
@workflow/nitro: 4.0.5
@workflow/nuxt: 4.0.5
@workflow/builders: 4.0.5
nuxt: 4.2.2 (Nitro 2.13.1, Vite 6.4.1)
- Node.js: v22.20.0
- Monorepo: pnpm workspaces (
apps/* + packages/*)
Description
In a pnpm-workspaces monorepo, a Nuxt app (apps/app, using workflow/nuxt) has workflows that import a sibling workspace package (@acme/shared at packages/shared, consumed as TypeScript source — "type": "module", main: ./src/index.ts, with extensionless relative imports like export * from './db/index').
Under nuxt dev, SSR 500s on every route with:
Cannot find module '/<repo>/packages/shared/src/db/index' imported from /<repo>/packages/shared/src/index.ts
The workflow steps bundle externalizes @acme/shared instead of bundling it, so at runtime Node's native ESM loader hits the package's extensionless relative imports and fails. The production Rollup/Nitro build inlines the package (so the funnel routes work there), which is why this currently only breaks nuxt dev — but the same builder runs for the Vercel build, so it looks latent there too.
Root cause
@workflow/nitro's LocalBuilder / VercelBuilder (dist/builders.js) construct the builder with:
createBaseBuilderConfig({ workingDir: nitro.options.rootDir, /* ...no projectRoot */ })
BaseBuilder then defaults projectRoot = config.projectRoot || config.workingDir (i.e. apps/app). The module-specifier logic computes relative(projectRoot, filePath) for the sibling package → ../../packages/shared/... (escapes projectRoot), so it's treated as external rather than a bundle-able workspace package.
The framework-integrations docs already prescribe the fix — "If your framework integration lives in a subdirectory and your workflows import code from sibling workspace packages, pass projectRoot to BaseBuilder … use the smallest directory that contains every workspace package imported by your workflows." But @workflow/nitro's ModuleOptions only exposes dirs / typescriptPlugin / runtime / sourcemap — there's no way to set or override projectRoot, and the integration doesn't auto-derive it. (The 5.0.0-beta line doesn't add it either.)
Related prior art (both closed/completed, but for @workflow/next / general bundling — the projectRoot handling didn't reach the Nitro/Nuxt integration): #419, #1680.
Reproduction
- pnpm-workspaces monorepo with
apps/app (Nuxt + workflow/nuxt) and packages/shared (@acme/shared, "type": "module", main: ./src/index.ts, extensionless internal imports).
- A workflow under
apps/app/server/workflows/* imports from @acme/shared.
- Run the app's dev server → request
/ → 500 with the Cannot find module …/src/db/index error above.
(A minimal repro can be built from workbench/nitro-v3 + a sibling workspace package.)
Proposed fix (happy to open a PR)
Make the Nitro integration honor projectRoot, via either:
- Auto-derive the workspace root by walking up from
nitro.options.rootDir to the nearest pnpm-workspace.yaml / package.json#workspaces / lockfile, and pass it as projectRoot to createBaseBuilderConfig. Zero-config; matches the docs' guidance.
- and/or expose
projectRoot (or workspaceRoot) in @workflow/nitro ModuleOptions, forwarded by @workflow/nuxt.
Option 1 (auto-detect, with Option 2 as an explicit override) seems most aligned with the framework-integration design. Which would you prefer? Happy to send a PR with a test modeled on the existing discover-entries / module-specifier projectRoot tests plus a workbench/nitro-v3-based repro.
Bug Report
Environment
workflow: 4.2.4@workflow/nitro: 4.0.5@workflow/nuxt: 4.0.5@workflow/builders: 4.0.5nuxt: 4.2.2 (Nitro 2.13.1, Vite 6.4.1)apps/*+packages/*)Description
In a pnpm-workspaces monorepo, a Nuxt app (
apps/app, usingworkflow/nuxt) has workflows that import a sibling workspace package (@acme/sharedatpackages/shared, consumed as TypeScript source —"type": "module",main: ./src/index.ts, with extensionless relative imports likeexport * from './db/index').Under
nuxt dev, SSR 500s on every route with:The workflow steps bundle externalizes
@acme/sharedinstead of bundling it, so at runtime Node's native ESM loader hits the package's extensionless relative imports and fails. The production Rollup/Nitro build inlines the package (so the funnel routes work there), which is why this currently only breaksnuxt dev— but the same builder runs for the Vercel build, so it looks latent there too.Root cause
@workflow/nitro'sLocalBuilder/VercelBuilder(dist/builders.js) construct the builder with:BaseBuilderthen defaultsprojectRoot = config.projectRoot || config.workingDir(i.e.apps/app). The module-specifier logic computesrelative(projectRoot, filePath)for the sibling package →../../packages/shared/...(escapesprojectRoot), so it's treated as external rather than a bundle-able workspace package.The framework-integrations docs already prescribe the fix — "If your framework integration lives in a subdirectory and your workflows import code from sibling workspace packages, pass
projectRoottoBaseBuilder… use the smallest directory that contains every workspace package imported by your workflows." But@workflow/nitro'sModuleOptionsonly exposesdirs/typescriptPlugin/runtime/sourcemap— there's no way to set or overrideprojectRoot, and the integration doesn't auto-derive it. (The5.0.0-betaline doesn't add it either.)Related prior art (both closed/completed, but for
@workflow/next/ general bundling — theprojectRoothandling didn't reach the Nitro/Nuxt integration): #419, #1680.Reproduction
apps/app(Nuxt +workflow/nuxt) andpackages/shared(@acme/shared,"type": "module",main: ./src/index.ts, extensionless internal imports).apps/app/server/workflows/*imports from@acme/shared./→ 500 with theCannot find module …/src/db/indexerror above.(A minimal repro can be built from
workbench/nitro-v3+ a sibling workspace package.)Proposed fix (happy to open a PR)
Make the Nitro integration honor
projectRoot, via either:nitro.options.rootDirto the nearestpnpm-workspace.yaml/package.json#workspaces/ lockfile, and pass it asprojectRoottocreateBaseBuilderConfig. Zero-config; matches the docs' guidance.projectRoot(orworkspaceRoot) in@workflow/nitroModuleOptions, forwarded by@workflow/nuxt.Option 1 (auto-detect, with Option 2 as an explicit override) seems most aligned with the framework-integration design. Which would you prefer? Happy to send a PR with a test modeled on the existing
discover-entries/module-specifierprojectRoottests plus aworkbench/nitro-v3-based repro.