Skip to content

Conversation

@huang-julien
Copy link
Member

@huang-julien huang-julien commented Sep 13, 2025

🔗 Linked issue

fix #33217 (comment)
resolves #32802

📚 Description

Should we put it under a flag to avoid unexpected behavior for our users ?

@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Sep 13, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@33222

nuxt

npm i https://pkg.pr.new/nuxt@33222

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@33222

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@33222

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@33222

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@33222

commit: d53fd26

@coderabbitai
Copy link

coderabbitai bot commented Sep 13, 2025

Walkthrough

Redirect handling in packages/nuxt/src/app/middleware/manifest-route-rule.ts was changed to compute a redirect path that uses rules.redirect or rules.redirect plus the current route hash when the target lacks a hash. The computed path is used for navigation and is returned when a redirect is taken; the function returns false only when an in-app navigation occurs. Tests and fixtures were added: a Nitro route rule redirect for /route-rules/redirect/, a page with a NuxtLink to /route-rules/redirect#hello, a redirect page component, and a Vitest test validating client-side navigation with a hash.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description Check ❓ Inconclusive The description links to the relevant issue but consists only of a question about feature flags and does not summarise the implemented changes or explain how they resolve the hash-preservation bug. Please expand the description to detail the hash-preserving redirect implementation, its impact on anchor links and any considerations for feature flags so reviewers have full context.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately describes the main change, namely preserving hash fragments in routeRules redirects, and follows the conventional commit style, making it clear to reviewers what the PR addresses.
Linked Issues Check ✅ Passed The updated middleware now computes redirects by appending the original route’s hash when the target lacks one, and the added tests verify client-side navigation retains anchors, directly fulfilling the primary requirement of preserving hash fragments from issue #32802.
Out of Scope Changes Check ✅ Passed All modifications—including middleware logic, tests and fixture updates—relate solely to hash-preserving redirects in routeRules, with no unrelated code or feature alterations introduced.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/manifestrouterulesredirect

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a73f51e and d53fd26.

📒 Files selected for processing (1)
  • packages/nuxt/src/app/middleware/manifest-route-rule.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/middleware/manifest-route-rule.ts
🔇 Additional comments (2)
packages/nuxt/src/app/middleware/manifest-route-rule.ts (2)

9-9: Note: Redirect's hash takes precedence over navigation's hash.

When both rules.redirect contains a hash and the user navigates with a hash (e.g., /source#anchor → redirect to /target#default), the redirect's hash (#default) takes precedence. This is the correct choice since redirects are explicitly configured, but ensure this behavior aligns with expectations.

Consider documenting this precedence rule if it's not already clear to users.


9-14: Add tests for manifest-route-rule hash preservation
packages/nuxt/src/app/middleware/manifest-route-rule.ts lacks test coverage for its hash logic. Add tests verifying:

  • rules.redirect without # appends to.hash (including the leading #)
  • rules.redirect with an existing #... remains unchanged
  • External redirects use window.location.href and internal redirects return the computed path
    Confirm to.hash includes the # prefix as expected by Vue Router.

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.

Copy link

@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: 0

🧹 Nitpick comments (2)
packages/nuxt/src/app/middleware/manifest-route-rule.ts (2)

8-16: Consider feature flag protection for this behavioural change.

As mentioned in the PR objectives, this change modifies existing redirect behaviour and could potentially cause unexpected results for users who rely on the current behaviour. Consider gating this behind a feature flag to allow gradual rollout.

You could introduce a feature flag check:

  if (rules.redirect) {
+   const preserveHash = process.env.NUXT_PRESERVE_REDIRECT_HASH !== 'false'
    const hash = window.location.hash
    const redirectHasHash = rules.redirect.includes('#')
+   const hashToAppend = preserveHash && !redirectHasHash ? hash : ''
    if (hasProtocol(rules.redirect, { acceptRelative: true })) {
-     window.location.href = rules.redirect + (redirectHasHash ? '' : hash)
+     window.location.href = rules.redirect + hashToAppend
      return false
    }
-   return rules.redirect + (redirectHasHash ? '' : hash)
+   return rules.redirect + hashToAppend
  }

15-15: Normalise and percent-encode hash before concatenation

packages/nuxt/src/app/middleware/manifest-route-rule.ts:15 — current: return rules.redirect + (redirectHasHash ? '' : hash)

  • Ensure the fragment has no leading '#' and is percent‑encoded before appending: const frag = hash.startsWith('#') ? hash.slice(1) : hash; return rules.redirect + (redirectHasHash ? '' : '#' + encodeURIComponent(frag));
  • Rationale: avoids double "##", malformed '%' sequences and inconsistent browser percent‑encoding for non‑ASCII; use URL.hash or history.pushState() for deterministic history/hashchange behaviour.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 412786a and 563d059.

📒 Files selected for processing (1)
  • packages/nuxt/src/app/middleware/manifest-route-rule.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/middleware/manifest-route-rule.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: code
  • GitHub Check: build
🔇 Additional comments (2)
packages/nuxt/src/app/middleware/manifest-route-rule.ts (2)

9-10: LGTM! Clean variable declarations for hash preservation logic.

The local variables clearly capture the current hash and check if the redirect target already contains a hash fragment.


12-12: Verify hash handling across redirect scenarios

Current implementation appends window.location.hash when rules.redirect does not include '#' (external targets use window.location.href; internal targets return the redirect string). Location: packages/nuxt/src/app/middleware/manifest-route-rule.ts (lines 8–15).

  • Verify behaviour for redirects that already include a fragment, contain query strings (no fragment), are protocol-relative/non-HTTP, or are hash-only (e.g. '#/path').
  • Add unit tests covering: external URL with/without fragment, internal route with/without fragment, and hash-only redirects.

@codspeed-hq
Copy link

codspeed-hq bot commented Sep 13, 2025

CodSpeed Performance Report

Merging #33222 will not alter performance

Comparing fix/manifestrouterulesredirect (d53fd26) with main (3ef2f3b)

Summary

✅ 10 untouched

@danielroe
Copy link
Member

can you reproduce this issue minimally? was just looking at this but things seem to work

@huang-julien huang-julien marked this pull request as draft October 5, 2025 21:23
@huang-julien
Copy link
Member Author

huang-julien commented Oct 5, 2025

can you reproduce this issue minimally? was just looking at this but things seem to work

Doesn''t work on my side on the main branch. I'll write a E2E test

Edit: Oh i just understood what you said. Yes, i'm write a test for this

@huang-julien huang-julien marked this pull request as ready for review October 5, 2025 21:52
@huang-julien
Copy link
Member Author

It's a bit strange. page.waitForURL doesn't work. Feels like an issue with playwright

Copy link

@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/basic.test.ts (1)

101-106: Consider using Playwright's page.waitForURL() instead of vi.waitFor().

Playwright provides a built-in page.waitForURL() method specifically designed for waiting for URL changes, which offers better error messages and more reliable behaviour than polling with vi.waitFor(). Other tests in this file successfully use page.waitForURL() (e.g., line 1222).

Apply this diff to use the more appropriate Playwright method:

-  await vi.waitFor(() => page.url() === url('/#hello'), { timeout: 5_000 })
+  await page.waitForURL(url('/#hello'), { timeout: 5_000 })

If you're encountering issues with page.waitForURL() as mentioned in the PR comments, please document the specific problem so it can be investigated further, as this method works correctly in other tests within the same file.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3115754 and 4b786a6.

📒 Files selected for processing (4)
  • test/basic.test.ts (2 hunks)
  • test/fixtures/basic/app/pages/index.vue (1 hunks)
  • test/fixtures/basic/app/pages/route-rules/redirect.vue (1 hunks)
  • test/fixtures/basic/nuxt.config.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • test/fixtures/basic/app/pages/route-rules/redirect.vue
🧰 Additional context used
📓 Path-based instructions (3)
**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use <script setup lang="ts"> and the composition API when creating Vue components

Files:

  • test/fixtures/basic/app/pages/index.vue
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • test/fixtures/basic/nuxt.config.ts
  • test/basic.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • test/basic.test.ts
🧠 Learnings (1)
📚 Learning: 2025-07-18T16:46:07.446Z
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Write unit tests for core functionality using `vitest`

Applied to files:

  • test/basic.test.ts
🧬 Code graph analysis (1)
test/basic.test.ts (1)
test/utils.ts (1)
  • renderPage (12-51)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: test-fixtures (windows-latest, built, rspack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, webpack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: release-pkg-pr-new
  • GitHub Check: typecheck (windows-latest, bundler)
  • GitHub Check: test-benchmark
  • GitHub Check: typecheck (ubuntu-latest, bundler)
  • GitHub Check: code

huang-julien and others added 3 commits October 6, 2025 08:32
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
if (rules.redirect) {
if (hasProtocol(rules.redirect, { acceptRelative: true })) {
window.location.href = rules.redirect
const path = rules.redirect.includes('#') ? rules.redirect : (rules.redirect + to.hash)
Copy link
Member

Choose a reason for hiding this comment

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

should we also support redirecting with a query string?

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah... that's a good question...
Anchors whether present or not won't deeply affect the application while query string can trigger unwanted side-effects

@danielroe danielroe merged commit 5589a44 into main Oct 7, 2025
84 of 86 checks passed
@danielroe danielroe deleted the fix/manifestrouterulesredirect branch October 7, 2025 13:20
@github-actions github-actions bot mentioned this pull request Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Docs contain multiple broken anchor links

4 participants