Commit ec0e98e
fix(ui): add @swc/helpers direct dependency (#17893)
## What
Declares `@swc/helpers` (`>=0.5.17`) as a direct dependency of
`@payloadcms/ui`.
## Why
`@payloadcms/ui` ships source containing modern regexes that use **named
capture groups** (e.g. `HtmlDiff` in
`src/elements/HTMLDiff/diff/index.ts`: `/^<(?<name>[^\s/>]+)[^>]*>$/`).
Named capture groups are ES2018.
When a host app compiles Payload's shipped code down to an older browser
target, the compiler's SWC pass down-levels these regexes and **injects
an import of `@swc/helpers/_/_wrap_reg_exp`**. That subpath was only
added in `@swc/helpers@0.5.17`.
Because `@payloadcms/ui` did not declare `@swc/helpers`, the host relied
on whatever version was hoisted transitively. Next.js 16.3.0 provides
`@swc/helpers@0.5.15`, which lacks the subpath, so the build fails:
```
Module not found: Can't resolve '@swc/helpers/_/_wrap_reg_exp'
```
Declaring `@swc/helpers` as a dependency co-locates a compatible copy in
`@payloadcms/ui`'s own `node_modules`. The bundler resolves the injected
`@swc/helpers` specifier via normal node resolution starting from the ui
module's location, so it finds the co-located `>=0.5.17` copy before
falling back to the hoisted `0.5.15`.
## Note: the helper is not in our published output
Worth clarifying since it's easy to misread: the shipped
`@payloadcms/ui` dist does **not** itself contain a
`@swc/helpers/_/_wrap_reg_exp` import (ui's own `.swcrc` targets
`esnext` with helpers inlined). The import is generated **in the
consumer's build**, when its compiler down-levels the named-capture
regex we ship. The fix belongs here because it's our shipped regex that
triggers the injection.
## Verification
Reproduced against a real Next.js 16.3.0 build using the byte-for-byte
`HtmlDiff` module from published `@payloadcms/ui@3.88.0`. Next 16 builds
with Turbopack, which requires the missing subpath.
| State | `@swc/helpers` on disk | `next build` |
| ------------------------------ |
----------------------------------------- |
-------------------------------------------------------------- |
| Before this PR | hoisted `0.5.15` only | ❌ `Can't resolve
'@swc/helpers/_/_wrap_reg_exp'` |
| This PR | ui-local `0.5.23`, hoisted still `0.5.15` | ✅ Compiled
successfully |
| Control (remove ui-local copy) | back to hoisted `0.5.15` | ❌ fails
again — confirms the co-located copy is what fixes it |
## Dependency vs. peer
Added as a direct `dependency`, not a `peerDependency`. It is an
implementation detail of our SWC compilation output, not a contract with
consumers. A peer dependency would only warn on install and leave
resolution to the same fragile hoisting that causes this bug.
## Workaround for affected users (until this ships)
Force a compatible version tree-wide, e.g. with pnpm:
```jsonc
// package.json
"pnpm": { "overrides": { "@swc/helpers": ">=0.5.17" } }
```
---------
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent 1c0c454 commit ec0e98e
2 files changed
Lines changed: 52 additions & 39 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
| 149 | + | |
149 | 150 | | |
150 | 151 | | |
151 | 152 | | |
| |||
0 commit comments