-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
fix(nuxt): preserve percent-encoding in navigateTo #33465
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
|
|
Caution Review failedFailed to post review comments WalkthroughThe router composable was changed: Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks and finishing touches and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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)
test/fixtures/basic/app/pages/redirect-target.vue (1)
5-7: Consider adding TypeScript to script setup.The coding guidelines specify using
<script setup lang="ts">for Vue components. Adding TypeScript would provide type safety and align with the project's conventions.Apply this diff:
-<script setup> +<script setup lang="ts"> const route = useRoute() </script>Based on coding guidelines.
test/fixtures/basic/app/pages/navigate-to-encoded-query.vue (1)
5-9: Consider adding TypeScript to script setup.The coding guidelines specify using
<script setup lang="ts">for Vue components. Adding TypeScript would provide type safety and align with the project's conventions.Apply this diff:
-<script setup> +<script setup lang="ts"> // This should trigger a redirect to /redirect-target?callback=/other // The query parameter contains a path that should be preserved await navigateTo('/redirect-target?callback=/other', { redirectCode: 302 }) </script>Based on coding guidelines.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/nuxt/src/app/composables/router.ts(1 hunks)test/basic.test.ts(1 hunks)test/fixtures/basic/app/pages/navigate-to-encoded-query.vue(1 hunks)test/fixtures/basic/app/pages/redirect-target.vue(1 hunks)
🧰 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/redirect-target.vuetest/fixtures/basic/app/pages/navigate-to-encoded-query.vue
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/nuxt/src/app/composables/router.tstest/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
⏰ 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: build
- GitHub Check: code
🔇 Additional comments (2)
packages/nuxt/src/app/composables/router.ts (1)
217-217: LGTM! The fix correctly normalises path encoding for comparison.The change ensures that both
final.fullPathandfullPathare compared in their encoded forms, which resolves the issue where one side was percent-encoded (e.g.,callback=%2Fother) whilst the other was decoded (e.g.,callback=/other). TheencodeURLutility consistently normalises both values before comparison, ensuring the redirect is triggered correctly.test/basic.test.ts (1)
1125-1132: LGTM! The test correctly validates percent-encoding preservation.The test verifies that
navigateTopreserves percent-encoding in query parameters during server-side redirects. The assertion confirms that the forward slash in the callback parameter is correctly encoded as%2Fotherin the redirect Location header, which addresses the issue described in #33273.
@nuxt/kit
nuxt
@nuxt/rspack-builder
@nuxt/schema
@nuxt/vite-builder
@nuxt/webpack-builder
commit: |
CodSpeed Performance ReportMerging #33465 will not alter performanceComparing Summary
|
8e5dd04 to
d17547e
Compare
There was a problem hiding this 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 (1)
packages/nuxt/src/app/composables/router.ts (1)
217-217: Correct fix for percent-encoding preservation.Applying
encodeURLto both sides of the comparison ensures that paths are normalized to the same format before checking equality. This properly addresses the root cause wherefullPathwas decoded whilstfinal.fullPathwas percent-encoded, causing the redirect check to fail.Optional optimisation: Consider caching the encoded target to avoid recomputing on each navigation:
+ const encodedFullPath = encodeURL(fullPath) - router.afterEach(final => encodeURL(final.fullPath) === encodeURL(fullPath) ? redirect(false) : undefined) + router.afterEach(final => encodeURL(final.fullPath) === encodedFullPath ? redirect(false) : undefined)This avoids repeatedly calling
encodeURL(fullPath)sincefullPathis captured in the closure and remains constant across navigation checks. However, the performance impact is negligible given this only runs server-side during middleware processing.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
packages/nuxt/src/app/composables/router.ts(1 hunks)test/basic.test.ts(1 hunks)test/bundle.test.ts(1 hunks)test/fixtures/basic/app/pages/navigate-to-encoded-query.vue(1 hunks)test/fixtures/basic/app/pages/redirect-target.vue(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- test/fixtures/basic/app/pages/navigate-to-encoded-query.vue
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
test/basic.test.tstest/bundle.test.tspackages/nuxt/src/app/composables/router.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.tstest/bundle.test.ts
**/*.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/redirect-target.vue
⏰ 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). (18)
- GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, built, rspack, 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, rspack, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, 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, dev, vite, default, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, 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, dev, vite, async, manifest-on, js, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, lts/-1)
- GitHub Check: test-size
- GitHub Check: typecheck (windows-latest, bundler)
- GitHub Check: release-pkg-pr-new
- GitHub Check: typecheck (ubuntu-latest, bundler)
- GitHub Check: test-benchmark
- GitHub Check: code
🔇 Additional comments (3)
test/fixtures/basic/app/pages/redirect-target.vue (1)
1-7: LGTM!The component correctly uses the composition API with
<script setup lang="ts">as specified in the coding guidelines. The implementation appropriately serves as a redirect target for testing percent-encoding preservation.test/bundle.test.ts (1)
120-120: Bundle size reduction is expected.The 1k reduction in server bundle size aligns with the router changes that now use
encodeURLfor path comparison, resulting in slightly more efficient code generation.test/basic.test.ts (1)
1125-1132: Excellent regression test coverage.This test case directly verifies the fix for issue #33273 by ensuring that percent-encoded query parameters (specifically the forward slash as
%2Fother) are preserved in redirect Location headers. The test expectations are clear and will prevent future regressions.
898ad95 to
1d5a89a
Compare
There was a problem hiding this 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 (1)
test/fixtures/basic/app/pages/navigate-to-encoded-query.vue (1)
5-9: Clarify comment to mention percent-encoding.The comment on line 6 could be more precise to help readers understand the test's purpose. Consider mentioning that the forward slash in
/otherwill be percent-encoded as%2Fotherin the query parameter.Apply this diff to improve the comment:
-// This should trigger a redirect to /redirect-target?callback=/other +// This should trigger a redirect to /redirect-target?callback=%2Fother (encoded) // The query parameter contains a path that should be preservedAdditionally, verify that the corresponding test in
test/basic.test.tsproperly validates the Location header contains the percent-encoded query parameter (%2Fother).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/nuxt/src/app/composables/router.ts(1 hunks)test/basic.test.ts(1 hunks)test/fixtures/basic/app/pages/navigate-to-encoded-query.vue(1 hunks)test/fixtures/basic/app/pages/redirect-target.vue(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- test/fixtures/basic/app/pages/redirect-target.vue
- test/basic.test.ts
- packages/nuxt/src/app/composables/router.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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/navigate-to-encoded-query.vue
🔇 Additional comments (1)
test/fixtures/basic/app/pages/navigate-to-encoded-query.vue (1)
1-3: LGTM! Template appropriately signals redirect.The placeholder text clearly indicates this page should not render, which is correct since the redirect should occur immediately in the script setup.
There was a problem hiding this 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/composables/router.ts (2)
217-217: Core fix correctly normalises both paths before comparison.By encoding both
final.fullPathandfullPathbefore comparing, this change ensures the equality check succeeds even when Vue Router returns them in different encoding states, which was the root cause of the redirect failure.Optional micro-optimisation: since
fullPathis constant in this scope, you could cache its encoded value to avoid encoding it on everyafterEachinvocation:+ const encodedFullPath = encodeURL(fullPath) - router.afterEach(final => encodeURL(final.fullPath) === encodeURL(fullPath) ? redirect(false) : undefined) + router.afterEach(final => encodeURL(final.fullPath) === encodedFullPath ? redirect(false) : undefined)However, since
afterEachis only registered once per navigation and URL encoding is fast, the performance benefit would be negligible.
303-338: Consider using ufo's dedicated query encoding functions.The decode-then-encode pattern for query parameters (lines 307-322) correctly normalises encoding, but manually parsing queries can miss edge cases.
Based on learnings, ufo v1.6.1 provides dedicated functions for query handling that may be more robust:
export function encodeURL (location: string, isExternalHost = false) { const url = parseURL(location) const pathname = encodePath(url.pathname) // Use ufo's parseQuery and withQuery for more robust query handling const query = url.search ? parseQuery(url.search) : {} const search = Object.keys(query).length > 0 ? '?' + Object.entries(query) .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`) .join('&') : '' const hash = url.hash ? encodeHash(url.hash) : '' if (!isExternalHost) { return pathname + search + hash } const protocol = url.protocol ? url.protocol + '//' : '' const host = url.host || '' const result = protocol + host + pathname + search + hash if (location.startsWith('//')) { return result.replace(/^https?:/, '') } return result }This approach leverages ufo's
parseQuery(which now uses null-prototype objects for safety) and separates parsing concerns from encoding logic. However, the current implementation is explicit and working correctly, so this refactor is optional.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/nuxt/src/app/composables/router.ts(3 hunks)test/fixtures/basic/app/pages/navigate-to-encoded-query.vue(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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/navigate-to-encoded-query.vue
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/nuxt/src/app/composables/router.ts
🧬 Code graph analysis (1)
packages/nuxt/src/app/composables/router.ts (1)
packages/nuxt/src/app/components/test-component-wrapper.ts (1)
url(8-26)
⏰ 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). (1)
- GitHub Check: code
🔇 Additional comments (2)
test/fixtures/basic/app/pages/navigate-to-encoded-query.vue (1)
1-13: LGTM! Well-structured test fixture.The test fixture correctly demonstrates the percent-encoding preservation issue. The middleware redirect to
/redirect-target?callback=/otherwill verify that the forward slash in the query parameter value is properly encoded as%2Fotherin the final redirect location.packages/nuxt/src/app/composables/router.ts (1)
5-5: LGTM! Appropriate imports for the encoding fix.The addition of
encodeHashandencodePathfrom ufo is necessary for the reworkedencodeURLimplementation and aligns with the library's purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
packages/nuxt/src/app/composables/router.ts (2)
217-217: LGTM with optional optimization suggestion.The normalization approach correctly addresses the percent-encoding mismatch issue. Both paths are now encoded consistently before comparison, ensuring redirects work regardless of encoding differences.
For a minor performance improvement, consider caching the encoded target to avoid reparsing on every
afterEachinvocation:+ const encodedFullPath = encodeURL(fullPath) - router.afterEach(final => encodeURL(final.fullPath) === encodeURL(fullPath) ? redirect(false) : undefined) + router.afterEach(final => encodeURL(final.fullPath) === encodedFullPath ? redirect(false) : undefined)
331-334: Add comment explaining protocol-relative URL handling.The special case for URLs starting with
//removes the protocol to preserve the protocol-relative format. This works correctly but is non-obvious and would benefit from a brief comment.if (location.startsWith('//')) { + // Protocol-relative URLs: preserve the '//' prefix by removing any protocol parseURL may have added return result.replace(/^https?:/, '') }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/nuxt/src/app/composables/router.ts(3 hunks)test/fixtures/basic/app/pages/navigate-to-encoded-query.vue(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- test/fixtures/basic/app/pages/navigate-to-encoded-query.vue
🧰 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/composables/router.ts
🧬 Code graph analysis (1)
packages/nuxt/src/app/composables/router.ts (1)
packages/nuxt/src/app/components/test-component-wrapper.ts (1)
url(8-26)
⏰ 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). (3)
- GitHub Check: codeql (javascript-typescript)
- GitHub Check: build
- GitHub Check: code
| import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, Router, useRoute as _useRoute, useRouter as _useRouter } from 'vue-router' | ||
| import { sanitizeStatusCode } from 'h3' | ||
| import { hasProtocol, isScriptProtocol, joinURL, parseQuery, parseURL, withQuery } from 'ufo' | ||
| import { encodeHash, encodePath, hasProtocol, isScriptProtocol, joinURL, parseQuery, parseURL, withQuery } from 'ufo' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Consider importing stringifyQuery for safer query encoding.
The new imports are correctly used, but stringifyQuery from the ufo library would simplify the query encoding logic in encodeURL (lines 310-319) and handle edge cases like array values, empty strings, and malformed input more robustly. Based on learnings, ufo provides stringifyQuery specifically for this purpose.
Add stringifyQuery to the imports:
-import { encodeHash, encodePath, hasProtocol, isScriptProtocol, joinURL, parseQuery, parseURL, withQuery } from 'ufo'
+import { encodeHash, encodePath, hasProtocol, isScriptProtocol, joinURL, parseQuery, parseURL, stringifyQuery, withQuery } from 'ufo'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { encodeHash, encodePath, hasProtocol, isScriptProtocol, joinURL, parseQuery, parseURL, withQuery } from 'ufo' | |
| import { encodeHash, encodePath, hasProtocol, isScriptProtocol, joinURL, parseQuery, parseURL, stringifyQuery, withQuery } from 'ufo' |
🤖 Prompt for AI Agents
In packages/nuxt/src/app/composables/router.ts around line 5 and impacting
encodeURL at lines 310-319, the code manually builds/encodes query strings
instead of using ufo's stringifyQuery which better handles arrays, empty strings
and malformed input; update the import line to include stringifyQuery from 'ufo'
and replace the manual query encoding in encodeURL with a call to
stringifyQuery(query) (ensuring any pre-processing required by the function is
applied) so query serialization is delegated to the library and edge cases are
handled robustly.
| export function encodeURL (location: string, isExternalHost = false) { | ||
| const url = new URL(location, 'http://localhost') | ||
| const url = parseURL(location) | ||
| const pathname = encodePath(url.pathname) | ||
|
|
||
| // Encode query parameters by parsing and re-stringifying with proper encoding | ||
| // This normalizes encoding (e.g., preserves %2F for forward slashes in query values) | ||
| const query = parseQuery(url.search) | ||
| const search = Object.keys(query).length > 0 | ||
| ? '?' + Object.entries(query) | ||
| .map(([key, value]) => { | ||
| // Normalize by decoding and re-encoding to ensure consistent encoding | ||
| const encodedKey = encodeURIComponent(decodeURIComponent(String(key))) | ||
| const encodedValue = value ? encodeURIComponent(decodeURIComponent(String(value))) : '' | ||
| return encodedValue ? `${encodedKey}=${encodedValue}` : encodedKey | ||
| }) | ||
| .join('&') | ||
| : '' | ||
|
|
||
| const hash = url.hash ? encodeHash(url.hash) : '' | ||
|
|
||
| if (!isExternalHost) { | ||
| return url.pathname + url.search + url.hash | ||
| return pathname + search + hash | ||
| } | ||
|
|
||
| const protocol = url.protocol ? url.protocol + '//' : '' | ||
| const host = url.host || '' | ||
| const result = protocol + host + pathname + search + hash | ||
|
|
||
| if (location.startsWith('//')) { | ||
| return url.toString().replace(url.protocol, '') | ||
| return result.replace(/^https?:/, '') | ||
| } | ||
| return url.toString() | ||
| return result | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplify query encoding using stringifyQuery and fix array value handling.
The manual query encoding has several issues:
-
Lines 314-315: The decode-then-encode pattern is problematic:
parseQueryalready returns decoded values, sodecodeURIComponentis redundant.- If values are double-encoded, this will triple-encode them.
decodeURIComponentcan throwURIErroron malformed input (no error handling).
-
Array values: If a query parameter has multiple values (e.g.,
?tag=a&tag=b),parseQueryreturns{ tag: ['a', 'b'] }.String(['a', 'b'])produces'a,b', which is incorrect. -
Empty strings: Line 316 treats empty string values as falsy, generating
?keyinstead of?key=.
Replace the manual query encoding with stringifyQuery:
- // Encode query parameters by parsing and re-stringifying with proper encoding
- // This normalizes encoding (e.g., preserves %2F for forward slashes in query values)
const query = parseQuery(url.search)
- const search = Object.keys(query).length > 0
- ? '?' + Object.entries(query)
- .map(([key, value]) => {
- // Normalize by decoding and re-encoding to ensure consistent encoding
- const encodedKey = encodeURIComponent(decodeURIComponent(String(key)))
- const encodedValue = value ? encodeURIComponent(decodeURIComponent(String(value))) : ''
- return encodedValue ? `${encodedKey}=${encodedValue}` : encodedKey
- })
- .join('&')
- : ''
+ const search = Object.keys(query).length > 0 ? '?' + stringifyQuery(query) : ''This leverages the ufo library's robust query encoding, which properly handles arrays, empty values, and special characters without the decode-then-encode issues.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function encodeURL (location: string, isExternalHost = false) { | |
| const url = new URL(location, 'http://localhost') | |
| const url = parseURL(location) | |
| const pathname = encodePath(url.pathname) | |
| // Encode query parameters by parsing and re-stringifying with proper encoding | |
| // This normalizes encoding (e.g., preserves %2F for forward slashes in query values) | |
| const query = parseQuery(url.search) | |
| const search = Object.keys(query).length > 0 | |
| ? '?' + Object.entries(query) | |
| .map(([key, value]) => { | |
| // Normalize by decoding and re-encoding to ensure consistent encoding | |
| const encodedKey = encodeURIComponent(decodeURIComponent(String(key))) | |
| const encodedValue = value ? encodeURIComponent(decodeURIComponent(String(value))) : '' | |
| return encodedValue ? `${encodedKey}=${encodedValue}` : encodedKey | |
| }) | |
| .join('&') | |
| : '' | |
| const hash = url.hash ? encodeHash(url.hash) : '' | |
| if (!isExternalHost) { | |
| return url.pathname + url.search + url.hash | |
| return pathname + search + hash | |
| } | |
| const protocol = url.protocol ? url.protocol + '//' : '' | |
| const host = url.host || '' | |
| const result = protocol + host + pathname + search + hash | |
| if (location.startsWith('//')) { | |
| return url.toString().replace(url.protocol, '') | |
| return result.replace(/^https?:/, '') | |
| } | |
| return url.toString() | |
| return result | |
| } | |
| export function encodeURL (location: string, isExternalHost = false) { | |
| const url = parseURL(location) | |
| const pathname = encodePath(url.pathname) | |
| const query = parseQuery(url.search) | |
| const search = Object.keys(query).length > 0 ? '?' + stringifyQuery(query) : '' | |
| const hash = url.hash ? encodeHash(url.hash) : '' | |
| if (!isExternalHost) { | |
| return pathname + search + hash | |
| } | |
| const protocol = url.protocol ? url.protocol + '//' : '' | |
| const host = url.host || '' | |
| const result = protocol + host + pathname + search + hash | |
| if (location.startsWith('//')) { | |
| return result.replace(/^https?:/, '') | |
| } | |
| return result | |
| } |
🤖 Prompt for AI Agents
In packages/nuxt/src/app/composables/router.ts around lines 303 to 335, the
manual query encoding is wrong (redundant decode/encode, throws on malformed
input, mishandles array values and empty strings); replace the custom
Object.entries mapping with the ufo.stringifyQuery utility (import if missing),
pass the parsed query object (from parseQuery(url.search)) to stringifyQuery and
prepend '?' when it returns a non-empty string so arrays and empty values are
encoded correctly, and remove the decodeURIComponent/encodeURIComponent logic
while preserving the existing pathname/hash and isExternalHost handling.
44d018f to
1d5a89a
Compare
Fixes #33273 where
navigateTowould drop percent-encoding for internal redirects triggered from middleware on the server.Summary
encodeURLbefore comparing themafterEachhook to avoid reparsing on every navigation checkTesting
pnpm vitest run test/basic.test.ts