fix(prerender): make CJS compat plugin work with Vite 6 nodeRunnerEnv#1735
fix(prerender): make CJS compat plugin work with Vite 6 nodeRunnerEnv#1735lisa-assistant wants to merge 1 commit into
Conversation
The cedarCjsCompatPlugin from @cedarjs/vite uses an async transform hook and relies on buildStart for lexer initialization. Neither is reliable for non-default Vite environments like nodeRunnerEnv — buildStart may not fire and async transforms can be skipped, causing CJS files to pass through untransformed and fail with 'module is not defined'. This replaces the imported plugin with a self-contained cjsCompatPlugin local to node-runner.ts that: - Initializes cjs-module-lexer eagerly at module load (before createServer) - Uses a synchronous transform hook that works in all Vite environments - Falls back to a brace-counting parser for hand-written CJS with function values that cjs-module-lexer cannot statically detect All 16 node-runner tests now pass, including the cedarjsJobPathInjectorPlugin test that imports @cedarjs/jobs (esbuild-compiled CJS with PrismaAdapter). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
👷 Deploy request for cedarjs pending review.Visit the deploys page to approve it
|
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run-many -t test --minWorkers=1 --maxWorkers=4 |
✅ Succeeded | 1m 19s | View ↗ |
nx run-many -t build:pack --exclude create-ceda... |
✅ Succeeded | 9s | View ↗ |
nx run-many -t build |
✅ Succeeded | 25s | View ↗ |
nx run-many -t test:types |
✅ Succeeded | 7s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-05-10 09:19:49 UTC
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run-many -t build |
✅ Succeeded | 15s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-05-10 09:13:45 UTC
Greptile SummaryThis PR fixes a real problem:
Confidence Score: 3/5The transform fix is sound for the tested cases, but the missing The eager-init and synchronous-transform changes correctly solve the stated problem, and the brace-counting fallback adds useful coverage. However, any TypeScript-compiled npm package that has packages/prerender/src/graphql/node-runner.ts — specifically the Important Files Changed
Reviews (1): Last reviewed commit: "fix(prerender): make CJS compat plugin w..." | Re-trigger Greptile |
| const namedExportLines = [...namedExports] | ||
| .map( | ||
| (name) => | ||
| `export const ${name} = __cjs_result__[${JSON.stringify(name)}]`, | ||
| ) | ||
| .join('\n') | ||
|
|
||
| return { | ||
| code: ` | ||
| import { createRequire as __createRequire__ } from 'node:module' | ||
| const require = __createRequire__(${filePath}) | ||
| const module = { exports: {} } | ||
| const exports = module.exports | ||
| const __dirname = ${dirPath} | ||
| const __filename = ${filePath} | ||
| ;(function() { | ||
| ${code} | ||
| }).call(module.exports) | ||
| const __cjs_result__ = module.exports | ||
| export default __cjs_result__ | ||
| ${namedExportLines} | ||
| `, |
There was a problem hiding this comment.
Missing
__esModule default-export unwrapping
The original cedarCjsCompatPlugin detects the __esModule: true flag (set by TypeScript's compiler and Babel when a module has a real default export) and unwraps module.exports.default before emitting export default. The new plugin always emits export default __cjs_result__, so a consumer that does import Foo from './ts-compiled-module' on a TypeScript-compiled CJS file with a default export will receive the entire module.exports wrapper object ({ __esModule: true, default: ActualFoo, ... }) instead of ActualFoo. Because exports.default = ActualFoo matches the heuristic regex, the file is transformed, and the regression is silent.
| if (lexerParse) { | ||
| try { | ||
| const { exports } = lexerParse(code) | ||
| for (const e of exports) { | ||
| if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(e) && e !== 'default') { | ||
| namedExports.add(e) | ||
| } | ||
| } | ||
| } catch { | ||
| // Ignore — extractCjsNamedExports result is still used | ||
| } |
There was a problem hiding this comment.
__esModule leaks into named exports
The named-export filter (e !== 'default') does not exclude __esModule. Both cjs-module-lexer and extractCjsNamedExports may detect it, causing the generated wrapper to emit export const __esModule = __cjs_result__["__esModule"]. The original plugin explicitly filtered __esModule out of the named-export list with e !== '__esModule' && e !== 'default'.
| transform(code, id) { | ||
| // Only handle plain .js / .cjs files — TypeScript and JSX are already | ||
| // transformed by Vite's esbuild plugin and will be valid ESM. | ||
| if (!/\.[cm]?js$/.test(id)) { | ||
| return null | ||
| } | ||
|
|
||
| // Quick heuristic: skip files that don't look like CJS | ||
| if (!/\bmodule\.exports\b|\bexports\.\w+/.test(code)) { | ||
| return null |
There was a problem hiding this comment.
No UMD guard — silently produces broken output
The original plugin detected UMD wrappers (typeof module.exports === 'object', typeof define === 'function', etc.) and threw an explicit, actionable error. The new plugin skips that check entirely: a UMD file passes the heuristic (module.exports is present), gets wrapped in the CJS shim, and produces broken runtime output with no diagnostic message. Adding the same UMD regex checks from the original plugin before the transform would preserve the fail-fast behavior.
|
Closing — the upstream implementation in |

Problem
node-runner.tsimportscedarCjsCompatPluginfrom@cedarjs/vite, but that plugin has two issues that cause it to silently skip transforming CJS files when used with a non-default Vite environment (nodeRunnerEnv):buildStartis not guaranteed to fire for non-default environments — socjs-module-lexernever gets initialized, andlexerParsestaysnull.async transformcan be skipped in non-default environments — CJS files pass through untransformed, causing'module' is not definederrors at runtime.The result: 15 of 16
node-runnertests fail.Fix
Replaces the imported plugin with a self-contained
cjsCompatPluginlocal tonode-runner.tsthat:cjs-module-lexereagerly at module load (a module-levelPromiseawaited increateViteServerbefore the server is created), so it's always ready before any transform runs.transformhook that works reliably in all Vite environments.extractCjsNamedExports) for hand-written CJS with function values thatcjs-module-lexercannot statically detect (e.g.module.exports = { handler: function() {} }).Test results
All 16
node-runnertests now pass, including:uses cedarjsJobPathInjectorPlugin to handle job files without errors— imports@cedarjs/jobs(esbuild-compiled CJS withPrismaAdapter)imports CommonJS module— hand-written CJS with function values