Environment
- nitro
pkg.pr.new/nitro@078fa53 (main snapshot, 2026-08)
- Reproduced with the
cloudflare_module preset, checked with wrangler dev
Reproduction
Any chunk whose string data contains createRequire(import.meta.url). Distilled to a minimal server
handler (our real case embedded a copy of h3/dist/h3.mjs as data for a dynamic-worker loader):
// server/routes/kit.ts
export default defineEventHandler(() => {
// Module text carried as data — e.g. sources for a sandbox/loader, embedded at build time.
const kit = {
"h3.mjs": "const _require = createRequire(import.meta.url);\nexport default _require;",
};
return Object.keys(kit);
});
Build with the cloudflare_module preset, then wrangler dev .output/server/index.mjs:
✘ [ERROR] Expected "}" but found "file"
.output/server/index.mjs:101:119284
Describe the bug
guardCreateRequire runs in generateBundle (from dist/_presets3.mjs):
function guardCreateRequire() {
return {
name: "nitro:cloudflare-guard-createRequire",
generateBundle(_options, bundle) {
for (const chunk of Object.values(bundle)) if (chunk.type === "chunk" && chunk.code?.includes("createRequire(import.meta.url)")) chunk.code = chunk.code.replace(/createRequire\(import\.meta\.url\)/g, "createRequire(import.meta.url || \"file:///\")");
}
};
}
The replace is applied to the whole chunk text, so the occurrence inside the string literal is rewritten
too — and the replacement's " characters are not escaped for the literal they land in:
// before (valid):
const kit = { "h3.mjs": "const _require = createRequire(import.meta.url);\n..." };
// after (parse error — the inserted `"` terminates the literal):
const kit = { "h3.mjs": "const _require = createRequire(import.meta.url || "file:///");\n..." };
stripBareNodeImports has the same defect class. Its multiline regex
const BARE_NODE_IMPORT_RE = /^import\s*['"]node:[^'"]+['"];?\s*$/gm;
matches a line inside a template literal carrying real newlines, silently deleting content from the
embedded text:
// before:
const source = `import "node:fs";
export const marker = 1;`;
// after (the first line of the DATA is gone):
const source = `export const marker = 1;`;
The rewrite itself is correct and needed for real code (workerd defines no import.meta.url). It must
just be scoped to code: matches inside string literals, template text, and comments are data and must
survive byte-identical.
Environment
pkg.pr.new/nitro@078fa53(main snapshot, 2026-08)cloudflare_modulepreset, checked withwrangler devReproduction
Any chunk whose string data contains
createRequire(import.meta.url). Distilled to a minimal serverhandler (our real case embedded a copy of
h3/dist/h3.mjsas data for a dynamic-worker loader):Build with the
cloudflare_modulepreset, thenwrangler dev .output/server/index.mjs:Describe the bug
guardCreateRequireruns ingenerateBundle(fromdist/_presets3.mjs):The replace is applied to the whole chunk text, so the occurrence inside the string literal is rewritten
too — and the replacement's
"characters are not escaped for the literal they land in:stripBareNodeImportshas the same defect class. Its multiline regexmatches a line inside a template literal carrying real newlines, silently deleting content from the
embedded text:
The rewrite itself is correct and needed for real code (workerd defines no
import.meta.url). It mustjust be scoped to code: matches inside string literals, template text, and comments are data and must
survive byte-identical.