Skip to content

fix(cloudflare): omit worker entry from wrangler.json for static builds#4255

Draft
danielroe wants to merge 1 commit into
mainfrom
fix/static-wrangler
Draft

fix(cloudflare): omit worker entry from wrangler.json for static builds#4255
danielroe wants to merge 1 commit into
mainfrom
fix/static-wrangler

Conversation

@danielroe
Copy link
Copy Markdown
Member

🔗 Linked issue

resolves nuxt/nuxt#34186

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

https://developers.cloudflare.com/workers/static-assets/

this aims to fix the generated wrangler.json for fully-static sites

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@danielroe danielroe requested a review from pi0 as a code owner May 11, 2026 00:12
@vercel
Copy link
Copy Markdown

vercel Bot commented May 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nitro.build Ready Ready Preview, Comment May 11, 2026 0:12am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

The PR fixes static Nuxt output deployment to Cloudflare Workers by refactoring writeWranglerConfig to apply different wrangler configuration overrides based on build type: static builds set only asset directory, while non-static module builds also set the entry point and bundling rules.

Changes

Cloudflare Static Build Configuration

Layer / File(s) Summary
Static vs Non-Static Overrides Generation
src/presets/cloudflare/utils.ts
For cfTarget === "module", assetsDirectory is computed once and conditionally assigned: static builds set only overrides.assets.directory; non-static builds set overrides.main = "index.mjs" and overrides.assets with both directory and "ASSETS" binding.
Module Bundling Rules Conditional
src/presets/cloudflare/utils.ts
The no_bundle flag and ESModule scanning rules assignment now runs only when cfTarget === "module" and !nitro.options.static, preventing bundling configuration from being applied to static builds.
Test Suite for Static and Non-Static Builds
test/unit/cloudflare.utils.test.ts
New Vitest suite with createNitroStub helper and lifecycle management validates non-static module builds emit main, ASSETS binding, no_bundle, and rules, while static builds omit these entries while retaining assets.directory.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • nitrojs/nitro#4046: Also modifies writeWranglerConfig in the Cloudflare preset; injects scheduled task cron triggers while this PR adds static build handling logic to the same configuration generation function.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits format with 'fix' type and clear description of the change.
Description check ✅ Passed The description clearly relates to the changeset by referencing the static assets documentation and explaining the wrangler.json generation issue.
Linked Issues check ✅ Passed The code changes directly address the linked issue by omitting the worker entry from wrangler.json for static builds, preventing deployment failures.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing wrangler.json generation for static builds with appropriate unit tests, with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/static-wrangler

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 11, 2026

Open in StackBlitz

npm i https://pkg.pr.new/nitro@4255

commit: 729d840

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/unit/cloudflare.utils.test.ts (1)

48-71: ⚡ Quick win

Solid regression coverage; consider an additional case with a non-default baseURL.

The two cases pin down the main regression nicely. A third case with baseURL: "/foo/" (or "/foo/bar/") would also exercise assetsDirectory for non-root deployments, where the relative path differs (e.g. ".." or further up) and would have caught the "..".repeat issue noted on utils.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/cloudflare.utils.test.ts` around lines 48 - 71, Add a third unit
test that mirrors the existing two but sets the nitro stub's baseURL to a
non-root path (e.g. baseURL: "/foo/" or "/foo/bar/") using createNitroStub({
static: false, baseURL: "/foo/" }) (or static:true variant as appropriate), call
writeWranglerConfig(nitro, "module"), read the generated JSON from
".output/server/wrangler.json" (using readFileSync and JSON.parse), and assert
that the assets directory/binding path is computed correctly for non-root
deployments (verify config.assets.directory and config.assets.binding or absence
of main/no_bundle/rules as per static vs non-static). This will exercise
assetsDirectory logic in utils.ts and catch the repeated ".." bug.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/presets/cloudflare/utils.ts`:
- Around line 231-237: assetsDirectory calculation uses "..".repeat(N) which
concatenates dots (e.g. "....") instead of producing repeated "../" segments,
breaking path traversal for baseURL depth >1; update the resolve call that
computes assetsDirectory (the call using wranglerConfigDir,
resolve(nitro.options.output.publicDir, "..".repeat(...))) to supply N separate
".." path segments rather than a single repeated-dot string—e.g., compute count
= nitro.options.baseURL.split("/").filter(Boolean).length and pass either an
array of ".." repeated count spread into resolve or a single
"../".repeat(count).slice(0,-1) string so pathe.resolve correctly climbs N
directories from nitro.options.output.publicDir when resolving against
wranglerConfigDir.

---

Nitpick comments:
In `@test/unit/cloudflare.utils.test.ts`:
- Around line 48-71: Add a third unit test that mirrors the existing two but
sets the nitro stub's baseURL to a non-root path (e.g. baseURL: "/foo/" or
"/foo/bar/") using createNitroStub({ static: false, baseURL: "/foo/" }) (or
static:true variant as appropriate), call writeWranglerConfig(nitro, "module"),
read the generated JSON from ".output/server/wrangler.json" (using readFileSync
and JSON.parse), and assert that the assets directory/binding path is computed
correctly for non-root deployments (verify config.assets.directory and
config.assets.binding or absence of main/no_bundle/rules as per static vs
non-static). This will exercise assetsDirectory logic in utils.ts and catch the
repeated ".." bug.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 736d0144-64e6-4f1f-ad21-437d6237738c

📥 Commits

Reviewing files that changed from the base of the PR and between d2e6490 and 729d840.

📒 Files selected for processing (2)
  • src/presets/cloudflare/utils.ts
  • test/unit/cloudflare.utils.test.ts

Comment on lines +231 to +237
const assetsDirectory = relative(
wranglerConfigDir,
resolve(
nitro.options.output.publicDir,
"..".repeat(nitro.options.baseURL.split("/").filter(Boolean).length)
)
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the `"..".repeat` pattern's behavior and check if it appears elsewhere in the repo.
rg -nP '\.\.".repeat\(' --type=ts -C2

# Sanity check pathe resolve semantics
cat <<'EOF' > /tmp/check.mjs
import { resolve } from "pathe";
console.log("repeat(0):", JSON.stringify("..".repeat(0)));
console.log("repeat(2):", JSON.stringify("..".repeat(2)));
console.log("resolve depth 2 (broken):", resolve("/tmp/out/public", "..".repeat(2)));
console.log("resolve depth 2 (spread):", resolve("/tmp/out/public", "..", ".."));
EOF
# (Cannot execute in sandbox; included for reference.)
cat /tmp/check.mjs

Repository: nitrojs/nitro

Length of output: 703


🏁 Script executed:

# First, check if pathe is available in the repo
cd /tmp && node --input-type=module <<'EOF'
import { resolve } from "pathe";

console.log("=== String repetition results ===");
console.log('"..".repeat(0):', JSON.stringify("..".repeat(0)));
console.log('"..".repeat(1):', JSON.stringify("..".repeat(1)));
console.log('"..".repeat(2):', JSON.stringify("..".repeat(2)));
console.log('"..".repeat(3):', JSON.stringify("..".repeat(3)));

console.log("\n=== pathe.resolve() behavior ===");
const base = "/app/dist/public";

// Test with broken input (4 consecutive dots)
const brokenPath = resolve(base, "..".repeat(2));
console.log("resolve(base, '....'):", brokenPath);

// Test with correct input (spread operator)
const correctPath1 = resolve(base, "..", "..");
console.log("resolve(base, '..', '..'):", correctPath1);

// Test with correct input (alternative: "../" repeated)
const correctPath2 = resolve(base, "../".repeat(2).slice(0, -1));
console.log("resolve(base, '../'.repeat(2).slice(0,-1)):", correctPath2);

console.log("\n=== Comparison ===");
console.log("brokenPath === correctPath1:", brokenPath === correctPath1);
console.log("correctPath1 === correctPath2:", correctPath1 === correctPath2);
EOF

Repository: nitrojs/nitro

Length of output: 1045


🏁 Script executed:

# Search for tests related to cloudflare preset and assetsDirectory
fd -e test.ts -e spec.ts -e test.js | xargs grep -l "cloudflare\|assetsDirectory" 2>/dev/null || echo "No direct test files found"

# Search in test directory
fd . test --type f 2>/dev/null | head -20

Repository: nitrojs/nitro

Length of output: 855


🏁 Script executed:

# Check the actual code context around the fix to understand the impact
sed -n '220,250p' src/presets/cloudflare/utils.ts

Repository: nitrojs/nitro

Length of output: 1109


🏁 Script executed:

# Check the cloudflare utils test file for baseURL and assetsDirectory tests
cat -n test/unit/cloudflare.utils.test.ts

Repository: nitrojs/nitro

Length of output: 3232


🏁 Script executed:

# Also check the presets test files for baseURL coverage
rg -n "baseURL" test/presets/cloudflare-module.test.ts test/presets/cloudflare-pages.test.ts --context=3

Repository: nitrojs/nitro

Length of output: 39


🏁 Script executed:

# Check pathe package source or docs to understand resolve behavior
fd package.json -x grep -l pathe {} \; | head -5

Repository: nitrojs/nitro

Length of output: 72


🏁 Script executed:

# Let's verify the JavaScript string behavior and reason about resolve semantics
node --eval='
console.log("JavaScript string repeat:");
console.log("\"..\" + \"..\" =", "\"..\" + \"..\"" + " -> " + (".." + ".."));
console.log(".repeat(2) =", ".repeat(2)" + " -> " + ("..".repeat(2)));
console.log("\nNode.js path.resolve semantics:");
console.log("path.resolve treats \"..\" as parent dir reference");
console.log("path.resolve treats \".....\" (or any non-.. string) as a directory name");
console.log("\nTherefore:");
console.log("resolve(base, \"..\", \"..\") -> climbs 2 levels");
console.log("resolve(base, \"....\") -> resolves to base/.... (literal directory)");
'

Repository: nitrojs/nitro

Length of output: 426


🏁 Script executed:

# Check if there are any tests with baseURL other than "/"
rg "baseURL" test/ -t ts --context=2 | head -30

Repository: nitrojs/nitro

Length of output: 2024


🏁 Script executed:

# Verify the proposed fix syntax is valid TypeScript
cat > /tmp/test_fix.ts << 'EOF'
import { resolve } from "pathe";

// Original broken approach
const broken = resolve("/tmp", "..".repeat(2));

// Proposed fix: spread operator
const fixed1 = resolve("/tmp", ...Array.from(
  { length: 2 },
  () => ".."
));

// Alternative mentioned in the comment
const fixed2 = resolve("/tmp", "../".repeat(2).slice(0, -1));

console.log("Broken:", broken);
console.log("Fixed1:", fixed1);
console.log("Fixed2:", fixed2);
EOF
cat /tmp/test_fix.ts

Repository: nitrojs/nitro

Length of output: 481


"..".repeat(N) produces broken paths for baseURL depth > 1.

"..".repeat(2) returns the string "...." (a single 4-dot segment), not "../..". pathe.resolve(publicDir, "....") then treats "...." as a literal directory name and resolves to publicDir + "/...." instead of climbing two levels up. So assetsDirectory is incorrect for any baseURL with two or more non-empty segments (e.g. "/foo/bar/"). The default baseURL = "/" (length 0 → "") and single-segment baseURL = "/foo/" (length 1 → "..") happen to work, which is why the new tests don't catch this.

This may be pre-existing (just extracted into a variable here), but the diff surface is a good place to fix it.

🐛 Proposed fix
     const assetsDirectory = relative(
       wranglerConfigDir,
       resolve(
         nitro.options.output.publicDir,
-        "..".repeat(nitro.options.baseURL.split("/").filter(Boolean).length)
+        ...Array.from(
+          { length: nitro.options.baseURL.split("/").filter(Boolean).length },
+          () => ".."
+        )
       )
     );

Note: pathe.resolve is variadic, so spreading multiple ".." segments climbs the directory tree correctly for any depth. Alternatively, "../".repeat(N).slice(0, -1) as a single argument also resolves correctly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/cloudflare/utils.ts` around lines 231 - 237, assetsDirectory
calculation uses "..".repeat(N) which concatenates dots (e.g. "....") instead of
producing repeated "../" segments, breaking path traversal for baseURL depth >1;
update the resolve call that computes assetsDirectory (the call using
wranglerConfigDir, resolve(nitro.options.output.publicDir, "..".repeat(...))) to
supply N separate ".." path segments rather than a single repeated-dot
string—e.g., compute count =
nitro.options.baseURL.split("/").filter(Boolean).length and pass either an array
of ".." repeated count spread into resolve or a single
"../".repeat(count).slice(0,-1) string so pathe.resolve correctly climbs N
directories from nitro.options.output.publicDir when resolving against
wranglerConfigDir.

@pi0
Copy link
Copy Markdown
Member

pi0 commented May 11, 2026

Nitro v3 / vite has no working full staic mode yet. Pending till it does so i can test.

@pi0 pi0 marked this pull request as draft May 11, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot deploy static Nuxt output to Cloudflare Workers

2 participants