Summary
In a pnpm workspace monorepo, upgrading workflow from 4.5.0 to any later 4.x release makes the SWC workflow plugin fail to parse TypeScript decorators in a workspace package that the Next.js app depends on. Every decorated class in that package fails with:
SWC transform failed: x Expression expected
,-[lib/shared/src/services/taskService.ts:46:1]
46 | @processCoachInjectable()
: ^
Caused by:
Syntax Error
experimentalDecorators: true is set in every relevant tsconfig.json, and the same build is clean on 4.5.0.
Versions
workflow |
@workflow/builders |
web build |
| 4.5.0 |
4.1.0 |
pass, 0 errors |
| 4.6.0 |
4.1.1 |
fail, 41 errors |
| 4.6.2 |
4.1.3 |
fail, 41 errors |
| 4.7.0 |
4.1.4 |
fail, 41 errors |
| 4.8.0 |
4.1.5 |
fail, 41 errors |
Next.js 16, pnpm 10.26.2, Node 22.22.2, macOS. Same result in CI on Linux.
Setup
repo-root/
pnpm-workspace.yaml <- no tsconfig.json at this level
web/ <- Next.js app, withWorkflow(nextConfig)
tsconfig.json <- experimentalDecorators: true
lib/shared/ <- workspace package "@lib/shared"
tsconfig.json <- experimentalDecorators: true, emitDecoratorMetadata: true
src/… <- ~150 classes decorated for a tsyringe DI container
web/tsconfig.json maps @lib/shared/workflows/* to ../lib/shared/src/workflows/* so the compiler sees "use step" / "use workflow" directives in source. Workflow steps reach the DI container, which lazily loads the service graph with a dynamic import(). On 4.6+ the compiler follows that dynamic import into the workspace package's TypeScript source and then fails to parse the decorators there.
What I ruled out
Not a missing experimentalDecorators. All three tsconfigs set it. Suspecting the new workspace-root resolution meant findUp landed somewhere without a tsconfig, I added a root tsconfig.json with experimentalDecorators and emitDecoratorMetadata. Still 41 errors — including after deleting web/.next to rule out a cached transform.
Not user-configurable. applySwcTransform takes projectRoot and moduleSpecifierRoot, and swc-esbuild-plugin reads options.projectRoot, but workflow/next never passes either — grepping the shipped dist for those names returns nothing. withWorkflow's documented options are workflows.local.port only.
Suspected cause
Diffing @workflow/builders 4.1.0 against 4.1.5, the change that lines up is the new resolveProjectRoot() in config-helpers.js, which walks up to the workspace root by looking for pnpm-workspace.yaml (then lockfiles), together with the split of the old single projectRoot into projectRoot (tsconfig lookup) and moduleSpecifierRoot (package resolution).
The consequence for a monorepo: files in lib/shared/src used to be project-local relative to web/, and are now resolved as a workspace package. isProjectLocalFile(path, moduleSpecifierRoot) returns false for them where it previously returned true. I did not chase exactly which branch then drops the decorator parser options, but the version boundary and the diff both point here.
Workaround
Route the workspace package's internals through its exports map so the compiler sees compiled dist JavaScript, which has no decorators left to parse. That clears all 41 errors, but within a single compilation the src and dist copies of a class are distinct nominal types, so every boundary where they meet needs its types realigned — impractical past a handful of files.
Ask
Either restore the pre-4.6 project-root default, or expose projectRoot / moduleSpecifierRoot through withWorkflow so a monorepo can opt out. If treating workspace packages as packages is intentional, documenting that decorated source in a workspace package must be consumed as compiled output would at least make the upgrade path discoverable — there is currently no migration note for 4.5 → 4.6 (the shipped docs/changelog/index.mdx reads ## 2026 / - TBD).
Happy to test a patch against the real repo.
Summary
In a pnpm workspace monorepo, upgrading
workflowfrom 4.5.0 to any later 4.x release makes the SWC workflow plugin fail to parse TypeScript decorators in a workspace package that the Next.js app depends on. Every decorated class in that package fails with:experimentalDecorators: trueis set in every relevanttsconfig.json, and the same build is clean on 4.5.0.Versions
workflow@workflow/buildersNext.js 16, pnpm 10.26.2, Node 22.22.2, macOS. Same result in CI on Linux.
Setup
web/tsconfig.jsonmaps@lib/shared/workflows/*to../lib/shared/src/workflows/*so the compiler sees"use step"/"use workflow"directives in source. Workflow steps reach the DI container, which lazily loads the service graph with a dynamicimport(). On 4.6+ the compiler follows that dynamic import into the workspace package's TypeScript source and then fails to parse the decorators there.What I ruled out
Not a missing
experimentalDecorators. All three tsconfigs set it. Suspecting the new workspace-root resolution meantfindUplanded somewhere without a tsconfig, I added a roottsconfig.jsonwithexperimentalDecoratorsandemitDecoratorMetadata. Still 41 errors — including after deletingweb/.nextto rule out a cached transform.Not user-configurable.
applySwcTransformtakesprojectRootandmoduleSpecifierRoot, andswc-esbuild-pluginreadsoptions.projectRoot, butworkflow/nextnever passes either — grepping the shippeddistfor those names returns nothing.withWorkflow's documented options areworkflows.local.portonly.Suspected cause
Diffing
@workflow/builders4.1.0 against 4.1.5, the change that lines up is the newresolveProjectRoot()inconfig-helpers.js, which walks up to the workspace root by looking forpnpm-workspace.yaml(then lockfiles), together with the split of the old singleprojectRootintoprojectRoot(tsconfig lookup) andmoduleSpecifierRoot(package resolution).The consequence for a monorepo: files in
lib/shared/srcused to be project-local relative toweb/, and are now resolved as a workspace package.isProjectLocalFile(path, moduleSpecifierRoot)returns false for them where it previously returned true. I did not chase exactly which branch then drops the decorator parser options, but the version boundary and the diff both point here.Workaround
Route the workspace package's internals through its
exportsmap so the compiler sees compileddistJavaScript, which has no decorators left to parse. That clears all 41 errors, but within a single compilation thesrcanddistcopies of a class are distinct nominal types, so every boundary where they meet needs its types realigned — impractical past a handful of files.Ask
Either restore the pre-4.6 project-root default, or expose
projectRoot/moduleSpecifierRootthroughwithWorkflowso a monorepo can opt out. If treating workspace packages as packages is intentional, documenting that decorated source in a workspace package must be consumed as compiled output would at least make the upgrade path discoverable — there is currently no migration note for 4.5 → 4.6 (the shippeddocs/changelog/index.mdxreads## 2026/- TBD).Happy to test a patch against the real repo.