-
-
Notifications
You must be signed in to change notification settings - Fork 107
Add experimental custom resolver #700
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
WalkthroughAdds experimental param-parser support, a generated resolver virtual module with HMR, param-parser-aware codegen and DTS injection, extended tree/param model and resolver generation, many tests, utility exports, and a new playground-experimental demo package with typed routes. Changes
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related issues
Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (2)📚 Learning: 2025-08-23T12:54:44.918Z
Applied to files:
📚 Learning: 2025-08-16T13:01:42.709Z
Applied to files:
🧬 Code graph analysis (1)src/runtime.ts (1)
⏰ 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)
🔇 Additional comments (4)
Comment |
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
commit: |
Deploying unplugin-vue-router with
|
Latest commit: |
8b22754
|
Status: | ✅ Deploy successful! |
Preview URL: | https://fd864f64.unplugin-vue-router.pages.dev |
Branch Preview URL: | https://feat-experimental-router.unplugin-vue-router.pages.dev |
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: 9
🔭 Outside diff range comments (1)
tsconfig.json (1)
2-7
: Fix invalid include pattern (trailing comma inside the string).This likely prevents test files from being included by TS.
"include": [ "./src/**/*.ts", "./src/*.d.ts", "./client.d.ts", - "./tests/**/*.ts," + "./tests/**/*.ts" ],
🧹 Nitpick comments (41)
playground-experimental/.gitignore (1)
1-1
: Prefer wildcard ignore for TS build info artifactsCovers multiple tsconfigs (e.g., tsconfig.config.tsbuildinfo) without future updates.
-tsconfig.tsbuildinfo +*.tsbuildinfoplayground-experimental/src/pages/[name]/24.vue (1)
5-5
: Fix typo in copy"Too test" → "To test".
- <p>Too test specificity in routes</p> + <p>To test specificity in routes</p>playground-experimental/src/pages/b.vue (1)
3-5
: Align heading with route/page nameLabeling this page “Home” can be confusing next to the actual home page. Consider a clearer title.
- <h1>Home</h1> + <h1>Page B</h1>package.json (1)
206-206
: Verify CI/install stability for private vue-router artifactUsing a PR artifact URL for
vue-router
in devDependencies is fine locally, but can break CI or future installs if the artifact is GC’d or the service is down. Ensure CI runners have access and consider documenting a fallback or pinning a public prerelease if available.I can add a short README note or a CI guard that fails with a clear message if the artifact can’t be fetched. Want me to draft it?
playground-experimental/src/pages/a.[b].c.[d].vue (1)
1-5
: Prefer useRoute over $route in templates (optional).Leverage typed access and avoid relying on the global in templates.
-<script lang="ts" setup></script> +<script lang="ts" setup> +import { useRoute } from 'vue-router' +const route = useRoute() +</script> <template> - <h1>{{ String($route.name) }} - {{ $route.path }}</h1> + <h1>{{ String(route.name) }} - {{ route.path }}</h1> </template>src/data-loaders/meta-extensions.ts (1)
13-45
: Type-only import OK; consider inlining the type in the augmentation (optional).This keeps the augmentation self-contained and avoids top-level imports if verbatim module syntax is enabled later.
-import { type RouteLocationNormalizedLoaded } from 'vue-router' +// (optional) Inline the type import within the augmentation below to avoid a top-level import. declare module 'vue-router' { + // Inline type import to avoid a top-level import in augmentations + type RouteLocationNormalizedLoaded = import('vue-router').RouteLocationNormalizedLoaded export interface Router { /** * Pending navigation that is waiting for data loaders to resolve. * @internal */ [PENDING_LOCATION_KEY]: RouteLocationNormalizedLoaded | nullplayground-experimental/src/pages/users/sub-[first]-[second].vue (1)
1-5
: Mirror the optional useRoute pattern for consistency.Keeps access typed and aligned with other pages.
-<script lang="ts" setup></script> +<script lang="ts" setup> +import { useRoute } from 'vue-router' +const route = useRoute() +</script> <template> - <h1>{{ String($route.name) }} - {{ $route.path }}</h1> + <h1>{{ String(route.name) }} - {{ route.path }}</h1> </template>tsconfig.json (1)
17-18
: Comment and config mismatch.The comment suggests enabling baseUrl, but it’s disabled. Either remove the comment or restore baseUrl if you still want canonical imports.
playground/src/params/date.ts (3)
1-3
: Consolidate invalid() helper (avoid duplication).You already noted this should come from vue-router. Either import it or move it to a shared local util so number/date stay in sync.
Option A: import from vue-router (when available):
-// NOTE: should be imported from vue-router -const invalid = (...args: ConstructorParameters<typeof Error>) => - new Error(...args) +import { invalid } from 'vue-router'Option B: shared local util (apply similar change in number.ts):
Create playground/src/params/_utils.ts:export const invalid = (...args: ConstructorParameters<typeof Error>) => new Error(...args)Then:
-// NOTE: should be imported from vue-router -const invalid = (...args: ConstructorParameters<typeof Error>) => - new Error(...args) +import { invalid } from './_utils'
5-12
: Confirm accepted formats (date-only vs any parseable Date).new Date(value) accepts many formats; if you only want YYYY-MM-DD, add a guard. Otherwise, current behavior is fine.
Example tightening:
export const parse = (value: string): Date => { + // Accept only YYYY-MM-DD (ISO date without time) + if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) { + throw invalid(`Invalid date (expected YYYY-MM-DD): "${value}"`) + } const asDate = new Date(value) if (Number.isNaN(asDate.getTime())) { throw invalid(`Invalid date: "${value}"`) } return asDate }
14-18
: toString(): prefer explicit date-only trimming..replace('T00:00:00.000Z', '') only handles UTC midnight. If you want stable date-only output for midnight values, make the intent explicit.
-export const toString = (value: Date): string => - value - .toISOString() - // allows keeping simple dates like 2023-10-01 without time - .replace('T00:00:00.000Z', '') +export const toString = (value: Date): string => { + const iso = value.toISOString() + // keep simple dates like 2023-10-01 without time + return iso.endsWith('T00:00:00.000Z') ? iso.slice(0, 10) : iso +}src/runtime.ts (2)
73-74
: Make QueryParamType extensible via registry pattern.Hard-coding 'int' | 'bool' will drift from available parsers. Suggest a registry type users/codegen can augment.
-export type QueryParamType = 'int' | 'bool' +// Open registry for param types. Augment in userland / codegen. +export interface QueryParamTypesRegistry { + int: unknown + bool: unknown +} +export type QueryParamType = keyof QueryParamTypesRegistry
78-84
: Fix broken doc link.The TODO link points to https://uvr.esm.is/TODO. Replace with the actual params folder docs URL.
playground-experimental/src/pages/users/[userId=int].vue (1)
2-3
: Remove unused expression or make intent explicit.route.params.userId on its own can trip linters. Either use it or mark it intentionally unused.
const route = useRoute() -route.params.userId +// Access to assert types without emitting code +void route.params.userIdplayground/src/params/number.ts (1)
1-4
: Deduplicate invalid() helper (same as date.ts).Centralize invalid() to a shared util to avoid divergence.
Option: shared util (apply similar change in date.ts):
Create playground/src/params/_utils.ts:export const invalid = (...args: ConstructorParameters<typeof Error>) => new Error(...args)Then:
-// NOTE: should be imported from vue-router -const invalid = (...args: ConstructorParameters<typeof Error>) => - new Error(...args) +import { invalid } from './_utils'src/core/tree.ts (1)
282-297
: Inefficient regexp constructionThe regexp getter walks up the tree on every call without caching. For deeply nested routes, this could impact performance.
Consider caching the regexp value or building it incrementally as nodes are added to the tree:
+ private _regexp: string | null = null + get regexp(): string { + if (this._regexp !== null) { + return this._regexp + } + let re = '' let node: TreeNode | undefined = this while (node) { if (node.value.isParam() && node.value.re) { re = node.value.re + (re ? '\\/' : '') + re } else { re = node.value.pathSegment + (re ? '\\/' : '') + re } node = node.parent } - return '/^' + re + '$/i' + this._regexp = '/^' + re + '$/i' + return this._regexp }Note: You'll need to invalidate the cache when the tree structure changes.
playground-experimental/vite.config.ts (2)
5-6
: Remove @ts-ignore commentThe @ts-ignore comment suppresses type checking. Consider properly typing the import or using a more specific type assertion.
-// @ts-ignore: the plugin should not be checked in the playground -import VueRouter from '../src/vite' +import VueRouter from '../src/vite' as anyOr better yet, ensure the module has proper type exports.
107-116
: Inconsistent exclude patternsThe exclude patterns mix different formats (glob patterns with
**
and simple patterns). Line 113 has a commented absolute path resolution that differs from the string patterns.Consider standardizing the exclude patterns format for consistency:
exclude: [ - '**/ignored/**', - // '**/ignored/**/*', - '**/__*', - '**/__**/*', + '**/ignored/**/*', + '**/__*/**', '**/*.component.vue', - // resolve(__dirname, './src/pages/ignored'), - // - // './src/pages/**/*.spec.ts', ],playground-experimental/src/pages/[name].vue (1)
1-7
: Make params readable and align with useRoute for consistencyInterpolating an object prints “[object Object]”. Also, other pages use
useRoute()
. Suggest switching touseRoute()
and pretty-printing params.Apply:
-<script lang="ts" setup></script> +<script lang="ts" setup> +const route = useRoute() +</script> - <pre>{{ $route.params }}</pre> + <pre>{{ JSON.stringify(route.params, null, 2) }}</pre>If you prefer to keep
$route
, minimally do:- <pre>{{ $route.params }}</pre> + <pre>{{ JSON.stringify($route.params, null, 2) }}</pre>playground-experimental/src/pages/(home).vue (1)
1-2
: Remove empty script blockThe empty
<script setup>
adds noise. Either remove it or use it.-<script lang="ts" setup></script> -playground-experimental/index.html (2)
8-13
: Add rel="noopener noreferrer" to external link opened in new tabPrevents tabnabbing and avoids exposing
window.opener
.- target="_blank" + target="_blank" + rel="noopener noreferrer"
3-6
: Add a document titleImproves UX and browser tab labeling.
<head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Vue Router Playground (experimental)</title> </head>
playground-experimental/src/pages/events/[when=date].vue (1)
7-7
: Show a stable, useful representation of the Date
typeof date
is always “object” andtoString()
is environment-dependent. Preferinstanceof Date
andtoISOString()
for clarity.- <pre>{{ typeof route.params.when }} - {{ route.params.when }}</pre> + <pre> + {{ + route.params.when instanceof Date ? 'date' : typeof route.params.when + }} - {{ + route.params.when instanceof Date + ? route.params.when.toISOString() + : String(route.params.when) + }} + </pre>playground-experimental/src/pages/[name]/[userId=int].vue (2)
1-1
: Remove empty script block.No script logic is present. Dropping the empty block keeps the SFC lean.
-<script lang="ts" setup></script> +
3-7
: Clarify intent: route renders content despite “should never be visible”.If this file is meant to be a layout-only segment, the
will render when navigating to /[name]/[userId=int]. Consider removing the heading or redirecting/adding an index child to ensure only children render.
src/codegen/generateRouteMap.spec.ts (1)
22-24
: Deduplicate the repeated empty paramParsersMap argument.Every test passes
new Map()
togenerateRouteNamedMap
. Introduce a single constant or helper to reduce noise and centralize future changes.Apply this at the top of the file (outside the shown ranges):
const EMPTY_PARAM_PARSERS = new Map() const gen = (tree: PrefixTree) => formatExports(generateRouteNamedMap(tree, DEFAULT_OPTIONS, EMPTY_PARAM_PARSERS))Then within the changed lines, replace calls like:
-formatExports(generateRouteNamedMap(tree, DEFAULT_OPTIONS, new Map())) +gen(tree)Additionally, consider adding one test asserting behavior with a non-empty
paramParsersMap
to exercise the new API path.Also applies to: 45-47, 68-70, 84-86, 104-106, 128-130, 149-151, 171-173, 189-191, 209-211, 226-228, 240-242, 257-259, 281-283, 297-299, 311-313, 325-327, 345-347, 379-381, 400-402
playground-experimental/src/params/date.ts (2)
3-5
: Throw a TypeError directly; drop the local invalid wrapper.A direct
TypeError
is clearer and avoids an unnecessary helper.-// NOTE: should be imported from vue-router -const invalid = (...args: ConstructorParameters<typeof Error>) => - new Error(...args) +// NOTE: could be replaced by a shared `invalid()` helper from vue-router when available @@ - if (Number.isNaN(asDate.getTime())) { - throw invalid(`Invalid date: "${value}"`) - } + if (Number.isNaN(asDate.getTime())) { + throw new TypeError(`Invalid date: "${value}"`) + }Also applies to: 10-12
16-21
: Anchor the time-stripping replace.Use a regex anchored to the end to avoid accidental mid-string replacements.
- set: (value: Date): string => - value - .toISOString() - // allows keeping simple dates like 2023-10-01 without time - .replace('T00:00:00.000Z', ''), + set: (value: Date): string => + value + .toISOString() + // allows keeping simple dates like 2023-10-01 without time + .replace(/T00:00:00\.000Z$/, ''),src/codegen/generateRouteRecords.ts (1)
148-152
: Add explicit return type to the newly exported API.Public functions benefit from explicit return types to prevent unintended API drift.
-export function generatePageImport( +export function generatePageImport( filepath: string, importMode: ResolvedOptions['importMode'], importsMap: ImportsMap -) { +): string {src/index.ts (1)
122-123
: Generate resolver on load; consider HMR invalidation too.The load branch for
auto-resolver
is correct. For DX, also invalidate the resolver module during HMR when pages change (mirroring auto-routes).You can augment handleHotUpdate to include the resolver module if present:
const resolverModule = server.moduleGraph.getModuleById( asVirtualId(MODULE_RESOLVER_PATH) ) if (resolverModule) { return [ ...modules, definePageModule, routesModule, resolverModule, ] }Also applies to: 146-151
src/codegen/generateRouteParams.ts (1)
36-37
: Simplify redundant fallback expression.The expression
type || '/* INVALID */ unknown'
is redundant sincetype
is already checked for truthiness.- type - ? type || '/* INVALID */ unknown' + type + ? typesrc/options.ts (1)
342-347
: Consider extracting common normalization logic.The pattern for normalizing and resolving paths is duplicated between
paramMatchersDir
andautoExportsDataLoaders
. Consider extracting a helper function.+function normalizeAndResolvePaths( + value: string | string[] | undefined, + root: string +): string[] | undefined { + if (!value) return undefined + return (isArray(value) ? value : [value]).map((path) => resolve(root, path)) +} const paramMatchersDir = ( paramMatchers?.dir ? isArray(paramMatchers.dir) ? paramMatchers.dir : [paramMatchers.dir] : [] ).map((dir) => resolve(root, dir)) -const autoExportsDataLoaders = options.experimental?.autoExportsDataLoaders - ? (isArray(options.experimental.autoExportsDataLoaders) - ? options.experimental.autoExportsDataLoaders - : [options.experimental.autoExportsDataLoaders] - ).map((path) => resolve(root, path)) - : undefined +const autoExportsDataLoaders = normalizeAndResolvePaths( + options.experimental?.autoExportsDataLoaders, + root +)playground-experimental/src/utils.ts (1)
35-40
: Consider removing unused utilities.The
dummy
functions and constants appear to be test utilities that should be removed or moved to test files.src/codegen/generateRouteMap.ts (1)
37-55
: Param-matcher types applied to both raw and decoded params — confirm intent.
When paramMatchers are enabled, paramType is used for both the “raw” and “decoded” type slots, losing the ParamValue/ distinction. If that’s intentional for the experiment, fine; otherwise consider generating distinct raw/decoded variants.src/codegen/generateParamParsers.ts (3)
17-32
: Drop unnecessary async from warnMissingParamParsers.
The function is synchronous; async adds noise and leads to un-awaited calls downstream.Apply this diff:
-export async function warnMissingParamParsers( +export function warnMissingParamParsers( tree: PrefixTree, paramParsers: ParamParsersMap ) {
68-82
: Fix param name typo for clarity (parparsersMap → paramParsersMap).
Pure readability; avoids confusion across the codebase.Apply this diff:
-export function generateParamsTypes( - params: TreeRouteParam[], - parparsersMap: ParamParsersMap -): Array<string | null> { +export function generateParamsTypes( + params: TreeRouteParam[], + paramParsersMap: ParamParsersMap +): Array<string | null> { return params.map((param) => { if (param.parser) { - if (parparsersMap.has(param.parser)) { - return parparsersMap.get(param.parser)!.typeName + if (paramParsersMap.has(param.parser)) { + return paramParsersMap.get(param.parser)!.typeName } else if (param.parser === 'int') { return 'number' } } return null }) }
102-109
: Improve error message in generateParamsOptions.
The current message is cryptic.Apply this diff:
- if (!paramParsers) { - throw new Error('what') - } + if (!paramParsers) { + throw new Error('paramParsers map is required to generate params options') + }src/core/context.ts (2)
211-229
: Normalize relativePath to avoid '././' in generated imports.
setupParamParserWatcher prefixes './' but generateParamParsersTypesDeclarations also adds './', yielding '././path'. It works but is sloppy.Apply this diff:
- relativePath: './' + relative(options.root, absolutePath), + relativePath: relative(options.root, absolutePath),
332-339
: warnMissingParamParsers is called without awaiting — align with its sync/async nature.
If you keep warnMissingParamParsers async, await it here; otherwise (preferred) make it sync per earlier suggestion.src/codegen/generateRouteResolver.ts (1)
35-40
: Avoid importing unused MatcherPatternPathStar.
It’s added unconditionally but not used, which may trigger noUnusedLocals or add dead imports to output.Apply this diff:
importsMap.add('vue-router/experimental', 'createStaticResolver') importsMap.add('vue-router/experimental', 'MatcherPatternPathStatic') importsMap.add('vue-router/experimental', 'MatcherPatternPathCustomParams') - importsMap.add('vue-router/experimental', 'MatcherPatternPathStar') importsMap.add('vue-router/experimental', 'normalizeRouteRecord')
playground-experimental/typed-router.d.ts (1)
3-3
: Remove @ts-nocheck from a .d.ts file (prefer catching generator mismatches).Suppressing diagnostics on a declaration file can hide structural/type drift from generators. If feasible, emit without this directive.
-// @ts-nocheck
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (51)
client.d.ts
(1 hunks)package.json
(1 hunks)playground-experimental/.gitignore
(1 hunks)playground-experimental/auto-imports.d.ts
(1 hunks)playground-experimental/db.json
(1 hunks)playground-experimental/env.d.ts
(1 hunks)playground-experimental/index.html
(1 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/App.vue
(1 hunks)playground-experimental/src/main.ts
(1 hunks)playground-experimental/src/pages/(home).vue
(1 hunks)playground-experimental/src/pages/[name].vue
(1 hunks)playground-experimental/src/pages/[name]/24.vue
(1 hunks)playground-experimental/src/pages/[name]/[userId=int].vue
(1 hunks)playground-experimental/src/pages/a.[b].c.[d].vue
(1 hunks)playground-experimental/src/pages/b.vue
(1 hunks)playground-experimental/src/pages/events/[when=date].vue
(1 hunks)playground-experimental/src/pages/users/[userId=int].vue
(1 hunks)playground-experimental/src/pages/users/sub-[first]-[second].vue
(1 hunks)playground-experimental/src/params/date.ts
(1 hunks)playground-experimental/src/router.ts
(1 hunks)playground-experimental/src/utils.ts
(1 hunks)playground-experimental/tsconfig.config.json
(1 hunks)playground-experimental/tsconfig.json
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)playground-experimental/vite.config.ts
(1 hunks)playground/src/pages/(some-layout).vue
(1 hunks)playground/src/params/date.ts
(1 hunks)playground/src/params/number.ts
(1 hunks)playground/vite.config.ts
(1 hunks)pnpm-workspace.yaml
(1 hunks)src/codegen/generateDTS.ts
(2 hunks)src/codegen/generateParamParsers.ts
(1 hunks)src/codegen/generateRouteMap.spec.ts
(20 hunks)src/codegen/generateRouteMap.ts
(1 hunks)src/codegen/generateRouteParams.ts
(1 hunks)src/codegen/generateRouteRecords.ts
(1 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/context.ts
(10 hunks)src/core/extendRoutes.spec.ts
(9 hunks)src/core/moduleConstants.ts
(2 hunks)src/core/tree.spec.ts
(2 hunks)src/core/tree.ts
(1 hunks)src/core/treeNodeValue.ts
(10 hunks)src/data-loaders/meta-extensions.ts
(1 hunks)src/index.ts
(5 hunks)src/options.ts
(3 hunks)src/runtime.ts
(1 hunks)src/utils/index.ts
(2 hunks)tsconfig.json
(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (17)
playground/src/params/date.ts (1)
playground/src/params/number.ts (2)
parse
(5-11)toString
(14-14)
playground/src/params/number.ts (2)
playground/src/params/date.ts (2)
parse
(5-12)toString
(14-18)src/core/treeNodeValue.ts (2)
toString
(91-93)toString
(285-298)
src/codegen/generateRouteMap.spec.ts (3)
src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/options.ts (1)
DEFAULT_OPTIONS
(249-266)src/index.ts (1)
DEFAULT_OPTIONS
(29-29)
playground-experimental/src/utils.ts (2)
playground-experimental/typed-router.d.ts (1)
RouteNamedMap
(23-33)playground-experimental/src/router.ts (2)
RouteNamedMap
(16-31)router
(9-12)
src/codegen/generateRouteResolver.spec.ts (7)
src/options.ts (2)
DEFAULT_OPTIONS
(249-266)resolveOptions
(313-395)src/index.ts (1)
DEFAULT_OPTIONS
(29-29)src/codegen/generateRouteResolver.ts (2)
generateRouteRecord
(56-122)generateRouteResolver
(17-54)src/codegen/generateRouteRecords.ts (1)
generateRouteRecord
(15-113)src/core/tree.ts (1)
PrefixTree
(368-402)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateParamParsers.ts (1)
ParamParsersMap
(7-15)
src/codegen/generateParamParsers.ts (4)
src/core/tree.ts (1)
PrefixTree
(368-402)src/core/treeNodeValue.ts (1)
TreeRouteParam
(226-233)src/core/utils.ts (1)
ImportsMap
(299-368)src/utils/index.ts (1)
ts
(18-18)
src/codegen/generateRouteMap.ts (4)
src/core/tree.ts (2)
TreeNode
(15-362)params
(268-280)src/options.ts (1)
ResolvedOptions
(400-400)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsTypes
(68-82)src/codegen/generateRouteParams.ts (2)
EXPERIMENTAL_generateRouteParams
(24-50)generateRouteParams
(3-22)
src/core/tree.spec.ts (2)
src/core/tree.ts (1)
PrefixTree
(368-402)src/core/treeNodeValue.ts (1)
TreeRouteParam
(226-233)
playground-experimental/src/main.ts (2)
client.d.ts (1)
DataLoaderPlugin
(52-52)playground-experimental/src/router.ts (1)
router
(9-12)
src/codegen/generateRouteResolver.ts (7)
src/core/tree.ts (3)
PrefixTree
(368-402)path
(217-222)TreeNode
(15-362)src/options.ts (1)
ResolvedOptions
(400-400)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsOptions
(102-120)src/codegen/generateRouteRecords.ts (2)
generateRouteRecord
(15-113)generatePageImport
(148-169)src/utils/index.ts (1)
ts
(18-18)src/core/treeNodeValue.ts (1)
path
(74-76)
src/core/tree.ts (2)
src/core/treeNodeValue.ts (2)
re
(269-283)score
(251-267)src/core/extendRoutes.ts (1)
params
(175-177)
src/index.ts (1)
src/core/moduleConstants.ts (6)
MODULE_RESOLVER_PATH
(7-7)routeBlockQueryRE
(41-41)MODULE_ROUTES_PATH
(6-6)MODULE_VUE_ROUTER_AUTO
(4-4)VIRTUAL_PREFIX
(26-26)ROUTES_LAST_LOAD_TIME
(14-21)
src/options.ts (1)
src/core/utils.ts (1)
isArray
(65-66)
playground-experimental/vite.config.ts (1)
src/index.ts (1)
VueRouterAutoImports
(240-253)
src/core/treeNodeValue.ts (2)
src/core/tree.ts (1)
params
(268-280)src/core/extendRoutes.ts (1)
params
(175-177)
playground-experimental/typed-router.d.ts (2)
playground-experimental/src/router.ts (1)
RouteNamedMap
(16-31)src/codegen/generateRouteParams.ts (1)
ParamValue
(83-85)
src/core/context.ts (11)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(7-15)warnMissingParamParsers
(17-32)generateParamParsersTypesDeclarations
(57-66)src/core/tree.ts (1)
name
(238-245)src/core/extendRoutes.ts (2)
name
(88-90)name
(95-97)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(17-54)src/utils/index.ts (1)
ts
(18-18)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/core/moduleConstants.ts (1)
MODULE_ROUTES_PATH
(6-6)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
🔇 Additional comments (53)
playground/vite.config.ts (1)
51-54
: Explicitly disabling param matchers in the stable playground looks goodKeeps experimental behavior out of the non-experimental playground.
pnpm-workspace.yaml (1)
3-3
: Verified: playground-experimental is marked privateConfirmed that
playground-experimental/package.json
contains"private": true
, so it’s safe from accidental publishing.playground/src/pages/(some-layout).vue (1)
2-4
: Usingname: false
to opt out of a route name is fineClearer than an empty string and supported by the page metadata.
playground-experimental/db.json (1)
1-24
: LGTM: simple fixture dataStructure is valid and works for json-server usage.
client.d.ts (1)
7-7
: Readonlyroutes
signature aligns with Vue Router v4.5.x
Vue Router v4.5.x declaresRouterOptions['routes']
asreadonly RouteRecordRaw[]
(orReadonly<RouteRecordRaw[]>
), so exportingexport const routes: readonly RouteRecordRaw[]is fully compatible with
createRouter
.Optionally, for consistency you can also mark the hot-update callback parameter as readonly:
export function handleHotUpdate( router: Router, hotUpdateCallback?: (newRoutes: readonly RouteRecordRaw[]) => void ): voidsrc/data-loaders/meta-extensions.ts (1)
44-44
: Confirm the correct route type for “pending” navigation.If this is truly pending (before components are resolved),
RouteLocationNormalized
might be more accurate thanRouteLocationNormalizedLoaded
. If loaders run after resolution, current type is fine.playground-experimental/env.d.ts (1)
1-2
: LGTM.Ambient refs for Vite and unplugin-vue-router client types are appropriate for the playground.
tsconfig.json (1)
43-45
: Paths look good; verify virtual module coverage.If the new virtual module
vue-router/auto-resolver
is imported anywhere for types, consider adding a paths alias so TS can resolve it.src/runtime.ts (2)
61-65
: LGTM: definePage gains query field.Nice additive type; matches playground usage.
61-65
: Confirm codegen/runtime alignmentNo
definePage
calls withquery.type
were found in the repo scan. Please manually verify that:
- Your codegen templates and runtime consumers pick up the updated
query
typing fromQueryParamType
.- The generated parsers for
int
/bool
(and any other generator logic) stay in sync withQueryParamType
.playground-experimental/src/pages/users/[userId=int].vue (1)
5-15
: LGTM: query param typing example.Good showcase of optional int query param via definePage.
playground/src/params/number.ts (1)
5-11
: LGTM: finite check covers NaN/±Infinity.Behavior aligns with expectations for numeric params.
playground-experimental/package.json (1)
18-22
: Confirm availability of private vue-router URL in all environments."https://pkg.pr.new/vue-router@2d79fe7" is ephemeral. Ensure CI/dev can resolve it or document a fallback/pin for local work.
src/core/tree.ts (4)
299-309
: LGTM!The score calculation correctly walks up the tree to find the minimum score, properly excluding the root node.
311-320
: LGTM!The matcherParams getter correctly aggregates parameters from the node's param list.
322-332
: LGTM!The matcherParts implementation correctly builds the path representation with parameter placeholders.
334-338
: LGTM!The isMatchable predicate correctly identifies nodes with components and valid names.
src/core/treeNodeValue.ts (2)
516-541
: LGTM!The parameter parser state machine correctly handles the
[param=type]
syntax with proper state transitions.
285-298
: LGTM!The toString implementation with the 𝑥 notation provides clear parameter visualization including modifiers and parsers.
src/core/extendRoutes.spec.ts (1)
72-81
: Test assertion change from toEqual to toMatchObjectThe change from
toEqual
totoMatchObject
makes the test less strict. This could hide unintended properties being added to params.Are there additional properties on params that necessitate using
toMatchObject
instead oftoEqual
? If not, consider keeping the stricter assertion.src/core/tree.spec.ts (5)
46-62
: LGTM!The test correctly validates custom parameter type parsing with the
[id=int]
syntax.
64-82
: LGTM!The test properly verifies repeatable custom param type behavior.
84-102
: LGTM!The test correctly validates optional custom param type parsing.
104-123
: LGTM!The test properly verifies the combination of repeatable and optional modifiers.
125-141
: LGTM!The test correctly validates custom param types within compound segments.
playground-experimental/tsconfig.config.json (1)
1-8
: LGTMConfig-only TS project looks correct for Vite/vitest/cypress configs. The narrowed include and Node types are appropriate.
src/index.ts (3)
13-14
: Exported resolver path import looks good.
70-71
: Resolver virtual id wiring is correct.Including
MODULE_RESOLVER_PATH
in resolve and handling it alongside routes/auto is sound.Also applies to: 79-82
93-95
: Async buildStart is fine.
await ctx.scanPages(options.watch)
on buildStart avoids race conditions before loads.playground-experimental/src/main.ts (1)
13-14
: Fix the DataLoaderPlugin type mismatch
Remove the@ts-expect-error
and update the call at playground-experimental/src/main.ts:13–14 to match the actual signature exported byunplugin-vue-router/data-loaders
.• Verify the plugin’s type in node_modules/unplugin-vue-router/data-loaders/index.d.ts (or its source) and adjust your call accordingly (e.g. passing arguments in the form it expects).
src/core/moduleConstants.ts (1)
31-35
: LGTM!The new resolver module constant and updated list are correctly integrated.
playground-experimental/tsconfig.json (1)
1-47
: LGTM!The TypeScript configuration properly extends Vue's config and sets up path mappings for the experimental playground.
src/options.ts (5)
236-243
: LGTM!The interface definition for experimental param matchers is well-structured with clear documentation.
245-247
: LGTM!The default configuration with
['src/params']
follows the established convention.
324-332
: LGTM!The normalization logic correctly handles boolean and object values for
paramMatchers
.
334-340
: LGTM!Directory resolution correctly handles both string and array inputs with proper path resolution.
397-400
: LGTM!The type alias provides convenient access to resolved options.
playground-experimental/auto-imports.d.ts (1)
1-14
: LGTM!Generated ambient declarations are properly configured for auto-imports.
src/codegen/generateRouteResolver.spec.ts (4)
1-22
: LGTM!Test setup is properly structured with appropriate imports and state initialization.
24-66
: LGTM!Tests for static path generation are comprehensive with clear snapshots.
68-133
: LGTM!The resolver generation test correctly validates parent-child relationships and internal naming.
135-173
: LGTM!Test correctly validates route ordering based on path specificity.
src/codegen/generateDTS.ts (2)
22-27
: LGTM!The parameter change from
vueRouterModule
toparamsTypesDeclaration
aligns with the new param matcher feature.
37-45
: LGTM!Conditional injection of param parsers declarations is properly implemented.
playground-experimental/src/router.ts (2)
9-12
: Router setup with experimental resolver looks correct.
Using experimental_createRouter with a resolver is consistent with the new virtual module and experimental API.
15-32
: Verify runtime parity with augmented types.
You’re extending RouteNamedMap with two dynamic routes. Ensure the resolver (and/or runtime code) actually registers routes with names 'custom-dynamic-name' and 'custom-dynamic-child-name' to avoid type/runtime drift.src/codegen/generateRouteMap.ts (1)
9-19
: Signature threading (options, paramParsersMap) through recursion is sound.
Call sites in core/context.ts are updated; recursion passes the new args consistently.src/codegen/generateRouteResolver.ts (2)
154-168
: Non-matchable nodes emit records without a path — confirm normalizeRouteRecord supports this.
For non-matchable nodes, path is omitted. Ensure normalizeRouteRecord tolerates records lacking a path (group-only nodes), or guard generation accordingly.
160-164
: Confirm availability and safety of node.regexp and node.matcherParts.
These symbols must be valid code (RegExp literal/string) and JSON-safe respectively. If either can be undefined, guard or sanitize to prevent broken output.playground-experimental/typed-router.d.ts (4)
24-33
: Typed params mapping looks coherent with param matchers/resolvers.Good propagation of typed params (e.g., number for
[userId=int]
, Param_date for[when=date]
) through raw/parsed generics.
90-94
: Conditional type for _RouteNamesForFilePath is standard and precise.The Record-based narrowing to infer route unions per file path is correct.
9-9
: TS configs already allow importing “.ts” in types viamoduleResolution: "Bundler"
. No changes needed.
45-82
: Default view mapping is correct
Onlysrc/pages/[name].vue
defines a nested route and renders<RouterView />
, so it’s the only entry withviews: 'default'
. All other pages have no child routes and correctly showviews: never
.
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
♻️ Duplicate comments (1)
playground-experimental/package.json (1)
9-16
: Add nodemon to devDependencies to satisfy the dev script.The dev script uses nodemon but it isn’t declared here, causing runtime failure in a workspace install.
"devDependencies": { + "nodemon": "^3.1.10", "@vitejs/plugin-vue": "^6.0.1", "@vue/compiler-sfc": "^3.5.18", "@vue/tsconfig": "^0.7.0", "json-server": "^0.17.4", "unplugin-vue-router": "workspace:*", "vite": "^7.1.2" },
🧹 Nitpick comments (3)
playground-experimental/package.json (1)
5-5
: Use nodemon’s -e without the dot for clarity.Minor: nodemon docs use extensions without a leading dot. Current flag likely works but this is clearer.
- "dev": "nodemon -w '../src/**/*.ts' -e .ts -x vite", + "dev": "nodemon -w '../src/**/*.ts' -e ts -x vite",package.json (2)
121-122
: Include playground-experimental in lint coverage.Currently excluded from the prettier check.
- "lint": "prettier -c '{src,test,e2e,examples,playground}/**/*.{ts,vue}'", + "lint": "prettier -c '{src,test,e2e,examples,playground,playground-experimental}/**/*.{ts,vue}'",
121-128
: Add scripts to run the experimental playground from the repo root.Improves DX and mirrors existing playground commands.
"scripts": { "build": "pnpm run --stream '/^build:/'", "build:core": "tsdown", "build:runtime": "tsdown --config tsdown-runtime.config.ts", "dev": "pnpm run vitest --ui", "vitest": "vitest --typecheck", "docs": "vitepress dev docs", "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs", "lint": "prettier -c '{src,test,e2e,examples,playground,playground-experimental}/**/*.{ts,vue}'", "play": "npm -C playground run dev", + "play:experimental": "npm -C playground-experimental run dev", "play:build": "npm -C playground run build", + "play:experimental:build": "npm -C playground-experimental run playground:build", "release": "node scripts/release.mjs", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1", "pretest": "pnpm run lint", "test": "pnpm run build && vitest --typecheck --coverage run && pnpm run docs:build" },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
examples/nuxt/package.json
(1 hunks)package.json
(5 hunks)playground-experimental/package.json
(1 hunks)playground/package.json
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- examples/nuxt/package.json
- playground/package.json
⏰ 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: Socket Security: Pull Request Alerts
🔇 Additional comments (3)
playground-experimental/package.json (1)
21-21
: Confirm CI/network access and reproducibility for private vue-router tarball URL.Ensure CI and contributors can fetch https://pkg.pr.new/vue-router@2d79fe7 and that your lockfile is committed to guarantee reproducible installs. If this is temporary, consider a comment or tracking issue.
package.json (2)
206-206
: Verify the private vue-router devDependency is acceptable for builds/tests.Using a tarball URL changes provenance and can break offline or air-gapped builds. Confirm CI/registry access and that the tarball’s internal version still satisfies peer constraints during local dev.
141-158
: Version bumps look good.Patch/minor bumps across deps are reasonable and low risk.
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 (6)
src/codegen/generateParamParsers.ts (5)
34-38
: Fix JSDoc param descriptionThe JSDoc refers to paramParserPath but the function receives a TreeRouteParam. Update the comment to avoid confusion.
68-82
: Rename parparsersMap to paramParsersMap for clarityTypo in the parameter name; use a consistent name across the codebase.
Apply this diff:
-export function generateParamsTypes( - params: TreeRouteParam[], - parparsersMap: ParamParsersMap -): Array<string | null> { +export function generateParamsTypes( + params: TreeRouteParam[], + paramParsersMap: ParamParsersMap +): Array<string | null> { return params.map((param) => { if (param.parser) { - if (parparsersMap.has(param.parser)) { - return parparsersMap.get(param.parser)!.typeName + if (paramParsersMap.has(param.parser)) { + return paramParsersMap.get(param.parser)!.typeName } else if (param.parser === 'int') { return 'number' } } return null }) }
84-101
: Use relativePath for generated imports; sanitize built-in checksAbsolute file-system import specifiers can break bundlers. Prefer the already-available relativePath. Also, see next comment to make the built-in “int” check consistent.
Apply this diff:
if (param.parser && paramParsers.has(param.parser)) { - const { name, absolutePath } = paramParsers.get(param.parser)! + const { name, relativePath } = paramParsers.get(param.parser)! const varName = `PARAM_PARSER__${name}` - importsMap.add(absolutePath, { name: 'parser', as: varName }) + importsMap.add(`./${relativePath}`, { name: 'parser', as: varName }) return ` ...${varName}, ` } else if (param.parser === 'int') { importsMap.add('vue-router/experimental', `PARAM_PARSER_INT`) return ` ...PARAM_PARSER_INT, ` }
95-98
: Normalize built-in “int” detectionElsewhere you already camelCase the parser name; do the same here to accept “Int”, “INT”, etc.
Apply this diff:
- } else if (param.parser === 'int') { + } else if (param.parser && camelCase(param.parser) === 'int') { importsMap.add('vue-router/experimental', `PARAM_PARSER_INT`) return ` ...PARAM_PARSER_INT, ` }
102-110
: Remove or improve the throw messageThrowing Error('what') is unhelpful and the param is non-optional by type. Either remove the check or provide a descriptive error.
Apply this diff to remove the check:
export function generateParamsOptions( params: TreeRouteParam[], importsMap: ImportsMap, paramParsers: ParamParsersMap ) { - if (!paramParsers) { - throw new Error('what') - }src/codegen/generateRouteResolver.ts (1)
35-40
: Drop unused MatcherPatternPathStar importIt's not referenced here. Remove to avoid unused import noise in generated output.
Apply this diff:
importsMap.add('vue-router/experimental', 'MatcherPatternPathStatic') importsMap.add('vue-router/experimental', 'MatcherPatternPathCustomParams') - importsMap.add('vue-router/experimental', 'MatcherPatternPathStar') importsMap.add('vue-router/experimental', 'normalizeRouteRecord')
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/codegen/generateParamParsers.ts
(1 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/tree.ts
(2 hunks)src/core/treeNodeValue.ts
(11 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/core/tree.ts
- src/codegen/generateRouteResolver.spec.ts
- src/core/treeNodeValue.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/codegen/generateParamParsers.ts (4)
src/core/tree.ts (3)
PrefixTree
(384-418)name
(242-249)params
(272-284)src/core/treeNodeValue.ts (1)
TreeRouteParam
(226-233)src/core/utils.ts (1)
ImportsMap
(299-368)src/utils/index.ts (1)
ts
(18-18)
src/codegen/generateRouteResolver.ts (4)
src/core/tree.ts (3)
PrefixTree
(384-418)TreeNode
(19-378)params
(272-284)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsOptions
(102-120)src/utils/index.ts (1)
ts
(18-18)
⏰ 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: Socket Security: Pull Request Alerts
🔇 Additional comments (3)
src/codegen/generateParamParsers.ts (2)
57-66
: Verify parser.get shape and path normalizationThe generated type alias assumes a named export parser with a 'get' property. Confirm that custom parsers follow this contract and that relativePath is normalized to POSIX separators in emitted code.
Would you like a small utility to normalize paths before they’re stored in ParamParsersMap?
122-133
: LGTM on TypesConfig augmentationThe ambient union for ParamParsers is clear and only emitted when needed.
src/codegen/generateRouteResolver.ts (1)
149-174
: LGTM on path serializationStatic vs. custom-params paths are emitted correctly, and params options integrate with generateParamsOptions.
e8c15b3
to
e5861d4
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #700 +/- ##
==========================================
+ Coverage 60.90% 64.14% +3.23%
==========================================
Files 36 38 +2
Lines 3379 4203 +824
Branches 618 801 +183
==========================================
+ Hits 2058 2696 +638
- Misses 1314 1500 +186
Partials 7 7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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
♻️ Duplicate comments (6)
src/utils/index.ts (1)
1-1
: Bug:constants
isn’t exported fromfs/promises
;fileExists()
always returns false
constants
import is invalid; accessingconstants.F_OK
throws and the catch returnsfalse
even for existing files.Apply this diff:
-import { access, constants } from 'fs/promises' +import { access } from 'fs/promises' @@ -export async function fileExists(filePath: string) { +export async function fileExists(filePath: string): Promise<boolean> { try { - await access(filePath, constants.F_OK) + await access(filePath) return true } catch { return false } }Also applies to: 20-27
src/core/tree.ts (1)
290-295
: Fix regex escaping for static segments.Static segments containing regex metacharacters (e.g., "users.list") will create invalid regex patterns. Apply proper escaping:
if (node.value.isParam() && node.value.re) { re = node.value.re + (re ? '\\/' : '') + re } else { - re = node.value.pathSegment + (re ? '\\/' : '') + re + // Escape special regex characters in static segments + const escaped = node.value.pathSegment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + re = escaped + (re ? '\\/' : '') + re }src/codegen/generateParamParsers.ts (1)
17-32
: Remove unnecessary async modifier.The function doesn't use
await
:-export async function warnMissingParamParsers( +export function warnMissingParamParsers( tree: PrefixTree, paramParsers: ParamParsersMap ) {src/codegen/generateRouteResolver.spec.ts (1)
126-126
: Filter glob pattern to prevent non-code files.Line 126 uses
glob('*')
which can include non-code files like README.md, causing invalid type imports.Apply this diff:
- return glob('*', { + return glob('*.{ts,js}', {src/core/context.ts (1)
126-126
: Filter glob pattern to code files only.Using
glob('*')
can match non-code files like README.md, causing invalid imports.- return glob('*', { + return glob('*.{ts,js}', {src/codegen/generateRouteResolver.ts (1)
93-93
: Fix potential negative repeat count.The padding calculation can produce negative values when varName is longer than expected.
- ` ${varName}, ${' '.repeat(String(state.id).length - varName.length + 2)}// ${path}` + ` ${varName}, ${' '.repeat(Math.max(0, String(state.id).length - varName.length + 2))}// ${path}`
🧹 Nitpick comments (2)
client.d.ts (1)
7-7
: Readonlyroutes
is a (minor) TS breaking change; align callback type for consistencySome users may have been mutating
routes
. Typing as readonly is good; consider makinghandleHotUpdate
’snewRoutes
readonly too for consistency.Apply this diff:
- export function handleHotUpdate( + export function handleHotUpdate( router: Router, - hotUpdateCallback?: (newRoutes: RouteRecordRaw[]) => void + hotUpdateCallback?: (newRoutes: readonly RouteRecordRaw[]) => void ): voidsrc/codegen/generateRouteMap.ts (1)
42-49
: Inefficient array initialization and type generation.Creating arrays and conditionally assigning paramType is unnecessarily complex.
- const params = node.params - let paramParsers: Array<string | null> = [] - let paramType: string = '' - - if (options.experimental.paramMatchers) { - paramParsers = generateParamsTypes(params, paramParsersMap) - paramType = EXPERIMENTAL_generateRouteParams(node, paramParsers) - } + const paramType = options.experimental.paramMatchers + ? EXPERIMENTAL_generateRouteParams(node, generateParamsTypes(node.params, paramParsersMap)) + : ''
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (55)
client.d.ts
(1 hunks)examples/nuxt/package.json
(1 hunks)package.json
(2 hunks)playground-experimental/.gitignore
(1 hunks)playground-experimental/auto-imports.d.ts
(1 hunks)playground-experimental/db.json
(1 hunks)playground-experimental/env.d.ts
(1 hunks)playground-experimental/index.html
(1 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/App.vue
(1 hunks)playground-experimental/src/main.ts
(1 hunks)playground-experimental/src/pages/(home).vue
(1 hunks)playground-experimental/src/pages/[name].vue
(1 hunks)playground-experimental/src/pages/[name]/24.vue
(1 hunks)playground-experimental/src/pages/[name]/[userId=int].vue
(1 hunks)playground-experimental/src/pages/a.[b].c.[d].vue
(1 hunks)playground-experimental/src/pages/b.vue
(1 hunks)playground-experimental/src/pages/events/[when=date].vue
(1 hunks)playground-experimental/src/pages/users/[userId=int].vue
(1 hunks)playground-experimental/src/pages/users/sub-[first]-[second].vue
(1 hunks)playground-experimental/src/params/date.ts
(1 hunks)playground-experimental/src/router.ts
(1 hunks)playground-experimental/src/utils.ts
(1 hunks)playground-experimental/tsconfig.config.json
(1 hunks)playground-experimental/tsconfig.json
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)playground-experimental/vite.config.ts
(1 hunks)playground/package.json
(1 hunks)playground/src/pages/(some-layout).vue
(1 hunks)playground/src/params/date.ts
(1 hunks)playground/src/params/number.ts
(1 hunks)playground/vite.config.ts
(1 hunks)pnpm-workspace.yaml
(1 hunks)src/codegen/generateDTS.ts
(2 hunks)src/codegen/generateParamParsers.ts
(1 hunks)src/codegen/generateRouteMap.spec.ts
(20 hunks)src/codegen/generateRouteMap.ts
(1 hunks)src/codegen/generateRouteParams.ts
(1 hunks)src/codegen/generateRouteRecords.ts
(1 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/context.ts
(10 hunks)src/core/extendRoutes.spec.ts
(9 hunks)src/core/moduleConstants.ts
(2 hunks)src/core/tree.spec.ts
(2 hunks)src/core/tree.ts
(2 hunks)src/core/treeNodeValue.ts
(11 hunks)src/data-loaders/meta-extensions.ts
(1 hunks)src/index.ts
(5 hunks)src/options.ts
(3 hunks)src/runtime.ts
(1 hunks)src/utils/index.ts
(2 hunks)tsconfig.json
(2 hunks)vitest.config.ts
(1 hunks)vitest.workspace.js
(0 hunks)
💤 Files with no reviewable changes (1)
- vitest.workspace.js
✅ Files skipped from review due to trivial changes (2)
- playground-experimental/tsconfig.config.json
- examples/nuxt/package.json
🚧 Files skipped from review as they are similar to previous changes (37)
- playground-experimental/src/pages/events/[when=date].vue
- playground-experimental/index.html
- playground-experimental/src/pages/(home).vue
- playground-experimental/src/pages/[name]/24.vue
- playground-experimental/src/pages/[name]/[userId=int].vue
- playground-experimental/src/pages/b.vue
- playground-experimental/src/pages/users/[userId=int].vue
- playground-experimental/env.d.ts
- playground-experimental/.gitignore
- src/core/extendRoutes.spec.ts
- vitest.config.ts
- src/codegen/generateRouteRecords.ts
- package.json
- src/core/tree.spec.ts
- playground/vite.config.ts
- playground/src/params/number.ts
- src/index.ts
- playground-experimental/src/pages/users/sub-[first]-[second].vue
- playground-experimental/src/params/date.ts
- playground/package.json
- src/data-loaders/meta-extensions.ts
- playground-experimental/src/main.ts
- playground-experimental/db.json
- playground-experimental/tsconfig.json
- pnpm-workspace.yaml
- playground-experimental/package.json
- playground-experimental/auto-imports.d.ts
- src/runtime.ts
- src/codegen/generateDTS.ts
- playground-experimental/src/router.ts
- playground/src/params/date.ts
- playground-experimental/src/pages/[name].vue
- src/codegen/generateRouteParams.ts
- playground-experimental/src/App.vue
- playground-experimental/vite.config.ts
- playground-experimental/src/utils.ts
- tsconfig.json
🧰 Additional context used
🧬 Code Graph Analysis (10)
src/codegen/generateRouteMap.spec.ts (3)
src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/options.ts (1)
DEFAULT_OPTIONS
(249-266)src/index.ts (1)
DEFAULT_OPTIONS
(29-29)
src/codegen/generateRouteResolver.ts (8)
src/core/tree.ts (4)
PrefixTree
(384-418)path
(221-226)TreeNode
(19-378)params
(272-284)src/options.ts (1)
ResolvedOptions
(400-400)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsOptions
(102-120)src/codegen/generateRouteRecords.ts (2)
generateRouteRecord
(15-113)generatePageImport
(148-169)src/utils/index.ts (1)
ts
(18-18)src/core/treeNodeValue.ts (1)
path
(74-76)src/core/extendRoutes.ts (1)
children
(190-194)
src/codegen/generateRouteMap.ts (4)
src/core/tree.ts (2)
TreeNode
(19-378)params
(272-284)src/options.ts (1)
ResolvedOptions
(400-400)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsTypes
(68-82)src/codegen/generateRouteParams.ts (2)
EXPERIMENTAL_generateRouteParams
(24-50)generateRouteParams
(3-22)
src/codegen/generateParamParsers.ts (4)
src/core/tree.ts (2)
PrefixTree
(384-418)name
(242-249)src/core/treeNodeValue.ts (1)
TreeRouteParam
(226-233)src/core/utils.ts (1)
ImportsMap
(299-368)src/utils/index.ts (1)
ts
(18-18)
src/core/tree.ts (2)
src/core/treeNodeValue.ts (1)
re
(273-286)src/core/extendRoutes.ts (1)
params
(175-177)
src/codegen/generateRouteResolver.spec.ts (6)
src/options.ts (2)
DEFAULT_OPTIONS
(249-266)resolveOptions
(313-395)src/codegen/generateRouteResolver.ts (3)
generateRouteRecord
(103-170)generateRouteRecordPath
(196-218)generateRouteResolver
(61-98)src/codegen/generateRouteRecords.ts (1)
generateRouteRecord
(15-113)src/core/utils.ts (1)
ImportsMap
(299-368)src/core/tree.ts (1)
PrefixTree
(384-418)src/codegen/generateParamParsers.ts (1)
ParamParsersMap
(7-15)
src/core/treeNodeValue.ts (2)
src/core/tree.ts (1)
params
(272-284)src/core/extendRoutes.ts (1)
params
(175-177)
src/options.ts (1)
src/core/utils.ts (1)
isArray
(65-66)
src/core/context.ts (10)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(7-15)warnMissingParamParsers
(17-32)generateParamParsersTypesDeclarations
(57-66)src/core/tree.ts (1)
name
(242-249)src/core/extendRoutes.ts (2)
name
(88-90)name
(95-97)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(61-98)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/core/moduleConstants.ts (1)
MODULE_ROUTES_PATH
(6-6)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
playground-experimental/typed-router.d.ts (2)
playground-experimental/src/router.ts (1)
RouteNamedMap
(16-31)src/codegen/generateRouteParams.ts (1)
ParamValue
(83-85)
🔇 Additional comments (18)
playground/src/pages/(some-layout).vue (1)
3-3
: Confirmname: false
is intended and supported by definePageIf the intent is to opt-out of a route name, ensure no navigation relies on this page’s name and that types/runtime accept
false
here.playground-experimental/src/pages/a.[b].c.[d].vue (1)
1-5
: LGTMMinimal page to surface name/path works for the experimental routes.
src/core/moduleConstants.ts (1)
7-7
: LGTM: new resolver virtual module constant and registration
MODULE_RESOLVER_PATH
and its inclusion inMODULES_ID_LIST
look correct.Also applies to: 31-35
src/options.ts (4)
324-333
: LGTM! Clean ternary logic for paramMatchers normalization.The logic correctly handles boolean
true
by using defaults, merges object configs with defaults, and returnsundefined
for falsy values.
334-340
: LGTM! Proper array normalization for paramMatchersDir.The code correctly normalizes
dir
to an array and resolves paths relative to root.
342-347
: LGTM! Consistent path resolution for autoExportsDataLoaders.The refactored code maintains the same logic while improving readability.
349-357
: LGTM! Proper experimental options restructuring.The code correctly preserves
undefined
forparamMatchers
when not set and replacesdir
with resolved paths when present.src/core/tree.ts (2)
326-348
: LGTM! Well-structured matcher parts generation.The recursive traversal correctly builds the matcher parts array, handling both single and multiple sub-segments appropriately.
350-354
: LGTM! Clear matchability check.The type guard correctly ensures a node has components and a truthy name.
playground-experimental/typed-router.d.ts (1)
1-133
: LGTM! Well-structured type definitions.The auto-generated TypeScript declarations properly define custom param parsers, route maps, and file info mappings for type-safe routing.
src/codegen/generateRouteMap.spec.ts (1)
22-24
: LGTM! Test updated for new API signature.All test calls correctly pass the required three arguments to
generateRouteNamedMap
.src/codegen/generateRouteResolver.spec.ts (2)
18-23
: Good test structure with proper state reset.The beforeEach hook correctly resets DEFAULT_STATE to prevent test interference.
309-320
: Clean helper function for extracting route order.The
getRouteOrderFromResolver
utility effectively parses resolver output for testing route prioritization.src/core/treeNodeValue.ts (3)
257-271
: Well-structured score calculation for route prioritization.The score getter properly differentiates static (300) from param segments with appropriate malus values.
274-286
: Good regex escaping implementation.Properly escapes regex special characters using REGEX_CHARS_RE pattern.
522-547
: Parser state handling looks correct.The paramParser state properly handles '=' delimiter and accumulates parser type in paramParserBuffer.
src/core/context.ts (1)
252-273
: Clean resolver generation implementation.The generateResolver function properly assembles imports and resolver code.
src/codegen/generateRouteResolver.ts (1)
12-50
: Robust route scoring algorithm.The compareRouteScore function implements comprehensive priority comparison with minimum score, average score, segment count, and path length.
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: 1
♻️ Duplicate comments (1)
src/codegen/generateRouteResolver.ts (1)
90-96
: Fix padding logic to avoid negative repeat while keeping alignmentClamp the repeat count to non-negative to prevent runtime errors.
Apply this diff:
- .map( - ({ varName, path }) => - ` ${varName}, ${' '.repeat(String(state.id).length - varName.length + 2)}// ${path}` - ) + .map( + ({ varName, path }) => + ` ${varName},${' '.repeat(Math.max(0, String(state.id).length - varName.length + 2))}// ${path}` + )
🧹 Nitpick comments (10)
playground-experimental/src/pages/u[name]/24.vue (2)
5-5
: Fix typo in copy"Too test" -> "To test".
Apply:
- <p>Too test specificity in routes</p> + <p>To test specificity in routes.</p>
1-1
: Remove empty script blockNo script logic is used. Dropping it keeps the SFC lean.
Apply:
-<script lang="ts" setup></script>
playground-experimental/src/pages/u[name]/[userId=int].vue (2)
4-4
: Clarify the intent of the headingThis route will be visible except when the path matches the more specific static child (…/24). The current message can mislead.
Apply:
- <h2>This page should never be visible</h2> + <h2>Dynamic userId route (hidden when the path is .../24)</h2>
6-6
: Drop RouterView if there are no nested childrenIf no routes exist under /u[name]/[userId=int]/..., the RouterView is unnecessary.
Apply:
- <RouterView />
If you plan to add children later, keeping it is fine.
playground-experimental/src/pages/[...404].vue (1)
21-23
: Prefer meta keys without spacesAccessing with dot notation becomes impossible with spaces. Consider camelCase.
Apply:
- "meta": { - "from block": true - } + "meta": { + "fromBlock": true + }Note: Update any code that reads $route.meta['from block'] accordingly.
src/runtime.ts (4)
119-129
: Consider preserving alias concatenation like _mergeRouteRecordThe experimental merge overwrites
alias
instead of concatenating. If aliases matter in the experimental resolver, mirror the behavior of_mergeRouteRecord
.Apply this diff:
- // Merge meta properties specially + // Merge meta properties specially const meta = Object.assign({}, acc.meta, routeRecord.meta) - // Merge all other properties directly + // Merge all other properties directly Object.assign(acc, routeRecord) + // Concatenate aliases if present + const alias = ([] as string[]).concat(acc.alias || [], routeRecord.alias || []) + if (alias.length) acc.alias = alias + // Set the merged meta
73-84
: Make QueryParamType extensible via module augmentationA type alias cannot be augmented by users. Expose a map interface and derive the union from its keys to allow extension.
Apply this diff:
-export type QueryParamType = 'int' | 'bool' +export interface QueryParamTypeMap { + int: true + bool: true +} +export type QueryParamType = keyof QueryParamTypeMap
81-82
: Fix broken JSDoc link
https://uvr.esm.is/TODO
is a placeholder and will confuse users. Replace with a neutral note until docs exist.Apply this diff:
- * and any parser in the {@link https://uvr.esm.is/TODO | params folder }. + * and any parser available in the params directory (documentation pending).
62-65
: Track the query serialization TODOThe TODO implies non-final behavior. Please open an issue to scope the custom serialization design and extraction rules.
I can open a tracking issue with a short proposal if you want.
src/codegen/generateRouteResolver.ts (1)
80-85
: Avoid emitting unused imports for the star matcher
MatcherPatternPathStar
is added unconditionally but never used here. Emit it only when needed to keep generated code clean and avoid noUnusedLocals issues.Apply this diff to remove it for now:
importsMap.add('vue-router/experimental', 'createStaticResolver') importsMap.add('vue-router/experimental', 'MatcherPatternPathStatic') importsMap.add('vue-router/experimental', 'MatcherPatternPathCustomParams') - importsMap.add('vue-router/experimental', 'MatcherPatternPathStar') importsMap.add('vue-router/experimental', 'normalizeRouteRecord')
Optionally, add it conditionally when a star segment is detected once that case is generated.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
src/codegen/__snapshots__/generateRouteRecords.spec.ts.snap
is excluded by!**/*.snap
📒 Files selected for processing (10)
playground-experimental/src/pages/[...404].vue
(1 hunks)playground-experimental/src/pages/u[name].vue
(1 hunks)playground-experimental/src/pages/u[name]/24.vue
(1 hunks)playground-experimental/src/pages/u[name]/[userId=int].vue
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)src/codegen/generateRouteRecords.ts
(2 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/treeNodeValue.ts
(11 hunks)src/runtime.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/codegen/generateRouteResolver.spec.ts
- src/codegen/generateRouteRecords.ts
- src/core/treeNodeValue.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-15T16:11:02.610Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.610Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.ts
🧬 Code Graph Analysis (3)
src/runtime.ts (2)
src/core/tree.ts (1)
meta
(264-270)src/core/extendRoutes.ts (2)
meta
(110-112)meta
(119-122)
src/codegen/generateRouteResolver.ts (7)
src/core/tree.ts (6)
PrefixTree
(384-418)path
(221-226)TreeNode
(19-378)isMatchable
(350-354)name
(242-249)params
(272-284)src/options.ts (1)
ResolvedOptions
(400-400)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsOptions
(102-120)src/codegen/generateRouteRecords.ts (3)
generateRouteRecord
(15-110)formatMeta
(168-178)generatePageImport
(145-166)src/utils/index.ts (1)
ts
(18-18)src/core/treeNodeValue.ts (1)
path
(74-76)
playground-experimental/typed-router.d.ts (2)
playground-experimental/src/router.ts (1)
RouteNamedMap
(16-31)src/codegen/generateRouteParams.ts (1)
ParamValue
(83-85)
🔇 Additional comments (5)
playground-experimental/src/pages/u[name].vue (1)
3-8
: LGTMParent displays params and hosts children correctly. Works well for testing specificity vs. static child (/24) and dynamic child ([userId=int]).
playground-experimental/typed-router.d.ts (2)
24-34
: Typed routes for u[name] and children look correct
- '/u[name]' -> '/u:name' with children union of '/u[name]/24' and '/u[name]/[userId=int]'.
- Static '/24' outranks the dynamic int segment, which matches the intent of the playground.
46-78
: typed-router.d.ts is already included in tsconfig.json
Verified that playground-experimental/tsconfig.json lists./typed-router.d.ts
underinclude
, so typed routes are available in IDEs.playground-experimental/src/pages/[...404].vue (1)
2-7
: LGTMMeta defined via definePage is fine and complements the route block.
src/codegen/generateRouteResolver.ts (1)
128-141
: DefinePage import breadth can yield undefined defaultsYou import definePage data for every component when
node.hasDefinePage
is true. Some components may not export a definePage default, making their variablesundefined
. With the runtime merge fix, this is safe, but confirm your loader always defines a default (even{}
) or rely on the new guard.If the loader cannot guarantee a default, no action in codegen is needed after adopting the runtime guard; otherwise, filter to known components if that metadata becomes available.
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: 7
♻️ Duplicate comments (2)
src/codegen/generateParamParsers.ts (2)
17-32
: Async removal applied correctlyPrevious feedback about removing unnecessary async was addressed; the function is now synchronous and iterates deep-sorted nodes.
102-109
: Replace placeholder error messageUse a descriptive error to aid debugging.
if (!paramParsers) { - throw new Error('what') + throw new Error('paramParsers is required for generateParamsOptions') }
🧹 Nitpick comments (8)
playground-experimental/src/page-outside.vue (1)
1-2
: Remove empty script blockNo script logic is present; dropping it reduces noise.
-<script lang="ts" setup></script> -playground-experimental/src/App.vue (3)
6-6
: Gate console log to dev builds.Avoid noisy logs in prod.
-console.log(`We have ${routes.length} routes.`) +if (import.meta.env.DEV) console.log(`We have ${routes.length} routes.`)
34-37
: Pretty-print route params for readability.Raw objects render as [object Object].
- <pre>{{ $route.params }}</pre> + <pre>{{ JSON.stringify($route.params, null, 2) }}</pre>
2-2
: Remove unused import.
useRoute
isn’t used in this component.-import { useRoute } from 'vue-router'
src/core/context.ts (1)
216-224
: NormalizerelativePath
consistently (avoid leading './').Initial scan stores
relativePath
without './', watcher path adds it, leading to inconsistent imports likeimport('././path')
.paramParsers.set(name, { name, typeName: `Param_${name}`, absolutePath, - relativePath: './' + relative(options.root, absolutePath), + relativePath: relative(options.root, absolutePath), })src/codegen/generateParamParsers.ts (3)
25-27
: Enrich warn message with param nameIncluding the param name improves debuggability without adding noise.
- console.warn( - `Parameter parser "${param.parser}" not found for route "${node.fullPath}".` - ) + console.warn( + `Parameter parser "${param.parser}" not found for route "${node.fullPath}" (param "${param.paramName}").` + )
39-55
: Prefer early return for native parserMinor: check for 'int' before computing camelCase.
export function generateParamParserType(param: TreeRouteParam): string | null { if (!param.parser) { return null } - // TODO: actualpath - // const nameWithExtension = basename(param.paramName) - const name = camelCase(param.parser) - - // TODO: treat custom parsers first - - // native parsers - if (name === 'int') { - return 'number' - } - - return `Param_${name}` + // native parsers + if (param.parser === 'int') { + return 'number' + } + // TODO: actualpath + // const nameWithExtension = basename(param.paramName) + const name = camelCase(param.parser) + return `Param_${name}` }
68-82
: RenameparparsersMap
→paramParsersMap
for consistencyUpdate the parameter name and its usages in
src/codegen/generateParamParsers.ts
:export function generateParamsTypes( params: TreeRouteParam[], - parparsersMap: ParamParsersMap + paramParsersMap: ParamParsersMap ): Array<string | null> { return params.map((param) => { if (param.parser) { - if (parparsersMap.has(param.parser)) { - return parparsersMap.get(param.parser)!.typeName + if (paramParsersMap.has(param.parser)) { + return paramParsersMap.get(param.parser)!.typeName } else if (param.parser === 'int') { return 'number' } } return null }) }[optional_refactors_recommended]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
playground-experimental/src/App.vue
(1 hunks)playground-experimental/src/page-outside.vue
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)playground-experimental/vite.config.ts
(1 hunks)src/codegen/generateParamParsers.ts
(1 hunks)src/core/context.ts
(10 hunks)src/core/extendRoutes.ts
(1 hunks)src/core/tree.ts
(3 hunks)src/core/treeNodeValue.ts
(11 hunks)src/utils/index.ts
(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/core/extendRoutes.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/utils/index.ts
- playground-experimental/typed-router.d.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-15T16:11:02.610Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.610Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/core/treeNodeValue.ts
src/core/context.ts
🧬 Code Graph Analysis (5)
playground-experimental/vite.config.ts (2)
scripts/release.mjs (1)
__dirname
(13-13)src/index.ts (1)
VueRouterAutoImports
(240-253)
src/codegen/generateParamParsers.ts (4)
src/core/tree.ts (3)
PrefixTree
(385-419)name
(243-250)params
(273-285)src/core/treeNodeValue.ts (1)
TreeRouteParam
(226-233)src/core/utils.ts (1)
ImportsMap
(299-368)src/utils/index.ts (1)
ts
(18-18)
src/core/tree.ts (2)
src/core/treeNodeValue.ts (2)
re
(282-295)escapeRegex
(247-248)src/core/extendRoutes.ts (1)
params
(177-179)
src/core/treeNodeValue.ts (2)
src/core/extendRoutes.ts (1)
params
(177-179)src/core/tree.ts (1)
params
(273-285)
src/core/context.ts (10)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(7-15)warnMissingParamParsers
(17-32)generateParamParsersTypesDeclarations
(57-66)src/core/extendRoutes.ts (2)
name
(90-92)name
(97-99)src/core/tree.ts (1)
name
(243-250)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(62-99)src/utils/index.ts (1)
ts
(18-18)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/core/moduleConstants.ts (1)
MODULE_ROUTES_PATH
(6-6)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
⏰ 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: test (lts/*, ubuntu-latest)
- GitHub Check: Cloudflare Pages
🔇 Additional comments (9)
playground-experimental/src/page-outside.vue (1)
3-5
: LGTMTemplate is clear and minimal.
src/core/treeNodeValue.ts (3)
235-249
: Proper regex escaping utility added.Good call extracting and reusing
escapeRegex
.
282-295
: Correct use ofescapeRegex
in param regex builder.Static segments are safely escaped; param patterns look solid.
497-503
: Avoid pushing leading empty static subsegment.This prevents bogus “” entries when a segment starts with a param.
src/core/tree.ts (2)
287-303
: Static segment escaping inregexp
is correct.Using
escapeRegex
fixes meta-character issues in route regex construction.
301-302
: Confirm case-insensitive matching is intended.
/i
changes default Vue Router behavior (case-sensitive). Validate this aligns with resolver expectations.Would you like me to scan for consumers that rely on case-sensitive matching before changing it?
src/core/context.ts (1)
71-73
: Good: param parser scan restricted to code files.Filtering to
*.{ts,js}
prevents spurious imports.src/codegen/generateParamParsers.ts (2)
57-66
: Type declarations generation looks goodThe emitted ReturnType over typeof import(...).parser['get'] is consistent with how options import parser objects elsewhere.
122-133
: LGTM on ambient module augmentationThe union emission and empty-case guard are appropriate.
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: 1
♻️ Duplicate comments (2)
src/runtime.ts (1)
114-124
: Guard against nullish records and undefined meta in experimental mergeAccessing routeRecord.meta or spreading acc.meta when either is undefined will throw at runtime. Also, Object.assign with an undefined routeRecord will throw. Add a nullish guard and safe meta merge.
Apply:
export function _mergeRouteRecordExperimental( main: EXPERIMENTAL_RouteRecordRaw, ...routeRecords: Partial<DefinePage>[] ): EXPERIMENTAL_RouteRecordRaw { - return routeRecords.reduce((acc, routeRecord) => { - Object.assign(acc, routeRecord) - acc.meta = { ...acc.meta, ...routeRecord.meta } - - return acc - }, main) + return routeRecords.reduce((acc, routeRecord) => { + if (!routeRecord) return acc + const meta = Object.assign({}, acc.meta ?? {}, routeRecord.meta ?? {}) + Object.assign(acc, routeRecord) + if (Object.keys(meta).length) acc.meta = meta + return acc + }, main) }src/codegen/generateParamParsers.ts (1)
91-94
: Normalize absolute import path (cross-OS safety)Backslashes in Windows paths break import specifiers. Normalize before adding.
- const { name, absolutePath } = paramParsers.get(param.parser)! - const varName = `PARAM_PARSER__${name}` - importsMap.add(absolutePath, { name: 'parser', as: varName }) + const { name, absolutePath } = paramParsers.get(param.parser)! + const varName = `PARAM_PARSER__${name}` + const importPath = absolutePath.replace(/\\/g, '/') + importsMap.add(importPath, { name: 'parser', as: varName })
🧹 Nitpick comments (6)
src/runtime.ts (2)
74-75
: Allow custom param parser names beyond 'int' | 'bool'If users can add parsers (e.g., from a params folder), this alias should be open-ended. Otherwise, using custom parser names will not type-check.
Two options:
- Minimal change (keeps suggestions, allows any string):
-export type QueryParamType = 'int' | 'bool' +export type QueryParamType = 'int' | 'bool' | (string & {})
- Augmentable pattern:
// in this file export interface _RouterParamParsers {} // to be augmented elsewhere export type QueryParamType = keyof ({ int: unknown; bool: unknown } & _RouterParamParsers)
123-123
: Optional: avoid mutating the input “main”If you prefer immutability, seed the reducer with a shallow clone.
- }, main) + }, { ...main })src/codegen/generateParamParsers.ts (4)
43-48
: Clarify or remove TODOsThe TODOs are vague (“actualpath”, “treat custom parsers first”). Either address them or remove to avoid confusion.
60-66
: Normalize Windows paths in generated type importsrelativePath may contain backslashes on Windows, breaking import specifiers. Normalize to POSIX-style slashes before embedding.
return Array.from(paramParsers.values()) - .map( - ({ typeName, relativePath }) => - `type ${typeName} = ReturnType<NonNullable<typeof import('./${relativePath}').parser['get']>>` - ) + .map(({ typeName, relativePath }) => { + const importPath = relativePath.replace(/\\/g, '/') + return `type ${typeName} = ReturnType<NonNullable<typeof import('./${importPath}').parser['get']>>` + }) .join('\n')
68-76
: Fix param name typo for readabilityRename parparsersMap → paramParsersMap for consistency.
-export function generateParamsTypes( - params: TreeRouteParam[], - parparsersMap: ParamParsersMap -): Array<string | null> { +export function generateParamsTypes( + params: TreeRouteParam[], + paramParsersMap: ParamParsersMap +): Array<string | null> { return params.map((param) => { if (param.parser) { - if (parparsersMap.has(param.parser)) { - return parparsersMap.get(param.parser)!.typeName + if (paramParsersMap.has(param.parser)) { + return paramParsersMap.get(param.parser)!.typeName } else if (param.parser === 'int') { return 'number' }This will require updating call sites accordingly.
91-94
: Consider using relativePath instead of absolutePath for ESM importsAbsolute file paths can be problematic in some bundlers and TS transpilation setups. Using the computed relativePath improves portability (normalize slashes as above).
- const { name, absolutePath } = paramParsers.get(param.parser)! + const { name, relativePath } = paramParsers.get(param.parser)! const varName = `PARAM_PARSER__${name}` - const importPath = absolutePath.replace(/\\/g, '/') + const importPath = relativePath.replace(/\\/g, '/') importsMap.add(importPath, { name: 'parser', as: varName })Please confirm that ImportsMap resolves specifiers relative to the generated file location.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/codegen/generateParamParsers.ts
(1 hunks)src/runtime.ts
(2 hunks)
🔇 Additional comments (7)
src/runtime.ts (3)
2-2
: Import of experimental types is fineType-only import from vue-router/experimental is appropriate.
62-66
: DefinePage.query addition looks goodShape is reasonable and future-proof for normalizers/codegen.
68-73
: No action on TODO commentAcknowledged. Nothing to change here right now.
src/codegen/generateParamParsers.ts (4)
7-15
: ParamParsersMap shape looks solidCovers the essential metadata (names and paths) for codegen and typing.
17-32
: Synchronous warnMissingParamParsers is correctNo async work; iteration over getChildrenDeepSorted() is appropriate. Log message is clear and scoped.
119-130
: Types augmentation looks goodConditional emission and union literal listing are correct.
95-97
: No action needed: ImportsMap.add supports both overloads
Verified thatImportsMap.add
accepts both astring
and anImportEntry
object, so the import emission is correct.
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)
playground/vite.config.ts (1)
12-13
: Keep path resolution consistent; avoid introducing a custom __dirname.To match the rest of the file (which uses fileURLToPath(new URL(...))), inline the path resolution in beforeWriteFiles and drop the extra variable. No functional change; just consistency and one less global.
Apply this diff to remove the variable:
-const __dirname = fileURLToPath(new URL('.', import.meta.url)) -Then update the usage in beforeWriteFiles (outside the selected range) to:
root.insert('/from-root', fileURLToPath(new URL('./src/pages/index.vue', import.meta.url)))
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
playground/vite.config.ts
(2 hunks)src/runtime.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/runtime.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-16T13:01:42.699Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.699Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
playground/vite.config.ts
📚 Learning: 2025-08-16T13:00:51.231Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.231Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
playground/vite.config.ts
🧬 Code Graph Analysis (1)
playground/vite.config.ts (1)
scripts/release.mjs (1)
__dirname
(13-13)
⏰ 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: Cloudflare Pages
🔇 Additional comments (1)
playground/vite.config.ts (1)
55-56
: Confirm: experimental.paramMatchers is explicitly disabled.Setting paramMatchers: false will disable the new param-matcher flow in the playground. If that’s intentional, all good. If the goal is to exercise the feature here, switch to an object (e.g., { dir: 'src/param-matchers' }) or remove the option to rely on defaults. Based on the retrieved learnings, booleans are normalized earlier in options resolution, so this is valid at the plugin boundary.
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)
playground-experimental/index.html (2)
3-11
: Move the <style> block into and add a <title>.The style element is outside of head/body, which is invalid HTML. Also, a title improves accessibility and DX.
Apply:
<head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - </head> - <style> - :root { - color-scheme: light dark; - } - </style> + <title>Playground (experimental)</title> + <style> + :root { + color-scheme: light dark; + } + </style> + </head>
13-18
: Add rel="noopener noreferrer" to the external link.Prevents reverse tabnabbing when using target="_blank".
Apply:
- target="_blank" + target="_blank" rel="noopener noreferrer"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
playground-experimental/index.html
(1 hunks)
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: 4
♻️ Duplicate comments (5)
src/runtime-experimental.ts (1)
86-101
: Guard merge against nullish definePage recordsThe reducer assumes each routeRecord is defined. If any imported default is undefined, accessing routeRecord.meta will throw.
export function _mergeRouteRecord( @@ ) { - return routeRecords.reduce((acc, routeRecord) => { - Object.assign(acc, routeRecord) - acc.meta = { ...acc.meta, ...routeRecord.meta } - - return acc - }, main) + return routeRecords.reduce((acc, routeRecord) => { + if (!routeRecord) return acc + Object.assign(acc, routeRecord) + if (routeRecord.meta) { + acc.meta = { ...acc.meta, ...routeRecord.meta } + } + return acc + }, main) }src/runtime.ts (1)
74-89
: Harden experimental merge to skip nullish recordsSame issue as flagged previously: reduce should skip falsy routeRecord and guard meta.
export function _mergeRouteRecordExperimental( @@ ) { - return routeRecords.reduce((acc, routeRecord) => { - Object.assign(acc, routeRecord) - acc.meta = { ...acc.meta, ...routeRecord.meta } - - return acc - }, main) + return routeRecords.reduce((acc, routeRecord) => { + if (!routeRecord) return acc + Object.assign(acc, routeRecord) + if (routeRecord.meta) { + acc.meta = { ...acc.meta, ...routeRecord.meta } + } + return acc + }, main) }src/codegen/generateRouteResolver.spec.ts (1)
205-205
: Typo in test data: wrong file path for '/b/e/f'File path should match the route.
- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')After changing, update the inline snapshots (e.g., run tests with -u) to reflect import('b/e/f.vue').
src/core/context.ts (1)
336-343
: Guard DTS warning against missing param parsersUse optional chaining to avoid accessing .length on undefined.
- if ( - options.experimental.paramMatchers && - options.experimental.paramMatchers.dir.length > 0 - ) { + if (options.experimental.paramMatchers?.dir?.length) { warnMissingParamParsers(routeTree, paramParsers) }src/codegen/generateRouteResolver.ts (1)
89-96
: Guard the repeat count while preserving comment alignmentWrap the repeat count with Math.max(0, …) to avoid runtime throws if the computed value is ever negative. This keeps alignment intact (as requested) and is future-proof.
- .map( - ({ varName, path }) => - ` ${varName}, ${' '.repeat(String(state.id).length - varName.length + 2)}// ${path}` - ) + .map( + ({ varName, path }) => + ` ${varName}, ${' '.repeat(Math.max(0, String(state.id).length - varName.length + 2))}// ${path}` + )
🧹 Nitpick comments (7)
src/runtime.ts (1)
63-89
: Avoid code duplication between runtime and runtime-experimentalThere are two implementations of the experimental merge helper now (here and in src/runtime-experimental.ts via alias). This risks divergence.
Consider a single implementation source (keeping type-only imports to avoid the children typing issue noted in comments) or add a minimal test to ensure both stay functionally identical. Do you want me to draft such a test?
src/core/context.ts (1)
71-73
: Broaden param parser glob to common script extensionsSupport tsx/jsx/mjs/cjs too.
- const PARAM_PARSER_GLOB = '*.{ts,js}' + const PARAM_PARSER_GLOB = '*.{ts,tsx,js,jsx,mjs,cjs}'src/codegen/generateRouteResolver.ts (5)
89-96
: Add deterministic tie-breakers to the resolver orderingWhen scores are equal, add secondary sort keys (path length, then lexicographic) for deterministic emission across environments.
- .sort((a, b) => compareRouteScore(a.score, b.score)) + .sort((a, b) => { + const byScore = compareRouteScore(a.score, b.score) + if (byScore) return byScore + return a.path.length - b.path.length || a.path.localeCompare(b.path) + })
149-162
: Escape route names to prevent invalid string literalsUsing raw interpolation can break if a name contains quotes or special chars. Emit a properly escaped string literal.
- recordName = `name: '${node.name}',` + // ensure proper escaping of the route name + recordName = `name: ${JSON.stringify(node.name)},`
173-188
: Normalize whitespace formatting consistently for both branchesThe trimming/filtering chain only applies to the else branch due to operator precedence, causing inconsistent output. Apply it to the whole ternary.
- recordDeclaration = - definePageDataList.length > 0 - ? ` -const ${varName} = normalizeRouteRecord( - ${generateRouteRecordMerge(routeRecordObject, definePageDataList, importsMap)} -) -` - : ` -const ${varName} = normalizeRouteRecord(${routeRecordObject}) -` - .trim() - .split('\n') - // remove empty lines - .filter((l) => l.trimStart().length > 0) - .join('\n') + recordDeclaration = ( + definePageDataList.length > 0 + ? ` +const ${varName} = normalizeRouteRecord( + ${generateRouteRecordMerge(routeRecordObject, definePageDataList, importsMap)} +) +` + : ` +const ${varName} = normalizeRouteRecord(${routeRecordObject}) +` + ) + .trim() + .split('\n') + // remove empty lines + .filter((l) => l.trimStart().length > 0) + .join('\n')
127-141
: Handle unknown SFC lang defensively when building definePage importsIf getLang(filePath) returns undefined or an unexpected value, the query suffix becomes invalid. Provide a sane default.
- const lang = getLang(filePath) + const lang = getLang(filePath) ?? 'ts'Please verify getLang() cannot return undefined for your inputs; otherwise, the default avoids malformed queries like lang.undefined.
254-255
: Escape static paths safelyUse JSON.stringify to emit valid string literals even if a path contains quotes.
- return `path: new MatcherPatternPathStatic('${node.fullPath}'),` + return `path: new MatcherPatternPathStatic(${JSON.stringify(node.fullPath)}),`
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (9)
package.json
(2 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/pages/users/[userId=int].vue
(1 hunks)playground-experimental/src/params/date.ts
(1 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/context.ts
(10 hunks)src/runtime-experimental.ts
(1 hunks)src/runtime.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- playground-experimental/src/params/date.ts
- playground-experimental/package.json
- package.json
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-15T16:11:02.610Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.610Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.spec.ts
src/codegen/generateRouteResolver.ts
src/core/context.ts
📚 Learning: 2025-08-16T13:00:51.231Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.231Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
src/core/context.ts
🧬 Code Graph Analysis (5)
src/runtime-experimental.ts (1)
src/runtime.ts (5)
_definePage
(9-9)DefinePage
(52-61)definePage
(16-16)_mergeRouteRecord
(27-47)_mergeRouteRecordExperimental
(74-89)
src/codegen/generateRouteResolver.spec.ts (7)
src/options.ts (2)
DEFAULT_OPTIONS
(249-266)resolveOptions
(313-395)src/index.ts (1)
DEFAULT_OPTIONS
(29-29)src/codegen/generateRouteResolver.ts (3)
generateRouteRecord
(103-208)generateRouteRecordPath
(234-256)generateRouteResolver
(62-98)src/codegen/generateRouteRecords.ts (1)
generateRouteRecord
(15-110)src/core/utils.ts (1)
ImportsMap
(299-368)src/core/tree.ts (1)
PrefixTree
(385-419)src/codegen/generateParamParsers.ts (1)
ParamParsersMap
(7-15)
src/codegen/generateRouteResolver.ts (7)
src/core/tree.ts (5)
path
(222-227)TreeNode
(20-379)isMatchable
(351-355)name
(243-250)params
(273-285)src/options.ts (1)
ResolvedOptions
(400-400)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsOptions
(102-117)src/codegen/generateRouteRecords.ts (3)
generateRouteRecord
(15-110)formatMeta
(168-178)generatePageImport
(145-166)src/utils/index.ts (1)
ts
(18-18)src/core/treeNodeValue.ts (1)
path
(74-76)
src/core/context.ts (8)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(7-15)warnMissingParamParsers
(17-32)generateParamParsersTypesDeclarations
(57-66)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(62-98)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/core/moduleConstants.ts (1)
MODULE_ROUTES_PATH
(6-6)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
src/runtime.ts (1)
src/runtime-experimental.ts (2)
_mergeRouteRecordExperimental
(104-104)DefinePage
(21-38)
🔇 Additional comments (1)
src/codegen/generateRouteResolver.ts (1)
62-68
: Imports are correctly emitted in the caller
In src/core/context.ts’s generateResolver() (lines 267–270 and 324–327), you already callimportsMap.toString()
, append a newline if needed, and prepend it to the resolver code. No changes required.
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: 1
♻️ Duplicate comments (3)
src/runtime.ts (1)
83-85
: Fix runtime error when meta/records are undefinedSpreading
main.meta
throws if it's undefined, andrecord.meta
access throws if any record is nullish. Guard and useObject.assign
to safely merge.- for (const record of routeRecords) { - main.meta = { ...main.meta, ...record.meta } - } + for (const record of routeRecords) { + if (!record) continue + main.meta = Object.assign({}, main.meta, record.meta) + }src/codegen/generateRouteResolver.spec.ts (2)
239-243
: Fix mismatched file path for route '/b/e/f' (typo reintroduced).Route path and file path disagree. This was flagged before; please correct both the fixture and the inline snapshot.
Apply:
- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')And in the snapshot:
- 'default': () => import('b/c/f.vue') + 'default': () => import('b/e/f.vue')Also applies to: 276-281
336-341
: Same path/file mismatch in “strips off empty parent records”.Ensure the file path matches the route and update the snapshot import accordingly.
- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')- 'default': () => import('b/c/f.vue') + 'default': () => import('b/e/f.vue')Also applies to: 372-378
🧹 Nitpick comments (5)
src/runtime.ts (1)
65-73
: Document that only meta is merged (intentional behavior?)If only meta is meant to be merged (and other DefinePage fields ignored), spell it out to avoid future regressions. If not, we should revisit the merge semantics.
/** * Merges route record objects for the experimental resolver format. * This function is specifically designed to work with objects that will be passed to normalizeRouteRecord(). * * @internal * * @param main - main route record object * @param routeRecords - route records to merge (from definePage imports) * @returns merged route record object + * Note: Only the `meta` field is merged; other DefinePage fields are intentionally ignored + * to avoid leaking DefinePage-only constructs (e.g. `name: false`) into runtime records. */src/codegen/generateRouteResolver.spec.ts (4)
9-11
: Use a type-only import for ParamParsersMap.It’s only used as a type in tests; avoid a runtime import.
-import { ParamParsersMap } from './generateParamParsers' +import type { ParamParsersMap } from './generateParamParsers'
507-545
: Assert definePage helper imports are generated.The snapshot references _definePage_default_0 and _mergeRouteRecordExperimental. Add assertions to ensure the corresponding import lines exist to prevent silent regressions.
Example assertions to insert before the snapshot expect:
- expect(resolver).toMatchInlineSnapshot(` + // ensure helper imports for definePage are present + expect(resolver).toMatch(/^\s*import .*_definePage_default_0/m) + expect(resolver).toMatch(/^\s*import .*_mergeRouteRecordExperimental\b/m) + expect(resolver).toMatchInlineSnapshot(`
293-306
: Align fixture file paths with routes for readability.Many inserts here reuse 'b/c/d.vue' for unrelated routes. It doesn’t break these order-only tests, but aligning file paths with routes makes snapshots and future diffs easier to reason about.
25-188
: Add coverage for param matchers ([param=type]) in path/codegen.Given this PR introduces typed param matchers, include tests that assert the generated param config reflects the declared types (e.g., 'a/[id=int]') and that ParamParsersMap integration shows up in the output.
I can draft targeted tests once you confirm the expected serialized shape for parser metadata in the generated code.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
package.json
(2 hunks)playground-experimental/package.json
(1 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/core/tree.ts
(3 hunks)src/runtime.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- package.json
- playground-experimental/package.json
- src/core/tree.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.231Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
📚 Learning: 2025-08-15T16:11:02.610Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.610Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.spec.ts
🔇 Additional comments (1)
src/codegen/generateRouteResolver.spec.ts (1)
548-560
: Nice, robust order extraction.Parsing the comment markers to derive ordering keeps the test resilient to alignment changes elsewhere.
43e7dfb
to
0846945
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
♻️ Duplicate comments (3)
src/codegen/generateRouteResolver.spec.ts (2)
242-242
: Fix mismatched test fixture path for '/b/e/f'.The route '/b/e/f' should point to 'b/e/f.vue', not 'b/c/f.vue'. This looks like a regression of the earlier fix.
- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')Also applies to: 339-339
279-279
: Update inline snapshots to match the corrected path for '/b/e/f'.Adjust the import path in snapshots after fixing the fixture path.
- 'default': () => import('b/c/f.vue') + 'default': () => import('b/e/f.vue')Also applies to: 376-376
src/core/context.ts (1)
216-227
: Don’t access watcher.options; use the passed cwd. Also drop the './' prefix from relativePath.
- FSWatcher.options isn’t a public API; avoid relying on it.
- relativePath should be clean (codegen already prepends './' in imports), otherwise you’ll get '././...' in generated DTS.
- function setupParamParserWatcher(watcher: FSWatcher, cwd: string) { - logger.log(`🤖 Scanning param parsers in ${watcher.options.cwd}`) + function setupParamParserWatcher(watcher: FSWatcher, cwd: string) { + logger.log(`🤖 Scanning param parsers in ${cwd}`) return watcher .on('add', (file) => { const name = parsePathe(file).name - const absolutePath = resolve(cwd, file) + const absolutePath = resolve(cwd, file) paramParsers.set(name, { name, typeName: `Param_${name}`, absolutePath, - relativePath: './' + relative(options.root, absolutePath), + relativePath: relative(options.root, absolutePath), }) writeConfigFiles() })
🧹 Nitpick comments (2)
src/core/context.ts (2)
71-73
: Broaden param parser glob (optional).If you plan to support common JS/TS variants, consider matching tsx/jsx/mjs/cjs as well.
- const PARAM_PARSER_GLOB = '*.{ts,js}' + const PARAM_PARSER_GLOB = '*.{ts,tsx,js,jsx,mjs,cjs}'
355-356
: Typo in TODO.Fix “copmlete” → “complete”.
- // TODO: parser auto copmlete for definePage + // TODO: parser auto complete for definePage
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
playground/tsconfig.json
(2 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/core/context.ts
(10 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-15T16:11:02.610Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.610Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.spec.ts
src/core/context.ts
📚 Learning: 2025-08-16T13:00:51.231Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.231Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
src/core/context.ts
🧬 Code Graph Analysis (2)
src/codegen/generateRouteResolver.spec.ts (7)
src/options.ts (2)
DEFAULT_OPTIONS
(249-266)resolveOptions
(313-395)src/index.ts (1)
DEFAULT_OPTIONS
(29-29)src/codegen/generateRouteResolver.ts (3)
generateRouteRecord
(103-208)generateRouteRecordPath
(234-256)generateRouteResolver
(62-98)src/codegen/generateRouteRecords.ts (1)
generateRouteRecord
(15-110)src/core/utils.ts (1)
ImportsMap
(299-368)src/core/tree.ts (1)
PrefixTree
(390-424)src/codegen/generateParamParsers.ts (1)
ParamParsersMap
(7-15)
src/core/context.ts (10)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(7-15)warnMissingParamParsers
(17-32)generateParamParsersTypesDeclarations
(57-66)src/core/tree.ts (1)
name
(243-250)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(62-98)src/utils/index.ts (1)
ts
(18-18)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/core/moduleConstants.ts (1)
MODULE_ROUTES_PATH
(6-6)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
⏰ 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: Socket Security: Pull Request Alerts
- GitHub Check: Cloudflare Pages
🔇 Additional comments (7)
playground/tsconfig.json (4)
15-15
: Path alias @/ looks good*Standard Vue alias to src. No concerns.
26-27
: No-op formatting change in paths blockClosing bracket/comma adjustments only. Safe.
8-8
: AutoImport dts path correct
- playground/auto-imports.d.ts exists.
- No custom
dts
option in vite.config.ts → plugin uses defaultauto-imports.d.ts
.TS will resolve ambient types as expected.
16-17
: Add or align Vite resolve.alias mappings for TS path overridesI didn’t find matching
resolve.alias
entries inplayground/vite.config.(js|ts)
, so your TS paths:
unplugin-vue-router/runtime
→../src/runtime.ts
unplugin-vue-router/types
→../src/types.ts
won’t apply at runtime.
Action items:
- In
playground/vite.config.ts
, underresolve.alias
, add:import { resolve } from 'path' export default defineConfig({ // … resolve: { alias: { 'unplugin-vue-router/runtime': resolve(__dirname, '../src/runtime.ts'), 'unplugin-vue-router/types': resolve(__dirname, '../src/types.ts'), } } })- Or remove/limit these TS path overrides if you intend to use the published package entry points.
- Double-check the published
unplugin-vue-router
exports (e.g. vianpm view unplugin-vue-router exports
) to confirm your aliases match the official entry-points.src/codegen/generateRouteResolver.spec.ts (2)
548-679
: Route prioritization coverage looks solid.Good breadth across static, dynamic, optional, repeatable, catch-all, nested, and complex subsegments. This should guard regressions in scoring and resolver ordering.
523-537
: No action needed: merge helper import is verifiedThe
_mergeRouteRecordExperimental
helper is already added viaimportsMap.add('unplugin-vue-router/runtime', '_mergeRouteRecordExperimental')
insrc/codegen/generateRouteResolver.ts
(line 270), alongside the other experimental helpers. The generated resolver will compile as expected.src/core/context.ts (1)
257-278
: New generateResolver entrypoint: LGTM.Clean integration with ImportsMap and paramParsers. The separation from generateRoutes is clear.
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
♻️ Duplicate comments (5)
src/utils/index.ts (1)
1-1
: Confirm Node engine; consider droppingconstants
import for wider compatibility
constants
fromnode:fs/promises
requires Node ≥ 18.4. If engines allow earlier 18.x, either importconstants
fromnode:fs
or avoid passingF_OK
(default), which lets you remove theconstants
import entirely.Apply this import-only simplification:
-import { access, constants } from 'node:fs/promises' +import { access } from 'node:fs/promises'Run to verify engines:
#!/bin/bash set -euo pipefail echo "Scanning package.json files for engines.node…" fd -a package.json -E node_modules | while read -r f; do echo "== $f ==" jq -r '.name // "(no name)"' "$f" jq -r '.engines?.node // "(no engines.node)"' "$f" echo done echo "Checking optional Node version files…" fd -a '.nvmrc' '.node-version' -E node_modules | while read -r f; do echo "== $f ==" cat "$f" echo donesrc/codegen/generateRouteResolver.spec.ts (2)
242-242
: Fix test fixture path inconsistency.The route path
b/e/f
should use matching file pathb/e/f.vue
instead ofb/c/f.vue
.- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')
649-649
: Fix nested route fixture path.The nested route
api/v1/[type]/c
should point to its own component file.- tree.insert('api/v1/[type]/c', 'api/v1/[type]/c.vue') + tree.insert('api/v1/[type]/c', 'api/v1/[type]/c.vue')src/codegen/generateRouteResolver.ts (1)
93-93
: Prevent negative repeat count error.The padding calculation can produce negative values when
varName.length
exceedsString(state.id).length + 2
.- ` ${varName}, ${' '.repeat(String(state.id).length - varName.length + 2)}// ${path}` + ` ${varName},${' '.repeat(Math.max(0, String(state.id).length - varName.length + 2))}// ${path}`playground-experimental/src/pages/users/[userId=int].vue (1)
5-16
: Remove unsupportedpath
in experimental definePageThe experimental definePage only accepts name, params, meta, and redirect. Drop path to avoid TS errors and confusion.
Apply:
definePage({ - path: '/users/:userId', // query: { // page: { // type: 'int', // optional: true, // }, // }, meta: { // te: 3, 2 }, })
🧹 Nitpick comments (7)
pnpm-workspace.yaml (1)
2-3
: Optional: collapse explicit entries into a single globUsing a glob reduces future edits if more playground variants are added.
Apply this diff:
- - playground - - playground-experimental + - playground*src/utils/index.ts (1)
20-27
: MakefileExists
portable and explicit (type + defaultF_OK
)No behavior change, but this removes an unnecessary flag and improves typing.
-export async function fileExists(filePath: string) { +export async function fileExists( + filePath: import('node:fs').PathLike +): Promise<boolean> { try { - await access(filePath, constants.F_OK) + // F_OK is the default + await access(filePath) return true } catch { return false } }playground-experimental/src/pages/users/[userId=int].vue (1)
2-3
: Remove or use the unusedroute
expression
route
is declared but not used; the standaloneroute.params.userId
expression has no effect. Either remove both lines and rely on$route
in the template, or bind a value you actually render.Apply (minimal cleanup):
-const route = useRoute() -route.params.userIdOptionally, if you prefer using
useRoute()
in the template:+const route = useRoute()
and switch the template to
{{ String(route.name) }} - {{ route.path }}
.src/codegen/generateRouteMap.spec.ts (2)
22-24
: Type the paramParsersMap and avoid repeatingnew Map()
everywhere.Use a tiny helper returning a properly typed map to improve readability and type-safety.
Apply this change to each occurrence within the selected ranges:
- formatExports(generateRouteNamedMap(tree, DEFAULT_OPTIONS, new Map())) + formatExports( + generateRouteNamedMap(tree, DEFAULT_OPTIONS, createParamParsersMap()) + )Add this once near the top of the file:
import type { ParamParsersMap } from './generateParamParsers' const createParamParsersMap = (): ParamParsersMap => new Map()Also applies to: 45-47, 68-70, 84-86, 104-106, 128-130, 149-151, 171-173, 189-191, 209-211, 226-228, 240-242, 257-259, 281-283, 297-299, 311-313, 325-327, 345-347, 379-381, 400-402
128-130
: Pass the same resolved options used to build the tree.These two tests build the tree with custom options but call the generator with
DEFAULT_OPTIONS
. Pass the sameoptions
to avoid future drift if generation logic depends on options.Apply this diff at the call sites:
- formatExports(generateRouteNamedMap(tree, DEFAULT_OPTIONS, new Map())) + formatExports(generateRouteNamedMap(tree, options, new Map()))And adjust the setup in each test to keep a reference to the resolved options (outside the selected lines; example):
// before const tree = new PrefixTree( resolveOptions({ routesFolder: [{ src: 'src/pages', path: ':lang/' }], }) ) // after const options = resolveOptions({ routesFolder: [{ src: 'src/pages', path: ':lang/' }], }) const tree = new PrefixTree(options)Same pattern for the test using
[lang]/
.Also applies to: 281-283
src/codegen/generateRouteParams.ts (2)
24-27
: Add explicit return type and tighten thetypes
param.Make the API clearer and immutable at the boundary.
-export function EXPERIMENTAL_generateRouteParams( - node: TreeNode, - types: Array<string | null> -) { +export function EXPERIMENTAL_generateRouteParams( + node: TreeNode, + types: ReadonlyArray<string | null> +): string {
34-34
: Minor: avoid re-declaringisRaw
inside the map.Hoist
const isRaw = false
outside the.map()
to avoid per-iteration redeclaration. Tiny win, clearer intent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
src/codegen/__snapshots__/generateRouteRecords.spec.ts.snap
is excluded by!**/*.snap
📒 Files selected for processing (57)
client.d.ts
(1 hunks)examples/nuxt/package.json
(1 hunks)package.json
(2 hunks)playground-experimental/.gitignore
(1 hunks)playground-experimental/auto-imports.d.ts
(1 hunks)playground-experimental/db.json
(1 hunks)playground-experimental/env.d.ts
(1 hunks)playground-experimental/index.html
(1 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/App.vue
(1 hunks)playground-experimental/src/main.ts
(1 hunks)playground-experimental/src/page-outside.vue
(1 hunks)playground-experimental/src/pages/(home).vue
(1 hunks)playground-experimental/src/pages/[...404].vue
(1 hunks)playground-experimental/src/pages/a.[b].c.[d].vue
(1 hunks)playground-experimental/src/pages/b.vue
(1 hunks)playground-experimental/src/pages/events/[when=date].vue
(1 hunks)playground-experimental/src/pages/u[name].vue
(1 hunks)playground-experimental/src/pages/u[name]/24.vue
(1 hunks)playground-experimental/src/pages/u[name]/[userId=int].vue
(1 hunks)playground-experimental/src/pages/users/[userId=int].vue
(1 hunks)playground-experimental/src/pages/users/sub-[first]-[second].vue
(1 hunks)playground-experimental/src/params/date.ts
(1 hunks)playground-experimental/src/router.ts
(1 hunks)playground-experimental/tsconfig.config.json
(1 hunks)playground-experimental/tsconfig.json
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)playground-experimental/vite.config.ts
(1 hunks)playground/src/pages/(some-layout).vue
(1 hunks)playground/src/params/date.ts
(1 hunks)playground/src/params/number.ts
(1 hunks)playground/tsconfig.json
(2 hunks)playground/vite.config.ts
(2 hunks)pnpm-workspace.yaml
(1 hunks)src/codegen/generateDTS.ts
(2 hunks)src/codegen/generateParamParsers.ts
(1 hunks)src/codegen/generateRouteMap.spec.ts
(20 hunks)src/codegen/generateRouteMap.ts
(1 hunks)src/codegen/generateRouteParams.ts
(1 hunks)src/codegen/generateRouteRecords.ts
(2 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/context.ts
(10 hunks)src/core/extendRoutes.spec.ts
(9 hunks)src/core/extendRoutes.ts
(1 hunks)src/core/moduleConstants.ts
(2 hunks)src/core/tree.spec.ts
(2 hunks)src/core/tree.ts
(3 hunks)src/core/treeNodeValue.ts
(11 hunks)src/data-loaders/meta-extensions.ts
(1 hunks)src/index.ts
(5 hunks)src/options.ts
(3 hunks)src/runtime.ts
(1 hunks)src/utils/index.ts
(2 hunks)tsconfig.json
(2 hunks)vitest.config.ts
(1 hunks)vitest.workspace.js
(0 hunks)
💤 Files with no reviewable changes (1)
- vitest.workspace.js
✅ Files skipped from review due to trivial changes (2)
- playground-experimental/db.json
- playground-experimental/index.html
🚧 Files skipped from review as they are similar to previous changes (46)
- playground-experimental/src/pages/b.vue
- playground-experimental/.gitignore
- src/core/extendRoutes.ts
- playground-experimental/src/pages/users/sub-[first]-[second].vue
- playground-experimental/env.d.ts
- playground-experimental/src/page-outside.vue
- playground/vite.config.ts
- client.d.ts
- playground-experimental/src/pages/u[name].vue
- playground-experimental/src/main.ts
- playground/src/params/number.ts
- playground-experimental/src/pages/[...404].vue
- playground-experimental/src/router.ts
- playground-experimental/src/pages/events/[when=date].vue
- src/data-loaders/meta-extensions.ts
- examples/nuxt/package.json
- src/codegen/generateRouteRecords.ts
- playground-experimental/tsconfig.json
- playground-experimental/package.json
- playground-experimental/src/pages/u[name]/24.vue
- playground-experimental/auto-imports.d.ts
- playground/src/pages/(some-layout).vue
- src/core/tree.ts
- playground-experimental/src/pages/u[name]/[userId=int].vue
- src/core/tree.spec.ts
- playground/src/params/date.ts
- playground-experimental/src/pages/(home).vue
- src/core/context.ts
- playground-experimental/src/App.vue
- playground-experimental/src/pages/a.[b].c.[d].vue
- playground-experimental/tsconfig.config.json
- src/runtime.ts
- playground-experimental/src/params/date.ts
- src/options.ts
- tsconfig.json
- src/core/extendRoutes.spec.ts
- package.json
- src/index.ts
- vitest.config.ts
- src/core/treeNodeValue.ts
- src/codegen/generateParamParsers.ts
- src/core/moduleConstants.ts
- src/codegen/generateDTS.ts
- playground/tsconfig.json
- src/codegen/generateRouteMap.ts
- playground-experimental/vite.config.ts
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.231Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
📚 Learning: 2025-08-16T12:19:37.722Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/utils/index.ts:1-1
Timestamp: 2025-08-16T12:19:37.722Z
Learning: In Node.js, the `fs/promises` module exports `constants` which contains file system constants like `F_OK`, `R_OK`, `W_OK`, and `X_OK`. The import `import { access, constants } from 'fs/promises'` is valid and `constants.F_OK` can be used with the `access()` function.
Applied to files:
src/utils/index.ts
📚 Learning: 2025-08-16T12:19:37.722Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/utils/index.ts:1-1
Timestamp: 2025-08-16T12:19:37.722Z
Learning: The `constants` export from Node.js `fs/promises` module is version-dependent: it's available in Node.js 18.4.0 and above, but not in earlier versions. In older Node.js versions, `constants` must be imported from `node:fs` instead. When reviewing Node.js fs API usage, always consider the project's minimum Node.js version requirements.
Applied to files:
src/utils/index.ts
📚 Learning: 2025-08-15T16:11:02.610Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.610Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.ts
src/codegen/generateRouteResolver.spec.ts
📚 Learning: 2025-08-16T13:01:42.699Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.699Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
playground-experimental/typed-router.d.ts
🧬 Code Graph Analysis (5)
src/codegen/generateRouteMap.spec.ts (2)
src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/options.ts (1)
DEFAULT_OPTIONS
(249-266)
src/codegen/generateRouteResolver.ts (8)
src/core/tree.ts (5)
PrefixTree
(390-424)path
(222-227)TreeNode
(20-384)name
(243-250)params
(273-285)src/options.ts (1)
ResolvedOptions
(400-400)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsOptions
(102-117)src/codegen/generateRouteRecords.ts (3)
generateRouteRecord
(15-110)formatMeta
(168-178)generatePageImport
(145-166)src/utils/index.ts (1)
ts
(18-18)src/core/treeNodeValue.ts (1)
path
(74-76)src/core/extendRoutes.ts (1)
children
(192-196)
src/codegen/generateRouteResolver.spec.ts (6)
src/options.ts (2)
DEFAULT_OPTIONS
(249-266)resolveOptions
(313-395)src/index.ts (1)
DEFAULT_OPTIONS
(29-29)src/codegen/generateRouteResolver.ts (3)
generateRouteRecord
(103-208)generateRouteRecordPath
(234-256)generateRouteResolver
(62-98)src/codegen/generateRouteRecords.ts (1)
generateRouteRecord
(15-110)src/core/utils.ts (1)
ImportsMap
(299-368)src/core/tree.ts (1)
PrefixTree
(390-424)
src/codegen/generateRouteParams.ts (1)
src/core/tree.ts (1)
TreeNode
(20-384)
playground-experimental/typed-router.d.ts (2)
playground-experimental/src/router.ts (1)
RouteNamedMap
(16-31)src/codegen/generateRouteParams.ts (1)
ParamValue
(83-85)
🔇 Additional comments (5)
pnpm-workspace.yaml (1)
3-3
: LGTM: workspace includes the new playground-experimental packageThe addition correctly brings the new playground into the monorepo.
playground-experimental/typed-router.d.ts (3)
8-10
: Param_date type derivation is correctDeriving the param type from the parser.get return type is sound and resilient to parser changes. This matches the param-parsers pipeline. Based on the retrieved learning, keeping the relative import path here (without extra normalization) is the right call since normalization is handled elsewhere.
11-35
: RouteNamedMap entries look consistent with file-based routes and param matchersStatic entries and the typed int/date params line up (e.g., number for userId, Param_date for when). Interface merging with dynamic routes declared in src/router.ts should work as intended.
1-7
: playground-experimental/typed-router.d.ts is already included in tsconfigThe include array in playground-experimental/tsconfig.json already lists
./typed-router.d.ts
, so no changes are needed.src/codegen/generateRouteMap.spec.ts (1)
22-24
: API migration to the new signature looks good.Calls now pass options and a fresh Map; snapshots remain stable. No issues spotted here.
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
♻️ Duplicate comments (1)
src/codegen/generateRouteResolver.ts (1)
92-94
: Prevent negative repeat while keeping aligned commentsWrap the repeat count to avoid a potential runtime RangeError if the naming scheme ever changes. Alignment is preserved.
- ({ varName, path }) => - ` ${varName}, ${' '.repeat(String(state.id).length - varName.length + 2)}// ${path}` + ({ varName, path }) => + ` ${varName}, ${' '.repeat(Math.max(0, String(state.id).length - varName.length + 2))}// ${path}`
🧹 Nitpick comments (3)
src/codegen/generateRouteResolver.ts (3)
88-96
: Nit: precompute max digits onceMicro-optimization: compute
const maxDigits = String(state.id).length
once before the template and reuse it in the mapper to avoid recomputing per item.
216-229
: Ensure deterministic components orderSort component entries for stable codegen across environments.
- const files = Array.from(node.value.components) + const files = Array.from(node.value.components).sort(([a], [b]) => + a.localeCompare(b) + )
246-256
: Remove unused local and inline the checkMinor cleanup;
params
local is unused.- const params = node.params - if (params.length > 0) { + if (node.params.length > 0) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
package.json
(2 hunks)playground-experimental/package.json
(1 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- playground-experimental/package.json
- src/codegen/generateRouteResolver.spec.ts
- package.json
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-15T16:11:02.610Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.610Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.ts
🔇 Additional comments (3)
src/codegen/generateRouteResolver.ts (3)
13-51
: LGTM: route scoring comparator is clear and deterministicThe tie-breakers (min, avg, subsegment count, then segment count) make sense and yield a stable ordering.
127-141
: Verify definePage import query stringI didn’t find any existing loader logic for
?definePage&vue&lang.tsx
. Please confirm that your bundler/parser handles?definePage&vue&lang.tsx
as intended (versus using a simpler query like?definePage&lang.tsx
or?definePage&lang=ts
).Location to check:
- src/codegen/generateRouteResolver.ts (lines 127–141)
120-124
: No action needed: definePage-only nodes aren’t skipped
The skip guard only excludes nodes that have neither a component nor meta. Every page node—including those with onlydefinePage
—always has at least one component (isMatchable() === true
), soshouldSkipNode
stays false.
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: 3
♻️ Duplicate comments (4)
playground-experimental/vite.config.ts (2)
78-82
: __dirname is undefined in ESM Vite config; use import.meta.url.Replace usage to avoid runtime error.
- root.insert( - '/manually-added', - join(__dirname, './src/page-outside.vue') - ) + root.insert( + '/manually-added', + fileURLToPath(new URL('./src/page-outside.vue', import.meta.url)) + )
96-102
: Fix features path transform; current replace doesn’t remove “pages/”.The relative path starts with "/pages/…", so
^pages/
never matches. Normalize separators and drop the intermediate "pages/" segment.{ src: 'src/features', filePatterns: '*/pages/**/*', path: (file) => { - return relative('src/features', file).replace(/^pages\//, '') + const rel = relative('src/features', file).replace(/\\/g, '/') + // keep "<feature>/" and drop the intermediate "pages/" + return rel.replace(/^([^/]+)\/pages\//, '$1/') }, },src/runtime.ts (1)
90-94
: Remove placeholder URL from public TSDoc.Avoid shipping a broken link in typings.
- * The type of the query parameter. Allowed values are native param parsers - * and any parser in the {@link https://uvr.esm.is/TODO | params folder }. If + * The type of the query parameter. Allowed values are native param parsers + * and any parser in the params folder. Ifsrc/core/context.ts (1)
216-218
: Avoid accessing privatewatcher.options
; use passedcwd
.
FSWatcher
doesn’t expose.options
in its public type. This is a TS error and a runtime hazard.Apply this diff:
- function setupParamParserWatcher(watcher: FSWatcher, cwd: string) { - logger.log(`🤖 Scanning param parsers in ${watcher.options.cwd}`) + function setupParamParserWatcher(watcher: FSWatcher, cwd: string) { + logger.log(`🤖 Scanning param parsers in ${cwd}`)
🧹 Nitpick comments (8)
playground-experimental/src/pages/[...path].vue (1)
4-6
: Remove unused route access.This no-op access to route params isn’t used and may trip linters.
-const route = useRoute() -route.params.path +src/codegen/generateRouteMap.ts (2)
3-7
: Type-only import for ParamParsersMap.Prevents bundling type metadata.
-import { generateParamsTypes, ParamParsersMap } from './generateParamParsers' +import { generateParamsTypes } from './generateParamParsers' +import type { ParamParsersMap } from './generateParamParsers'
46-49
: Optional: defensive check on experimental flag.Safer if callers pass a partially constructed options object.
- if (options.experimental.paramParsers) { + if (options.experimental?.paramParsers) { paramParsers = generateParamsTypes(params, paramParsersMap) paramType = EXPERIMENTAL_generateRouteParams(node, paramParsers) }src/core/context.ts (5)
71-73
: Broaden glob to support nested folders and common JS/TS variants.Current pattern only matches top-level .ts/.js files. Consider supporting nested dirs and TS/JS variants to reduce surprises.
Apply this diff:
- const PARAM_PARSER_GLOB = '*.{ts,js}' + const PARAM_PARSER_GLOB = '**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}' const isParamParserMatch = picomatch(PARAM_PARSER_GLOB)
133-143
: Warn on duplicate parser names to avoid silent overwrites.Two files named the same (e.g., index.ts in different dirs) will collide on
name
and silently overwrite in the map.Apply this diff:
for (const file of paramParserFiles) { const name = parsePathe(file).name // TODO: could be simplified to only one import that starts with / for vite const absolutePath = resolve(folder, file) + if (paramParsers.has(name)) { + logger.warn( + `Duplicate param parser "${name}" at ${absolutePath} overwrites ${paramParsers.get(name)!.absolutePath}` + ) + } paramParsers.set(name, { name, typeName: `Param_${name}`, absolutePath, relativePath: relative(options.root, absolutePath), }) }
219-228
: NormalizerelativePath
(remove redundant './') and guard duplicates on add.
generateParamParsersTypesDeclarations
already prepends './' in imports; adding it here leads to '././...' paths. Also mirror duplicate-name warning on add.Apply this diff:
.on('add', (file) => { const name = parsePathe(file).name const absolutePath = resolve(cwd, file) + if (paramParsers.has(name)) { + logger.warn( + `Duplicate param parser "${name}" at ${absolutePath} overwrites ${paramParsers.get(name)!.absolutePath}` + ) + } paramParsers.set(name, { name, typeName: `Param_${name}`, absolutePath, - relativePath: './' + relative(options.root, absolutePath), + relativePath: relative(options.root, absolutePath), }) writeConfigFiles() })
337-344
: Safer guard before warning about missing param parsers.Avoid assuming
dir
exists. Optional-chain to prevent runtime errors.Apply this diff:
- if ( - options.experimental.paramParsers && - options.experimental.paramParsers.dir.length > 0 - ) { + if (options.experimental.paramParsers?.dir?.length) { warnMissingParamParsers(routeTree, paramParsers) }
355-357
: Fix typo in TODO.Small spelling fix.
Apply this diff:
- // TODO: parser auto copmlete for definePage + // TODO: parser autocomplete for definePage
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (13)
package.json
(2 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/pages/[...path].vue
(1 hunks)playground-experimental/src/pages/b.vue
(1 hunks)playground-experimental/src/pages/users/[userId=int].vue
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)playground-experimental/vite.config.ts
(1 hunks)playground/vite.config.ts
(2 hunks)src/codegen/generateRouteMap.ts
(1 hunks)src/core/context.ts
(10 hunks)src/core/extendRoutes.ts
(1 hunks)src/options.ts
(3 hunks)src/runtime.ts
(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/core/extendRoutes.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- playground-experimental/src/pages/users/[userId=int].vue
- playground-experimental/src/pages/b.vue
- playground-experimental/package.json
- playground/vite.config.ts
- playground-experimental/typed-router.d.ts
- package.json
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-16T13:00:51.231Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.231Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
playground-experimental/vite.config.ts
src/options.ts
src/core/context.ts
📚 Learning: 2025-08-15T16:11:02.610Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.610Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/core/context.ts
🧬 Code Graph Analysis (5)
src/codegen/generateRouteMap.ts (4)
src/core/tree.ts (2)
TreeNode
(20-384)params
(273-285)src/options.ts (1)
ResolvedOptions
(400-400)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsTypes
(68-82)src/codegen/generateRouteParams.ts (2)
EXPERIMENTAL_generateRouteParams
(24-50)generateRouteParams
(3-22)
playground-experimental/vite.config.ts (2)
scripts/release.mjs (1)
__dirname
(13-13)src/index.ts (1)
VueRouterAutoImports
(240-253)
src/runtime.ts (1)
client.d.ts (1)
TypesConfig
(35-37)
src/options.ts (1)
src/core/utils.ts (1)
isArray
(65-66)
src/core/context.ts (8)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(7-15)warnMissingParamParsers
(17-32)generateParamParsersTypesDeclarations
(57-66)src/core/utils.ts (1)
ImportsMap
(299-368)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(62-98)src/utils/index.ts (1)
ts
(18-18)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
🔇 Additional comments (8)
playground-experimental/src/pages/[...path].vue (1)
7-22
: LGTM: definePage meta and query param schema align with experimental param parsers.The query param config (parser/default/format) is consistent with the new types.
src/core/context.ts (7)
3-11
: LGTM on new imports.Using node:fs promises, pathe utilities, and MODULE_ROUTES_PATH is appropriate here.
24-31
: LGTM on resolver and param-parser related imports.Bringing in generateRouteResolver, chokidar FSWatcher, param parser helpers, and picomatch is consistent with the new functionality introduced.
56-57
: LGTM on watcher and paramParsers state.Union-typing watchers and introducing ParamParsersMap is clear and maintainable.
257-278
: LGTM: clean resolver generation entrypoint.Nice separation using ImportsMap + generateRouteResolver and returning a self-contained module string.
289-289
: LGTM on tagged template for HMR block.Using
ts
keeps indentation and avoids unintended escapes.
345-353
: LGTM: DTS now includes param parser types.Passing paramsTypesDeclaration and a paramParsers-aware RouteNamedMap looks consistent with the new feature.
416-418
: LGTM: expose generateResolver in public API.Matches the new experimental pathway.
const experimental = { | ||
...options.experimental, | ||
autoExportsDataLoaders, | ||
// keep undefined if paramParsers is not set | ||
paramParsers: paramParsers && { | ||
...paramParsers, | ||
dir: paramParsersDir, | ||
}, | ||
} |
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.
Guard object spread when experimental is undefined.
{ ...options.experimental }
throws if experimental
isn’t provided.
- const experimental = {
- ...options.experimental,
+ const experimental = {
+ ...(options.experimental || {}),
autoExportsDataLoaders,
// keep undefined if paramParsers is not set
paramParsers: paramParsers && {
...paramParsers,
dir: paramParsersDir,
},
}
🤖 Prompt for AI Agents
In src/options.ts around lines 349 to 357, the spread of options.experimental
can throw when options.experimental is undefined; change the construction to
spread a safe default (e.g., ...(options.experimental ?? {}) or
...(options?.experimental || {})) so the object spread never receives undefined,
then merge autoExportsDataLoaders and keep the existing conditional paramParsers
logic (paramParsers && { ...paramParsers, dir: paramParsersDir }) so
paramParsers remains undefined when not provided.
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: 6
♻️ Duplicate comments (4)
src/codegen/generateRouteResolver.ts (1)
89-97
: Guard repeat() and keep alignment (can throw at runtime)The padding can go negative when varName is longer than expected, causing
' '.repeat()
to throw. Compute max varName length and clamp.-export const resolver = createFixedResolver([ -${state.matchableRecords - .sort((a, b) => compareRouteScore(a.score, b.score)) - .map( - ({ varName, path }) => - ` ${varName}, ${' '.repeat(String(state.id).length - varName.length + 2)}// ${path}` - ) - .join('\n')} -]) +const maxVarNameLength = state.matchableRecords.reduce( + (m, r) => Math.max(m, r.varName.length), + 0 +) +export const resolver = createFixedResolver([ +${state.matchableRecords + .sort((a, b) => compareRouteScore(a.score, b.score)) + .map(({ varName, path }) => { + const pad = Math.max(0, maxVarNameLength - varName.length + 2) + return ` ${varName},${' '.repeat(pad)}// ${path}` + }) + .join('\n')} +])src/core/tree.ts (1)
318-319
: Resolved: static segments are now escapedUsing escapeRegex fixes the prior “regex metacharacters in static segments” issue. ✅
src/core/context.ts (2)
208-209
: Don’t read privatewatcher.options
; use thecwd
you already pass.
FSWatcher#options
isn’t part of the public API and isn’t typed. Log the explicitcwd
param.- logger.log(`🤖 Scanning param parsers in ${watcher.options.cwd}`) + logger.log(`🤖 Scanning param parsers in ${cwd}`)
109-150
: Fix crash:.map
on possibly undefinedparamParsers.dir
.If
experimental.paramParsers
is undefined,paramParsers?.dir.map(...)
still attempts.map
onundefined
(because the optional chaining only applies toparamParsers
). Guard with a fallback array.- ...(options.experimental.paramParsers?.dir.map((folder) => { + ...((options.experimental.paramParsers?.dir ?? []).map((folder) => { @@ - }) - }) || []), + }) + })),
🧹 Nitpick comments (13)
src/core/definePage.ts (3)
320-333
: Support array defaults for query paramsDefault values often need arrays (e.g., checkboxes). Add ArrayExpression handling to avoid silently ignoring valid configs.
- } else if (paramProp.key.name === 'default') { + } else if (paramProp.key.name === 'default') { // Handle different literal types for default values if (paramProp.value.type === 'NumericLiteral') { paramInfo.default = paramProp.value.value } else if (paramProp.value.type === 'StringLiteral') { paramInfo.default = paramProp.value.value } else if (paramProp.value.type === 'BooleanLiteral') { paramInfo.default = paramProp.value.value } else if (paramProp.value.type === 'NullLiteral') { paramInfo.default = null + } else if (paramProp.value.type === 'ArrayExpression') { + // Accept arrays of literals (numbers, strings, booleans, null) + paramInfo.default = paramProp.value.elements + .filter((el): el is any => !!el && el.type !== 'SpreadElement') + .map((el: any) => (el.type === 'NullLiteral' ? null : el.value)) } // TODO: handle function expressions for default values }
369-377
: Clarify alias warning messageThe validator accepts a string or an array of strings, but the warning mentions only “string literal”. Align the message with behavior.
- warn(`route alias must be a string literal. Found in "${id}".`) + warn( + `route alias must be a string literal or an array of string literals. Found in "${id}".` + )
406-413
: Fix typo in JSDoc“filtere” → “filtered”. Minor readability polish.
-/** - * Generate a filtere import statement based on a set of identifiers that should be kept. +/** + * Generate a filtered import statement based on a set of identifiers to keep.src/codegen/generateRouteResolver.ts (1)
246-255
: Prefer pathParams for clarityUse the dedicated path params accessor; avoids confusion with any future query params handling.
- const params = node.params + const params = node.pathParams ... - ${generateParamsOptions(node.params, importsMap, paramParsersMap)}, + ${generateParamsOptions(params, importsMap, paramParsersMap)},Also applies to: 250-251
src/core/utils.ts (1)
202-212
: Optionally omit empty params blocksIf both
path
andquery
end up empty, consider skippingparams
entirely to reduce noise in overrides.- merged[key] = { path, query } + merged[key] = + Object.keys(path).length || Object.keys(query).length + ? { path, query } + : merged[key]src/core/vite/index.ts (2)
10-17
: Normalize return type of invalidate to voidCurrent implementation returns
Promise<void> | boolean
. ServerContext.invalidate is typed as() => void
. Don’t return values; fire-and-forget the reload.- function invalidate(path: string) { + function invalidate(path: string): void { const foundModule = server.moduleGraph.getModuleById(path) // console.log(`🟣 Invalidating module: ${path}, found: ${!!foundModule}`) - if (foundModule) { - return server.reloadModule(foundModule) - } - return !!foundModule + if (foundModule) { + void server.reloadModule(foundModule) + } }
26-31
: Docstring: covers both routes and resolverReflects actual behavior of reloading two virtual modules.
- /** - * Triggers HMR for the vue-router/auto-routes module. - */ + /** + * Triggers HMR for the routes and resolver virtual modules. + */src/core/tree.ts (1)
293-305
: pathParams duplicates params logicCurrently identical to params. Either delegate to params for now or document planned divergence to avoid future drift.
Apply this diff:
- get pathParams(): TreeRouteParam[] { - const params = this.value.isParam() ? [...this.value.params] : [] - let node = this.parent - // add all the params from the parents - while (node) { - if (node.value.isParam()) { - params.unshift(...node.value.params) - } - node = node.parent - } - - return params - } + get pathParams(): TreeRouteParam[] { + return this.params + }src/core/treeNodeValue.ts (1)
241-255
: Broaden escaping to include '|'escapeRegex misses '|', a regex metacharacter. Add it to REGEX_CHARS_RE (standard pattern).
Apply this diff:
-const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g +const REGEX_CHARS_RE = /[.*+?^${}()|[\]\\]/gsrc/core/context.ts (4)
71-73
: Broaden the glob to cover common module extensions.Support TSX/JSX and node ESM/CJS variants for param parsers.
- const PARAM_PARSER_GLOB = '*.{ts,js}' + const PARAM_PARSER_GLOB = '*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}'
274-301
: Remove unreachable HMR code (earlyinvalidate()
+return
).Everything after the
return
is dead. Keep a minimal accept that forces reload until live swapping is implemented.-if (import.meta.hot) { - import.meta.hot.accept((mod) => { - console.log('🔥 HMRRRR') - import.meta.hot.invalidate('[unplugin-vue-router:HMR] reloading resolver') - return - const router = import.meta.hot.data.router - if (!router) { - import.meta.hot.invalidate('[unplugin-vue-router:HMR] Cannot replace the resolver because there is no active router. Reloading.') - return - } - // TODO: - router._hmrReplaceResolver(mod.resolver) - // call the hotUpdateCallback for custom updates - import.meta.hot.data.router_hotUpdateCallback?.(mod.routes) - const route = router.currentRoute.value - router.replace({ - ...route, - // NOTE: we should be able to just do ...route but the router - // currently skips resolving and can give errors with renamed routes - // so we explicitly set remove matched and name - name: undefined, - matched: undefined, - params: {}, - force: true - }) - }) -} +if (import.meta.hot) { + import.meta.hot.accept(() => { + import.meta.hot.invalidate('[unplugin-vue-router:HMR] reloading resolver') + }) +}
133-145
: Detect duplicate param-parser names during initial scan.Basename collisions across
dir[]
silently override previous entries. Warn to aid debugging.for (const file of paramParserFiles) { const name = parsePathe(file).name // TODO: could be simplified to only one import that starts with / for vite const absolutePath = resolve(folder, file) + if (paramParsers.has(name)) { + logger.warn( + `Duplicate param parser "${name}" at ${absolutePath} overrides ${paramParsers.get(name)!.absolutePath}.` + ) + } paramParsers.set(name, { name, typeName: `Param_${name}`, absolutePath, relativePath: relative(dts ? dirname(dts) : options.root, absolutePath), }) }
211-221
: Mirror duplicate-name detection in the watcher.Prevents silent overrides on file adds after startup.
.on('add', (file) => { const name = parsePathe(file).name const absolutePath = resolve(cwd, file) + if (paramParsers.has(name) && paramParsers.get(name)!.absolutePath !== absolutePath) { + logger.warn( + `Duplicate param parser "${name}" at ${absolutePath} overrides ${paramParsers.get(name)!.absolutePath}.` + ) + } paramParsers.set(name, { name, typeName: `Param_${name}`, absolutePath, - relativePath: './' + relative(options.root, absolutePath), + relativePath: relative(dts ? dirname(dts) : options.root, absolutePath), }) writeConfigFiles() })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
src/core/__snapshots__/definePage.spec.ts.snap
is excluded by!**/*.snap
📒 Files selected for processing (16)
playground-experimental/src/App.vue
(1 hunks)playground-experimental/src/pages/(home).vue
(1 hunks)playground-experimental/src/pages/[...path].vue
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)src/codegen/generateRouteMap.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/context.ts
(12 hunks)src/core/customBlock.ts
(2 hunks)src/core/definePage.spec.ts
(10 hunks)src/core/definePage.ts
(4 hunks)src/core/tree.ts
(4 hunks)src/core/treeNodeValue.ts
(13 hunks)src/core/utils.ts
(1 hunks)src/core/vite/index.ts
(2 hunks)src/index.ts
(6 hunks)src/runtime.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- playground-experimental/src/App.vue
- playground-experimental/src/pages/[...path].vue
- src/runtime.ts
- playground-experimental/typed-router.d.ts
- playground-experimental/src/pages/(home).vue
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-16T13:01:42.709Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.709Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
src/index.ts
src/core/treeNodeValue.ts
📚 Learning: 2025-08-15T16:11:02.627Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.627Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.ts
src/core/context.ts
src/codegen/generateRouteMap.ts
src/core/treeNodeValue.ts
📚 Learning: 2025-08-16T13:00:51.271Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.271Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
src/core/context.ts
🧬 Code graph analysis (10)
src/core/customBlock.ts (1)
src/runtime.ts (1)
DefinePage
(52-75)
src/core/vite/index.ts (2)
src/options.ts (1)
ServerContext
(268-272)src/core/moduleConstants.ts (3)
asVirtualId
(43-45)MODULE_ROUTES_PATH
(6-6)MODULE_RESOLVER_PATH
(7-7)
src/core/definePage.spec.ts (3)
src/core/context.ts (1)
definePageTransform
(452-457)src/core/definePage.ts (2)
definePageTransform
(65-188)extractDefinePageInfo
(198-259)src/utils/index.ts (1)
ts
(18-18)
src/index.ts (1)
src/core/moduleConstants.ts (6)
MODULE_RESOLVER_PATH
(7-7)routeBlockQueryRE
(41-41)MODULE_ROUTES_PATH
(6-6)MODULE_VUE_ROUTER_AUTO
(4-4)VIRTUAL_PREFIX
(26-26)ROUTES_LAST_LOAD_TIME
(14-21)
src/codegen/generateRouteResolver.ts (6)
src/core/tree.ts (6)
PrefixTree
(405-439)path
(222-227)TreeNode
(20-399)isMatchable
(371-375)name
(243-250)params
(276-288)src/options.ts (1)
ResolvedOptions
(400-400)src/core/utils.ts (1)
ImportsMap
(310-379)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsOptions
(102-117)src/codegen/generateRouteRecords.ts (3)
generateRouteRecord
(15-110)formatMeta
(168-178)generatePageImport
(145-166)src/utils/index.ts (1)
ts
(18-18)
src/core/definePage.ts (3)
src/runtime.ts (2)
DefinePage
(52-75)ParamParserType
(79-83)src/core/tree.ts (2)
params
(276-288)pathParams
(293-305)src/core/extendRoutes.ts (1)
params
(177-179)
src/core/context.ts (9)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(7-15)warnMissingParamParsers
(17-32)generateParamParsersTypesDeclarations
(57-66)src/core/definePage.ts (1)
extractDefinePageInfo
(198-259)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(62-98)src/utils/index.ts (1)
ts
(18-18)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/core/moduleConstants.ts (1)
MODULE_ROUTES_PATH
(6-6)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
src/codegen/generateRouteMap.ts (4)
src/core/tree.ts (1)
TreeNode
(20-399)src/options.ts (1)
ResolvedOptions
(400-400)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(7-15)generateParamsTypes
(68-82)src/codegen/generateRouteParams.ts (2)
EXPERIMENTAL_generateRouteParams
(24-50)generateRouteParams
(3-22)
src/core/treeNodeValue.ts (3)
src/runtime.ts (1)
DefinePage
(52-75)src/core/tree.ts (1)
params
(276-288)src/core/extendRoutes.ts (1)
params
(177-179)
src/core/tree.ts (2)
src/core/treeNodeValue.ts (3)
TreeRouteParam
(232-239)re
(288-301)escapeRegex
(253-254)src/core/extendRoutes.ts (1)
params
(177-179)
⏰ 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: test (20.x, windows-latest)
🔇 Additional comments (14)
src/core/customBlock.ts (1)
28-29
: LGTM: params typing wired to DefinePageTyping CustomRouteBlock.params as DefinePage['params'] keeps schema consistent with runtime types.
src/index.ts (3)
63-85
: Resolver virtual module wiring looks correctIncluding MODULE_RESOLVER_PATH in resolveId’s filter and returning a virtual id alongside MODULE_ROUTES_PATH/MODULE_VUE_ROUTER_AUTO is consistent with existing behavior.
92-95
: Async buildStart is fineAwaiting ctx.scanPages(options.watch) prevents unhandled promise cases and aligns with Vite hooks.
113-151
: Load branch for vue-router/auto-resolver is properly integratedAdding the VIRTUAL_PREFIX filter and delegating to ctx.generateResolver() while updating ROUTES_LAST_LOAD_TIME mirrors the routes path.
src/codegen/generateRouteMap.ts (1)
45-54
: Param-parsers integration and fallback logic are soundparamType overriding generateRouteParams when experimental.paramParsers is enabled is clear and preserves backward compatibility when disabled.
src/core/tree.ts (2)
310-325
: Confirm behavior with overridden pathsIf a node’s path is overridden (absolute/relative), regexp uses pathSegment/re only, not value.path. That may desync matching vs actual route path. Confirm intended behavior; adjust to honor overrides if needed.
327-337
: Score aggregation and isMatchable guard look goodscore builds a top-down 2D matrix; isMatchable reliably narrows types.
Also applies to: 371-375
src/core/treeNodeValue.ts (2)
271-301
: Param regex and scoring per subsegment are sensibleUsing non-greedy groups and malus-based scoring will help deterministic matching across mixed static/param segments.
420-421
: Param parser ([param=type]) integration is tightNew parser state, buffer handling, and consumption into currentTreeRouteParam.parser are consistent; createEmptyRouteParam sets parser: null to keep types stable.
Also applies to: 451-563, 731-740
src/core/definePage.spec.ts (2)
3-7
: Test refactor and coverage improvements look goodRenaming to extractDefinePageInfo, adding ts/vue wrappers, and expanding params extraction tests improve clarity and coverage.
75-87
: Import pruning tests are valuableThe “remove default/star unused imports” cases catch common regressions in definePageTransform.
Also applies to: 89-101, 103-119
src/core/context.ts (3)
166-174
: LGTM:extractDefinePageInfo()
merge.Merging extracted definePage metadata into the custom route block is correct and side-effect free.
249-258
: LGTM: NewgenerateResolver()
public API.The resolver pipeline and exposure look coherent and match the new codegen flow.
Also applies to: 449-450
415-419
: LGTM: Refresh routes after DTS changes.Calling
server.updateRoutes()
post-DTS write keeps editor integrations in sync.
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
♻️ Duplicate comments (9)
src/codegen/generateRouteResolver.spec.ts (2)
439-439
: Wrong fixture path for route '/b/e/f'.The inserted file path should match the route. Use 'b/e/f.vue' instead of 'b/c/f.vue'. Inline snapshots will update accordingly after fixing.
- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')#!/bin/bash # Find any remaining mismatches for '/b/e/f' pointing to b/c/f.vue rg -nP "insert\\('b/e/f'\\s*,\\s*'b/c/f\\.vue'\\)" src
536-536
: Same path mismatch repeated.Duplicate of the previous issue: fix the file path for '/b/e/f'.
- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')src/core/utils.ts (1)
202-212
: Object spread on possibly undefined will throw at runtime.
{ ...a[key]?.path }
(and similar) throws “Cannot convert undefined or null to object” when absent. Default to{}
before spreading.- } else if (key === 'params') { - merged[key] = { - path: { - ...a[key]?.path, - ...b[key]?.path, - }, - query: { - ...a[key]?.query, - ...b[key]?.query, - }, - } + } else if (key === 'params') { + const path = { + ...(a[key]?.path ?? {}), + ...(b[key]?.path ?? {}), + } + const query = { + ...(a[key]?.query ?? {}), + ...(b[key]?.query ?? {}), + } + merged[key] = { path, query }src/codegen/generateRouteMap.ts (1)
16-20
: Fix extra closing brace in generated RouteNamedMapThe template closes the interface twice, producing invalid TS.
- return `export interface RouteNamedMap { -${node - .getChildrenSorted() - .map((n) => generateRouteNamedMap(n, options, paramParsersMap)) - .join('')}}` + return `export interface RouteNamedMap { +${node + .getChildrenSorted() + .map((n) => generateRouteNamedMap(n, options, paramParsersMap)) + .join('')} +}`src/core/tree.ts (1)
316-331
: Regex builder adds extra slashes for groups/root; build parts and joinIncluding root and empty segments injects redundant '/', yielding patterns like /^//users$/i. Build an array of non-empty parts and join once.
- get regexp(): string { - let re = '' - let node: TreeNode | undefined = this - - while (node) { - if (node.value.isParam() && node.value.re) { - re = node.value.re + (re ? '\\/' : '') + re - } else { - re = escapeRegex(node.value.pathSegment) + (re ? '\\/' : '') + re - } - - node = node.parent - } - - return '/^' + re + '$/i' - } + get regexp(): string { + const parts: string[] = [] + let node: TreeNode | undefined = this + while (node && !node.isRoot()) { + if (node.value.isParam() && node.value.re) { + parts.unshift(node.value.re) + } else if (node.value.pathSegment) { + parts.unshift(escapeRegex(node.value.pathSegment)) + } + node = node.parent + } + const body = parts.length ? '\\/' + parts.join('\\/') : '\\/' + return '/^' + body + '$/i' + }src/codegen/generateRouteParams.ts (2)
40-48
: Missing cardinality wrapping for overridden typesCustom parser types aren't wrapped with
ParamValueOneOrMore
/ParamValueZeroOrMore
/ParamValueZeroOrOne
based onparam.modifier
, breaking type consistency for repeatable/optional params.
31-49
: Bug: override logic never executes fallback pathThe conditional
type ? type || '/* INVALID */ unknown'
is faulty. Whentype
is truthy, the entire ternary evaluates totype
, ignoring the|| '/* INVALID */ unknown'
part. Empty strings slip through as valid types.Apply this diff to correctly handle null/empty overrides:
- .map((param, i) => { - const type = types[i] - return `${param.paramName}${ - isRaw && isTreeParamOptional(param) ? '?' : '' - }: ${ - 'modifier' in param - ? param.repeatable - ? param.optional || isRaw // in raw mode, the tuple version is annoying to pass - ? 'string[]' - : '[string, ...string[]]' - : param.optional - ? 'string | null' - : 'string' - : type - }` - }) + .map((param, i) => { + const hasOverride = i < types.length + const type = types[i] + return `${param.paramName}${ + isRaw && isTreeParamOptional(param) ? '?' : '' + }: ${ + 'modifier' in param + ? param.repeatable + ? param.optional || isRaw // in raw mode, the tuple version is annoying to pass + ? 'string[]' + : '[string, ...string[]]' + : param.optional + ? 'string | null' + : 'string' + : hasOverride + ? (type && type.trim() ? type : '/* INVALID */ unknown') + : 'string' + }` + })src/codegen/generateRouteResolver.ts (1)
95-98
: Guard against negative repeat countWhen
varName.length
exceedsString(state.id).length + 2
, the repeat count becomes negative and throws.Apply this diff:
- ` ${varName}, ${' '.repeat(String(state.id).length - varName.length + 2)}// ${path}` + ` ${varName},${' '.repeat(Math.max(1, String(state.id).length - varName.length + 3))}// ${path}`src/core/context.ts (1)
218-218
: Remove manual './' prefix for relative pathsThe codegen already adds the './' prefix when needed. Adding it here causes double prefixing.
- relativePath: './' + relative(options.root, absolutePath), + relativePath: relative(options.root, absolutePath),
🧹 Nitpick comments (13)
playground-experimental/src/pages/blog/[[slugOptional]]+.vue (1)
6-6
: Render params as readable JSON instead of [object Object].Stringify the params so the playground shows useful output.
- <pre>{{ $route.params }}</pre> + <pre v-text="JSON.stringify($route.params, null, 2)" />playground-experimental/src/pages/blog/[slug]+.vue (1)
6-6
: Render params as readable JSON instead of [object Object].Same tweak here for clearer debugging output.
- <pre>{{ $route.params }}</pre> + <pre v-text="JSON.stringify($route.params, null, 2)" />playground-experimental/src/pages/blog/info/[[section]].vue (1)
6-6
: Render params as readable JSON instead of [object Object].Helps verify optional param behavior in the playground.
- <pre>{{ $route.params }}</pre> + <pre v-text="JSON.stringify($route.params, null, 2)" />playground-experimental/src/pages/blog/info/(info).vue (1)
9-14
: Passing null as a param may turn into the literal 'null' segment; use undefined/omit if the goal is to remove the optional segment.Depending on resolver/encoder, null can stringify. Recommend undefined (or omitting the key) for “absent” to avoid unexpected URLs.
<RouterLink :to="{ name: '/blog/info/[[section]]', params: { - section: null, + section: undefined, }, }" > @@ <RouterLink :to="{ name: '/blog/info/[[section]]', params: { section: '1', }, }" >If the intent is to explicitly test null handling, ignore this suggestion.
Also applies to: 20-25
src/codegen/generateRouteResolver.spec.ts (2)
222-222
: Grammar nit: test title.Prefer “does not include query params from parent nodes”.
- it('does not includes query params from parent nodes', () => { + it('does not include query params from parent nodes', () => {
480-485
: GuardString.repeat
to prevent negative countsThe only
repeat(
usage in src/codegen/generateRouteResolver.ts (line 97) should be wrapped withMath.max(0, …)
:• File: src/codegen/generateRouteResolver.ts
Line: ~97Suggested diff:
.map(({ varName, path }) => { - return ` ${varName}, ${' '.repeat(String(state.id).length - varName.length + 2)}// ${path}` + const pad = Math.max(0, String(state.id).length - varName.length + 2) + return ` ${varName}, ${' '.repeat(pad)}// ${path}` })This preserves comment alignment in snapshots while ensuring
repeat()
never receives a negative count.playground-experimental/typed-router.d.ts (1)
8-10
: Param_date is defined but not used.Route '/events/[when=date]' still types
when
asstring
. If parser-aware types are intended, wire Param_date here (via codegen) to reflect the custom parser.- '/events/[when=date]': RouteRecordInfo<'/events/[when=date]', '/events/:when', { when: string }, { when: string }>, + '/events/[when=date]': RouteRecordInfo<'/events/[when=date]', '/events/:when', { when: Param_date }, { when: Param_date }>,If this file is auto-generated (as noted), make the change in the generator rather than editing this file directly. Do you want a patch against the generator to produce Param_* types for parser-backed params?
src/core/definePage.ts (2)
314-321
: Validateformat
and warn on invalid valuesCurrently any string is accepted; constrain to 'value' | 'array' | 'both' with a fallback.
- } else if ( - paramProp.key.name === 'format' && - paramProp.value.type === 'StringLiteral' - ) { - paramInfo.format = paramProp.value.value as - | 'value' - | 'array' - | 'both' + } else if ( + paramProp.key.name === 'format' && + paramProp.value.type === 'StringLiteral' + ) { + const v = paramProp.value.value + if (v === 'value' || v === 'array' || v === 'both') { + paramInfo.format = v + } else { + warn( + `Unrecognized format "${v}" in definePage() for query param "${paramName}" in "${_id}". Falling back to "both".` + ) + paramInfo.format = 'both' + }
357-371
: Path param parser validation is TODO; consider basic guard + warnThe TODO notes missing validation of parser values. At minimum, warn when the value is empty or obviously invalid; custom parsers will still be handled later by paramParsersMap.
I can add a minimal guard that warns on empty strings and leaves custom values untouched. Want a patch?
src/codegen/generateParamParsers.ts (1)
15-30
: Use unified warn helper for missing parsersPrefer the project’s warn() to keep log format consistent.
-import { ImportsMap } from '../core/utils' +import { ImportsMap, warn } from '../core/utils' @@ - console.warn( - `Parameter parser "${param.parser}" not found for route "${node.fullPath}".` - ) + warn( + `Parameter parser "${param.parser}" not found for route "${node.fullPath}".` + )src/core/context.ts (2)
109-149
: Unnecessary fallback|| []
after optional chainingThe
|| []
at line 149 is redundant since.map()
returns an array. The spread operator handlesundefined
gracefully.- }) || []), + }),
357-363
: Simplify directory length checkUse optional chaining for cleaner code.
- if ( - options.experimental.paramParsers && - options.experimental.paramParsers.dir.length > 0 - ) { + if (options.experimental.paramParsers?.dir?.length) {src/core/treeNodeValue.ts (1)
119-120
: Avoid usingvar
declarationsUse
const
orlet
instead ofvar
for block scoping.- for (var paramName in paramsQuery) { - var config = paramsQuery[paramName] + for (const paramName in paramsQuery) { + const config = paramsQuery[paramName]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
src/core/__snapshots__/definePage.spec.ts.snap
is excluded by!**/*.snap
📒 Files selected for processing (27)
client.d.ts
(2 hunks)package.json
(2 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/pages/[...path].vue
(1 hunks)playground-experimental/src/pages/blog/[[slugOptional]]+.vue
(1 hunks)playground-experimental/src/pages/blog/[slug]+.vue
(1 hunks)playground-experimental/src/pages/blog/info/(info).vue
(1 hunks)playground-experimental/src/pages/blog/info/[[section]].vue
(1 hunks)playground-experimental/src/pages/tests/[[optional]]/end.vue
(1 hunks)playground-experimental/src/router.ts
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)src/codegen/generateParamParsers.ts
(1 hunks)src/codegen/generateRouteMap.ts
(1 hunks)src/codegen/generateRouteParams.ts
(2 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/context.ts
(12 hunks)src/core/customBlock.ts
(2 hunks)src/core/definePage.spec.ts
(10 hunks)src/core/definePage.ts
(4 hunks)src/core/extendRoutes.ts
(2 hunks)src/core/tree.spec.ts
(4 hunks)src/core/tree.ts
(3 hunks)src/core/treeNodeValue.ts
(16 hunks)src/core/utils.ts
(3 hunks)src/runtime.ts
(2 hunks)src/types.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
- playground-experimental/src/router.ts
- playground-experimental/package.json
- client.d.ts
- package.json
- playground-experimental/src/pages/[...path].vue
- src/core/extendRoutes.ts
- src/core/customBlock.ts
- src/runtime.ts
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-08-15T16:11:02.627Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.627Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.spec.ts
src/codegen/generateRouteMap.ts
src/core/treeNodeValue.ts
src/core/tree.ts
src/core/context.ts
src/codegen/generateRouteResolver.ts
📚 Learning: 2025-08-16T13:01:42.709Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.709Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
src/codegen/generateParamParsers.ts
src/core/treeNodeValue.ts
playground-experimental/typed-router.d.ts
📚 Learning: 2025-08-23T12:44:23.804Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/utils.ts:202-212
Timestamp: 2025-08-23T12:44:23.804Z
Learning: In JavaScript, spreading undefined or null values in object literals is safe and does not throw errors. `{ ...undefined }` and `{ ...null }` both result in empty objects `{}`. The spread operator ignores falsy values like undefined and null.
Applied to files:
src/core/utils.ts
📚 Learning: 2025-08-16T13:00:51.271Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.271Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
src/core/context.ts
🧬 Code graph analysis (13)
src/core/definePage.spec.ts (3)
src/core/context.ts (1)
definePageTransform
(444-449)src/core/definePage.ts (2)
definePageTransform
(66-189)extractDefinePageInfo
(199-260)src/utils/index.ts (1)
ts
(18-18)
src/core/tree.spec.ts (2)
src/core/tree.ts (1)
PrefixTree
(411-445)src/core/treeNodeValue.ts (1)
TreePathParam
(287-294)
src/codegen/generateRouteResolver.spec.ts (6)
src/index.ts (1)
DEFAULT_OPTIONS
(28-28)src/options.ts (2)
DEFAULT_OPTIONS
(249-266)resolveOptions
(313-395)src/codegen/generateRouteResolver.ts (4)
generateRouteRecord
(107-219)generateRouteRecordPath
(245-267)generateRouteRecordQuery
(272-316)generateRouteResolver
(66-102)src/core/utils.ts (1)
ImportsMap
(310-379)src/core/tree.ts (1)
PrefixTree
(411-445)src/codegen/generateParamParsers.ts (1)
ParamParsersMap
(5-13)
src/codegen/generateParamParsers.ts (3)
src/core/tree.ts (3)
PrefixTree
(411-445)params
(277-287)name
(244-251)src/core/treeNodeValue.ts (3)
params
(148-150)TreePathParam
(287-294)TreeQueryParam
(296-309)src/core/utils.ts (1)
ImportsMap
(310-379)
src/core/utils.ts (1)
src/core/treeNodeValue.ts (1)
TreePathParam
(287-294)
src/core/definePage.ts (4)
src/core/customBlock.ts (1)
CustomRouteBlock
(20-34)src/core/treeNodeValue.ts (2)
params
(148-150)queryParams
(111-142)src/runtime.ts (1)
ParamParserType
(79-83)src/core/utils.ts (1)
warn
(10-15)
src/codegen/generateRouteMap.ts (4)
src/core/tree.ts (1)
TreeNode
(21-405)src/options.ts (1)
ResolvedOptions
(400-400)src/codegen/generateParamParsers.ts (2)
ParamParsersMap
(5-13)generateParamsTypes
(43-57)src/codegen/generateRouteParams.ts (2)
EXPERIMENTAL_generateRouteParams
(26-54)generateRouteParams
(4-24)
src/core/treeNodeValue.ts (2)
src/core/customBlock.ts (1)
CustomRouteBlockQueryParamOptions
(36-41)src/core/tree.ts (3)
queryParams
(309-311)pathParams
(292-304)params
(277-287)
playground-experimental/typed-router.d.ts (1)
playground-experimental/src/router.ts (1)
RouteNamedMap
(21-36)
src/core/tree.ts (2)
src/core/treeNodeValue.ts (5)
TreePathParam
(287-294)TreeQueryParam
(296-309)params
(148-150)re
(365-378)escapeRegex
(332-333)src/core/extendRoutes.ts (1)
params
(177-179)
src/core/context.ts (5)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(5-13)warnMissingParamParsers
(15-30)generateParamParsersTypesDeclarations
(32-41)src/core/definePage.ts (1)
extractDefinePageInfo
(199-260)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(66-102)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)
src/codegen/generateRouteParams.ts (1)
src/core/treeNodeValue.ts (1)
isTreeParamOptional
(311-318)
src/codegen/generateRouteResolver.ts (4)
src/core/tree.ts (7)
PrefixTree
(411-445)path
(223-228)TreeNode
(21-405)isMatchable
(377-381)name
(244-251)params
(277-287)queryParams
(309-311)src/core/utils.ts (1)
ImportsMap
(310-379)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(5-13)generatePathParamsOptions
(78-93)generateParamParserOptions
(59-76)src/codegen/generateRouteRecords.ts (3)
generateRouteRecord
(15-110)formatMeta
(168-178)generatePageImport
(145-166)
🔇 Additional comments (10)
playground-experimental/src/pages/tests/[[optional]]/end.vue (1)
3-7
: LGTM for showcasing fullPath.This is fine for validating optional-in-the-middle routing.
src/codegen/generateRouteResolver.spec.ts (1)
885-888
: Thanks for correcting the nested fixture path.The child route now correctly points to 'api/v1/[type]/c.vue'.
src/types.ts (1)
9-13
: Re-exports look consistent with core type changes.Public type surface aligns with TreeNodeValue/Param/Group updates.
src/core/tree.spec.ts (3)
46-62
: Good coverage for custom param parser variants.Solid assertions for typed parsers across plain, repeatable, optional, repeatable-optional, and mixed subsegment cases.
Also applies to: 64-82, 84-102, 104-122, 125-141
552-559
: Type-checked expectation usingsatisfies
is a nice touch.Keeps the runtime check minimal while enforcing the shape at compile time.
691-772
: Query params extraction tests read well.Clear separation between path and query params, defaults, and parser hints.
src/codegen/generateRouteMap.ts (1)
9-13
: All call sites updated to use 3-arg signatures
Verified that every invocation ofgenerateRouteNamedMap
andgenerateRouteRecordInfo
now includesparamParsersMap
as the third argument (insrc/codegen/generateRouteMap.ts
, its spec file, andsrc/core/context.ts
).src/core/definePage.spec.ts (1)
184-239
: Nice coverage for params extractionGood end‑to‑end check of path/query params, parser/default/format handling.
src/core/tree.ts (1)
324-324
: Good use of escapeRegex for static segmentsEscaping static segments avoids regex metacharacter issues.
src/core/treeNodeValue.ts (1)
324-333
: LGTM! Proper regex escaping implementationThe regex escaping now correctly handles all special characters, not just forward slashes. Good fix for the previously identified issue.
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
♻️ Duplicate comments (5)
src/runtime.ts (1)
90-94
: Broken TSDoc link: replace or removeReplace the placeholder URL with plain text to avoid leaking an invalid link into public typings.
- * and any parser in the {@link https://uvr.esm.is/TODO | params folder }. If + * and any parser in the params folder. Ifsrc/codegen/generateRouteResolver.spec.ts (1)
439-439
: Typo in fixture path for route '/b/e/f'The inserted file path should match the route.
- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')src/core/context.ts (3)
208-210
: Don’t access private watcher.options; use the provided cwd
FSWatcher
doesn’t exposeoptions
in its public API. Use thecwd
argument you already pass.- function setupParamParserWatcher(watcher: FSWatcher, cwd: string) { - logger.log(`🤖 Scanning param parsers in ${watcher.options.cwd}`) + function setupParamParserWatcher(watcher: FSWatcher, cwd: string) { + logger.log(`🤖 Scanning param parsers in ${cwd}`)
358-361
: Guard .dir.length with optional chainingAvoid reading
.length
onundefined
whenparamParsers
ordir
is not set.- if (options.experimental.paramParsers?.dir.length) { + if (options.experimental.paramParsers?.dir?.length) { warnMissingParamParsers(routeTree, paramParsers) }
109-150
: Fix potential crash when paramParsers is undefined
options.experimental.paramParsers?.dir.map(...)
calls.map
onundefined
. Guard the mapping.- ...(options.experimental.paramParsers?.dir.map((folder) => { + ...((options.experimental.paramParsers?.dir ?? []).map((folder) => { watchers.push( setupParamParserWatcher( fsWatch('.', { cwd: folder, ignoreInitial: true, ignorePermissionErrors: true, ignored: (filePath, stats) => { // let folders pass, they are ignored by the glob pattern if (!stats || stats.isDirectory()) { return false } return !isParamParserMatch(relative(folder, filePath)) }, }), folder ) ) return glob(PARAM_PARSER_GLOB, { cwd: folder, onlyFiles: true, expandDirectories: false, }).then((paramParserFiles) => { for (const file of paramParserFiles) { const name = parsePathe(file).name // TODO: could be simplified to only one import that starts with / for vite const absolutePath = resolve(folder, file) paramParsers.set(name, { name, typeName: `Param_${name}`, absolutePath, relativePath: relative(options.root, absolutePath), }) } logger.log( 'Parsed param parsers', [...paramParsers].map((p) => p[0]) ) }) - }) || []), + })),
🧹 Nitpick comments (8)
src/core/definePage.ts (5)
199-205
: Narrow the return type of extractDefinePageInfoThe function never returns
null
, onlyundefined
or an object. Simplify the signature for clarity.-export function extractDefinePageInfo( +export function extractDefinePageInfo( sfcCode: string, id: string -): DefinePageInfo | null | undefined { +): DefinePageInfo | undefined {
251-255
: Warn on non-object params valueIf
params
is present but not an object, silently ignoring it hides misconfigurations. Emit a warning.- } else if (prop.key.name === 'params') { - if (prop.value.type === 'ObjectExpression') { - routeInfo.params = extractParamsInfo(prop.value, id) - } - } + } else if (prop.key.name === 'params') { + if (prop.value.type === 'ObjectExpression') { + routeInfo.params = extractParamsInfo(prop.value, id) + } else { + warn(`"params" must be an object. Found in "${id}".`) + } + }
314-318
: Validate format option instead of trusting any stringCasting to
'value' | 'array'
without runtime validation lets invalid formats slip through. Default to'value'
and warn on unknowns.- } else if ( - paramProp.key.name === 'format' && - paramProp.value.type === 'StringLiteral' - ) { - paramInfo.format = paramProp.value.value as 'value' | 'array' + } else if ( + paramProp.key.name === 'format' && + paramProp.value.type === 'StringLiteral' + ) { + const fmt = paramProp.value.value + if (fmt === 'value' || fmt === 'array') { + paramInfo.format = fmt + } else { + warn( + `Unknown "format" "${fmt}" in definePage() for query param "${paramName}". Falling back to "value".` + ) + paramInfo.format = 'value' + } }
319-341
: Support negative numeric and template literal defaultsDefaults like
-1
(UnaryExpression) and simple template literals currently fall into the “Unrecognized” branch. Handle common cases to reduce noise.- } else if (paramProp.key.name === 'default') { + } else if (paramProp.key.name === 'default') { if ( typeof paramProp.value.extra?.raw === 'string' && paramProp.value.extra ) { paramInfo.default = paramProp.value.extra.raw } else { warn( `No raw value parsed in definePage() for query param "${paramName}". This is a bug, open an issue on https://github.com/posva/unplugin-vue-router and provide the definePage() code.` ) - if (paramProp.value.type === 'NumericLiteral') { + if (paramProp.value.type === 'NumericLiteral') { paramInfo.default = String(paramProp.value.value) + } else if ( + paramProp.value.type === 'UnaryExpression' && + paramProp.value.operator === '-' && + paramProp.value.argument.type === 'NumericLiteral' + ) { + // e.g. -1 + paramInfo.default = `-${String(paramProp.value.argument.value)}` } else if (paramProp.value.type === 'StringLiteral') { paramInfo.default = JSON.stringify(paramProp.value.value) + } else if ( + paramProp.value.type === 'TemplateLiteral' && + paramProp.value.expressions.length === 0 + ) { + // simple template literal without expressions: `foo` + paramInfo.default = JSON.stringify( + paramProp.value.quasis.map((q) => q.value.cooked || '').join('') + ) } else if (paramProp.value.type === 'BooleanLiteral') { paramInfo.default = String(paramProp.value.value) } else if (paramProp.value.type === 'NullLiteral') { paramInfo.default = 'null' } else { warn( `Unrecognized default value in definePage() for query param "${paramName}". Typeof value: ${paramProp.value.type}. This is a bug, open an issue on https://github.com/posva/unplugin-vue-router and provide the definePage() code.` ) } } }
354-373
: Optional: warn on non-string path param valuesPath params silently ignore non-string literal values. Add a warning to aid users.
for (const prop of pathObj.properties) { if ( prop.type === 'ObjectProperty' && prop.key.type === 'Identifier' && prop.value.type === 'StringLiteral' ) { // TODO: we should check if the value is a valid parser type pathParams[prop.key.name] = prop.value.value as ParamParserType + } else if ( + prop.type === 'ObjectProperty' && + prop.key.type === 'Identifier' + ) { + warn( + `Path param "${prop.key.name}" must be a string literal parser.` + ) } }src/runtime.ts (2)
31-47
: Make _mergeRouteRecord resilient to nullish entriesGuard against accidental
undefined
ornull
route records to avoid runtime errors when merging.export function _mergeRouteRecord( main: RouteRecordRaw, ...routeRecords: Partial<RouteRecordRaw>[] ): RouteRecordRaw { // @ts-expect-error: complicated types - return routeRecords.reduce((acc, routeRecord) => { + return routeRecords.reduce((acc, routeRecord) => { + if (!routeRecord) return acc const meta = Object.assign({}, acc.meta, routeRecord.meta) const alias: string[] = ([] as string[]).concat( acc.alias || [], routeRecord.alias || [] )
99-103
: TSDoc grammar nitsSmall wording fixes improve generated docs.
- * (e.g. a invalid number is passed to the int param parser). If not provided + * (e.g. an invalid number is passed to the int param parser). If not provided- * not provided, the value will kept as is. + * not provided, the value will be kept as is.src/core/context.ts (1)
218-219
: Unify relativePath shape (drop extra './' here)Initial scan stores
relativePath
without a'./'
prefix, but the watcher adds it, leading to inconsistent imports ('././...'
). Let codegen add the'./'
prefix.- relativePath: './' + relative(options.root, absolutePath), + relativePath: relative(options.root, absolutePath),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/core/context.ts
(12 hunks)src/core/definePage.ts
(4 hunks)src/core/tree.spec.ts
(4 hunks)src/core/treeNodeValue.ts
(16 hunks)src/runtime.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/core/tree.spec.ts
- src/core/treeNodeValue.ts
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:137-142
Timestamp: 2025-08-23T12:50:36.588Z
Learning: In src/core/context.ts, param parser relative paths must be computed relative to options.root (not relative to DTS file location) and the './' prefix is required for proper path resolution in the generated code.
📚 Learning: 2025-08-15T16:11:02.627Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.627Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.spec.ts
src/core/context.ts
📚 Learning: 2025-08-23T12:50:36.588Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:137-142
Timestamp: 2025-08-23T12:50:36.588Z
Learning: In src/core/context.ts, param parser relative paths must be computed relative to options.root (not relative to DTS file location) and the './' prefix is required for proper path resolution in the generated code.
Applied to files:
src/codegen/generateRouteResolver.spec.ts
src/core/context.ts
📚 Learning: 2025-08-23T12:54:44.896Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-150
Timestamp: 2025-08-23T12:54:44.896Z
Learning: In src/core/context.ts, when paramParsers is defined in resolved options, the dir property is guaranteed to be an array, so optional chaining on options.experimental.paramParsers?.dir.map() is sufficient protection against undefined values.
Applied to files:
src/runtime.ts
src/core/context.ts
📚 Learning: 2025-08-16T13:00:51.271Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.271Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-16T13:01:42.709Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.709Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
src/core/context.ts
🧬 Code graph analysis (4)
src/codegen/generateRouteResolver.spec.ts (6)
src/index.ts (1)
DEFAULT_OPTIONS
(28-28)src/options.ts (2)
DEFAULT_OPTIONS
(249-266)resolveOptions
(313-395)src/codegen/generateRouteResolver.ts (4)
generateRouteRecord
(107-219)generateRouteRecordPath
(245-267)generateRouteRecordQuery
(272-316)generateRouteResolver
(66-102)src/core/utils.ts (1)
ImportsMap
(310-379)src/core/tree.ts (1)
PrefixTree
(411-445)src/codegen/generateParamParsers.ts (1)
ParamParsersMap
(5-13)
src/core/definePage.ts (5)
src/core/customBlock.ts (1)
CustomRouteBlock
(20-34)src/core/treeNodeValue.ts (2)
params
(146-148)queryParams
(111-140)src/core/tree.ts (3)
params
(277-287)queryParams
(309-311)pathParams
(292-304)src/runtime.ts (1)
ParamParserType
(79-83)src/core/utils.ts (1)
warn
(10-15)
src/runtime.ts (2)
client.d.ts (1)
TypesConfig
(38-40)src/data-loaders/entries/index.ts (1)
TypesConfig
(41-41)
src/core/context.ts (8)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(5-13)warnMissingParamParsers
(15-30)generateParamParsersTypesDeclarations
(32-41)src/core/definePage.ts (1)
extractDefinePageInfo
(199-260)src/core/utils.ts (1)
ImportsMap
(310-379)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(66-102)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
🔇 Additional comments (1)
src/core/context.ts (1)
171-175
: Avoid spreading possibly undefined objects
routeBlock
anddefinedPageInfo
can beundefined
. Spreading them throws at runtime. Use nullish fallbacks.- node.setCustomRouteBlock(filePath, { - ...routeBlock, - ...definedPageInfo, - }) + node.setCustomRouteBlock(filePath, { + ...(routeBlock || {}), + ...(definedPageInfo || {}), + })⛔ Skipped due to learnings
Learnt from: posva PR: posva/unplugin-vue-router#700 File: src/core/utils.ts:202-212 Timestamp: 2025-08-23T12:44:23.804Z Learning: In JavaScript, spreading undefined or null values in object literals is safe and does not throw errors. `{ ...undefined }` and `{ ...null }` both result in empty objects `{}`. The spread operator ignores falsy values like undefined and null.
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: 1
♻️ Duplicate comments (1)
src/core/context.ts (1)
357-361
: Use optional chaining on.dir?.length
(defensive, low impact).Resolved options make
dir
an array when defined, but this guard is harmless and future-proof.- if (options.experimental.paramParsers?.dir.length) { + if (options.experimental.paramParsers?.dir?.length) {
🧹 Nitpick comments (5)
src/core/context.ts (5)
71-73
: Broaden matcher to nested dirs and common JS/TS module extensions.Your current
*.{ts,js}
only matches files at the root of each folder. Consider supporting subfolders and Node variants.- const PARAM_PARSER_GLOB = '*.{ts,js}' + const PARAM_PARSER_GLOB = '**/*.{ts,js,mts,mjs,cjs,cts}'
137-142
: Normalize param-parser relativePath: relative to root + './' prefix (consistency).Initial scan stores
relativePath
without'./'
, while the watcher adds it. Based on your earlier clarification, paths should be relative tooptions.root
and include'./'
. Make both code paths consistent.- relativePath: relative(options.root, absolutePath), + relativePath: './' + relative(options.root, absolutePath),Note: With this change, both initial scan and watcher add
'./'
. See the follow-up on generateParamParsers for avoiding a double'./'
in imports.
208-219
: Avoid double'./'
in generated imports; keep normalization in one place.Currently, watcher adds
'./'
andgenerateParamParsersTypesDeclarations()
also prefixes'./'
, leading toimport('././...')
. While it usually works, it’s noisy. Since we normalize in context (per your guidance: relative to root +'./'
), drop the extra'./'
in the generator.Proposed change in src/codegen/generateParamParsers.ts (outside this diff):
- `type ${typeName} = ReturnType<NonNullable<typeof import('./${relativePath}').parser['get']>>` + `type ${typeName} = ReturnType<NonNullable<typeof import('${relativePath}').parser['get']>>`This aligns with the learning that path normalization belongs outside individual generators and keeps imports clean.
160-175
: Protect the scan from crashing on malformed definePage().A syntax error in
definePage()
currently throws and can abort the scan. Catch and warn instead.const content = await fs.readFile(filePath, 'utf8') @@ - const definedPageInfo = extractDefinePageInfo(content, filePath) + let definedPageInfo: ReturnType<typeof extractDefinePageInfo> + try { + definedPageInfo = extractDefinePageInfo(content, filePath) + } catch (err) { + logger.warn( + `[definePage] Failed to parse definePage() in "${filePath}": ${String( + (err as Error)?.message || err + )}` + ) + definedPageInfo = undefined + } @@ node.setCustomRouteBlock(filePath, { ...routeBlock, ...definedPageInfo, })
418-423
: Reset watchers list after closing to allow rescans in the same context.Without clearing, subsequent
scanPages()
calls early-return forever.if (watchers.length) { logger.log('🛑 stopping watcher') - watchers.forEach((watcher) => watcher.close()) + watchers.forEach((watcher) => watcher.close()) + watchers.length = 0 }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/core/context.ts
(12 hunks)src/core/tree.ts
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/core/tree.ts
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-08-23T12:50:36.588Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:137-142
Timestamp: 2025-08-23T12:50:36.588Z
Learning: In src/core/context.ts, param parser relative paths must be computed relative to options.root (not relative to DTS file location) and the './' prefix is required for proper path resolution in the generated code.
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-23T12:54:44.896Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-150
Timestamp: 2025-08-23T12:54:44.896Z
Learning: In src/core/context.ts, when paramParsers is defined in resolved options, the dir property is guaranteed to be an array, so optional chaining on options.experimental.paramParsers?.dir.map() is sufficient protection against undefined values.
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-16T13:00:51.271Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.271Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-15T16:11:02.627Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.627Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-16T13:01:42.709Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.709Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
src/core/context.ts
🧬 Code graph analysis (1)
src/core/context.ts (10)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(5-13)warnMissingParamParsers
(15-30)generateParamParsersTypesDeclarations
(32-41)src/core/definePage.ts (1)
extractDefinePageInfo
(199-260)src/core/utils.ts (1)
ImportsMap
(310-379)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(66-102)src/utils/index.ts (1)
ts
(18-18)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/core/moduleConstants.ts (1)
MODULE_ROUTES_PATH
(6-6)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
🔇 Additional comments (2)
src/core/context.ts (2)
249-298
: Resolver generation + HMR wrapper looks solid.ImportsMap orchestration and HMR flow are coherent. No issues spotted.
71-73
: One more check: param-parser behavior summary.
- Pattern: consider
**/*
+ common extensions.- Watchers: gate param-parser watchers on
startWatchers
.- Paths: make both initial scan and watcher store
'./' + relative(root, abs)
.- Codegen: remove extra
'./'
ingenerateParamParsersTypesDeclarations()
to avoid././
.These keep behavior consistent with your earlier guidance (root-relative +
'./'
prefix).Also applies to: 109-150, 208-219, 357-361
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
♻️ Duplicate comments (2)
src/core/tree.ts (2)
4-8
: Escaping static segments: good fix.Importing and using
escapeRegex
addresses the previously flagged risk of unescaped static segments in regex construction.
313-331
: Avoid extra slashes with empty segments (e.g., groups); keep root-leading slash behavior.Current loop inserts a separator even when
pathSegment
is empty (group nodes), yielding double slashes like^\\/\\/users$
. Build parts and join; prefix once. This preserves the (previously validated) leading slash from the root.Apply this diff:
get regexp(): string { - let re = '' - let node: TreeNode | undefined = this - - while (node) { - if (node.value.isParam() && node.value.re) { - re = node.value.re + (re ? '\\/' : '') + re - } else { - re = escapeRegex(node.value.pathSegment) + (re ? '\\/' : '') + re - } - - node = node.parent - } - - return '/^' + re + '$/i' + const parts: string[] = [] + let node: TreeNode | undefined = this + while (node && !node.isRoot()) { + if (node.value.isParam() && node.value.re) { + parts.unshift(node.value.re) + } else if (node.value.pathSegment) { + parts.unshift(escapeRegex(node.value.pathSegment)) + } + node = node.parent + } + const body = parts.length ? '\\/' + parts.join('\\/') : '\\/' + return '/^' + body + '$/i' }
🧹 Nitpick comments (1)
src/core/tree.ts (1)
17-19
: Constrain matcher token type to avoid accidental numbers.Only
0 | 1
are meaningful numeric tokens; tighten the type for clarity and safety.Apply this diff:
-export type TreeNodeValueMatcherPart = Array< - string | number | Array<string | number> -> +export type MatcherToken = string | 0 | 1 +export type TreeNodeValueMatcherPart = Array< + MatcherToken | Array<MatcherToken> +>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
package.json
(2 hunks)playground-experimental/package.json
(1 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/core/tree.ts
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/codegen/generateRouteResolver.spec.ts
- package.json
- playground-experimental/package.json
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-23T12:49:31.712Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/tree.ts:313-331
Timestamp: 2025-08-23T12:49:31.712Z
Learning: The regexp getter in src/core/tree.ts correctly processes the root node to add the leading slash. The empty pathSegment from root becomes an empty string when escaped, but the concatenation logic `"" + (re ? '\\/' : '') + re` properly adds the leading slash that routes require.
Applied to files:
src/core/tree.ts
📚 Learning: 2025-08-15T16:11:02.627Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.627Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/core/tree.ts
🧬 Code graph analysis (1)
src/core/tree.ts (3)
src/core/treeNodeValue.ts (5)
TreePathParam
(285-292)TreeQueryParam
(294-307)params
(146-148)re
(363-376)escapeRegex
(330-331)src/core/extendRoutes.ts (1)
params
(177-179)src/types.ts (1)
TreeNode
(7-7)
🔇 Additional comments (6)
src/core/tree.ts (6)
274-287
: Params aggregation looks correct.Properly aggregates both path and query params from ancestors as documented.
292-304
: Path params aggregation looks correct.Collects only path params and includes parents’ param segments. Behavior aligns with the JSDoc.
306-312
: Local query params getter is clear and correct.Returns only this node’s query params as intended.
333-343
: Score path is fine.Skips root and preserves order from root→leaf via unshift; matches the intended semantics.
377-381
: Matchability guard is correct.Accurately narrows when there is at least one component and
name !== false
.
345-375
: Unify param token encoding for multi-subsegmentsIn the multi-subsegment branch, params are always mapped to
0
, discarding the splat (0
) vs non-splat (1
) distinction used elsewhere, and duplicating the mapping logic. Refactor to reuse the already-computedsubSegments
:@@ src/core/tree.ts:352 - if (subSegments.length > 1) { - parts.unshift( - node.value.subSegments.map( - segment => typeof segment === 'string' ? segment : 0 - ) - ) + if (subSegments.length > 1) { + parts.unshift(subSegments) } else if (subSegments.length === 1) { parts.unshift(subSegments[0]!) }This change:
- Preserves the correct
0
(splat) vs1
(non-splat) semantics in all cases- Removes duplicated mapping logic
Next steps:
- Apply the diff above
- Manually verify in
vue-router/experimental
howMatcherPatternPathDynamic
interprets numeric tokens (0
vs1
) to ensure the consumer handles both correctly
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: 3
🧹 Nitpick comments (13)
src/codegen/generateParamParsers.spec.ts (3)
73-127
: Type declarations tests are correct; consider asserting deterministic orderingInline snapshot for multiple parsers is fine. For long-term stability across platforms/sources, consider asserting a sorted output (e.g., by typeName) if the generator is updated to sort.
Would you like me to add a test that builds the map in reverse insertion order and still matches a sorted snapshot?
129-241
: Params-to-types mapping tests are comprehensiveCovers no parser, custom, native, and mixed. Consider adding a small case with TreeQueryParam to ensure parity for query params.
If helpful, I can draft a minimal test for a query param with parser 'bool'.
243-342
: Parser options import tests are clear; add override testGreat checks for custom and native imports. Add a test to verify that a custom parser named 'int' overrides the native one (as intended by the implementation).
I can provide the exact test snippet if you want it in this file.
src/codegen/generateParamParsers.ts (2)
40-49
: Deterministic order for generated type declarationsTo avoid order drift across environments, sort entries before mapping.
Apply:
export function generateParamParsersTypesDeclarations( paramParsers: ParamParsersMap ) { - return Array.from(paramParsers.values()) + return Array.from(paramParsers.values()) + .sort((a, b) => a.typeName.localeCompare(b.typeName)) .map( ({ typeName, relativePath }) => `type ${typeName} = ReturnType<NonNullable<typeof import('./${relativePath}').parser['get']>>` ) .join('\n') }
90-120
: Path params options are lean and readableConditional inclusion of repeatable/optional keeps output minimal. If you prefer explicitness, you could always emit repeatable: false, but current behavior is fine.
playground-experimental/src/pages/[...path].vue (3)
21-31
: Align default with format: 'array' for page.default: 1 conflicts with format: 'array' (type should be number[]). Either drop format or make default an array.
page: { parser: 'int', - default: 1, + default: [1], format: 'array', },
41-43
: Fix duplicate output; show query once.<pre>{{ $route.params }}</pre> - <pre>{{ $route.params }}</pre> + <pre>{{ $route.query }}</pre>
5-5
: Remove no-op property access (or comment intent).This reads the value without using it and may trigger no-unused-expression lint.
-route.params.path
src/core/definePage.ts (5)
24-24
: Use a type-only import to avoid runtime dependency/cycles.
CustomRouteBlock
is used purely for typing here; mark it as a type-only import.-import { CustomRouteBlock } from './customBlock' +import type { CustomRouteBlock } from './customBlock'
251-254
: Add a warning whenparams
is not an object literal.Right now non-object
params
is silently ignored. Emit a warning for better DX.- } else if (prop.key.name === 'params') { - if (prop.value.type === 'ObjectExpression') { - routeInfo.params = extractParamsInfo(prop.value, id) - } + } else if (prop.key.name === 'params') { + if (prop.value.type === 'ObjectExpression') { + routeInfo.params = extractParamsInfo(prop.value, id) + } else { + warn(`route params must be an object literal. Found in "${id}".`) + } }
262-283
: Warn on unknown properties insideparams
.Only
query
andpath
are supported. Unknown keys should emit a warning to catch typos.for (const prop of paramsObj.properties) { if (prop.type === 'ObjectProperty' && prop.key.type === 'Identifier') { if (prop.key.name === 'query' && prop.value.type === 'ObjectExpression') { params.query = extractQueryParams(prop.value, id) } else if ( prop.key.name === 'path' && prop.value.type === 'ObjectExpression' ) { params.path = extractPathParams(prop.value, id) + } else { + warn(`Unknown params key "${prop.key.name}" in definePage() of "${id}".`) } } }
351-370
: Guard against empty parser names inpath
params.Cheap validation to catch
''
by mistake.) { - // TODO: we should check if the value is a valid parser type - pathParams[prop.key.name] = prop.value.value as ParamParserType + // TODO: we should check if the value is a valid parser type + const parser = (prop.value.value as string) || '' + if (!parser.trim()) { + warn(`Empty parser for path param "${prop.key.name}" in "${_id}".`) + continue + } + pathParams[prop.key.name] = parser as ParamParserType }
371-386
: Improve alias diagnostics and preserve only string literals.Message should mention arrays; warn when non-string elements are dropped.
if ( aliasValue.type !== 'StringLiteral' && aliasValue.type !== 'ArrayExpression' ) { - warn(`route alias must be a string literal. Found in "${id}".`) + warn(`route alias must be a string literal or an array of string literals. Found in "${id}".`) } else { - return aliasValue.type === 'StringLiteral' - ? [aliasValue.value] - : aliasValue.elements.filter(isStringLiteral).map((el) => el.value) + if (aliasValue.type === 'StringLiteral') return [aliasValue.value] + const literals = aliasValue.elements.filter(isStringLiteral) + if (literals.length !== aliasValue.elements.length) { + warn(`Some non-string entries in route alias were ignored in "${id}".`) + } + return literals.map((el) => el.value) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
package.json
(2 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/pages/[...path].vue
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)src/codegen/generateParamParsers.spec.ts
(1 hunks)src/codegen/generateParamParsers.ts
(1 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/core/definePage.ts
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- package.json
- playground-experimental/package.json
- src/codegen/generateRouteResolver.spec.ts
- playground-experimental/typed-router.d.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-16T13:01:42.709Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.709Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
src/codegen/generateParamParsers.spec.ts
src/codegen/generateParamParsers.ts
📚 Learning: 2025-08-23T12:54:44.918Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-150
Timestamp: 2025-08-23T12:54:44.918Z
Learning: In src/core/context.ts, when paramParsers is defined in resolved options, the dir property is guaranteed to be an array, so optional chaining on options.experimental.paramParsers?.dir.map() is sufficient protection against undefined values.
Applied to files:
src/codegen/generateParamParsers.spec.ts
src/codegen/generateParamParsers.ts
📚 Learning: 2025-08-23T12:50:36.614Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:137-142
Timestamp: 2025-08-23T12:50:36.614Z
Learning: In src/core/context.ts, param parser relative paths must be computed relative to options.root (not relative to DTS file location) and the './' prefix is required for proper path resolution in the generated code.
Applied to files:
src/codegen/generateParamParsers.ts
🧬 Code graph analysis (3)
src/codegen/generateParamParsers.spec.ts (6)
src/options.ts (1)
resolveOptions
(313-395)tests/vitest-mock-warn.ts (1)
mockWarn
(5-114)src/core/tree.ts (1)
PrefixTree
(411-445)src/codegen/generateParamParsers.ts (6)
ParamParsersMap
(5-13)warnMissingParamParsers
(23-38)generateParamParsersTypesDeclarations
(40-49)generateParamsTypes
(51-67)generateParamParserOptions
(69-88)generatePathParamsOptions
(90-120)src/core/treeNodeValue.ts (1)
TreePathParam
(285-292)src/core/utils.ts (1)
ImportsMap
(310-379)
src/core/definePage.ts (4)
src/core/customBlock.ts (1)
CustomRouteBlock
(20-34)src/core/treeNodeValue.ts (2)
params
(146-148)queryParams
(111-140)src/runtime.ts (1)
ParamParserType
(79-83)src/core/utils.ts (1)
warn
(10-15)
src/codegen/generateParamParsers.ts (3)
src/core/tree.ts (3)
PrefixTree
(411-445)params
(277-287)name
(244-251)src/core/treeNodeValue.ts (3)
params
(146-148)TreePathParam
(285-292)TreeQueryParam
(294-307)src/core/utils.ts (1)
ImportsMap
(310-379)
⏰ 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: Cloudflare Pages
🔇 Additional comments (8)
src/codegen/generateParamParsers.spec.ts (2)
18-71
: Tests for warnMissingParamParsers look solidGood coverage of no-parser, native parsers, missing custom, and existing custom cases. The mockWarn guard also prevents silent warnings.
344-466
: Path params options generation reads wellCovers empty, parser/no-parser, flags, and mixed scenarios. Nice lean output with comment hints.
src/codegen/generateParamParsers.ts (4)
16-22
: Native parsers/types block is well-typedThe const assertions and satisfies guarantee the mapping stays in sync.
23-38
: Warning logic is correct and minimalTraversal + native filter is right. Keeping console.warn is consistent with the test harness that spies on it.
69-88
: Import selection logic is correct; no path normalization hereCustom overrides native and aliases are stable. Per retrieved learnings, path normalization is handled elsewhere, so using absolutePath directly here is appropriate.
51-67
: RenameparparsersMap
→paramParsersMap
The only remaining occurrences are in src/codegen/generateParamParsers.ts:
- Line 53: parameter declaration
- Line 57:
if (parparsersMap.has(param.parser))
- Line 58:
return parparsersMap.get(param.parser)!.typeName
After renaming these three references, the typo is fully resolved and no other files need changes.
src/core/definePage.ts (2)
295-299
: Good: normalize query shorthand to full form.Normalizing
'int'
→{ parser: 'int' }
simplifies consumers and matchestreeNodeValue.ts
expectations.
229-260
: Ensure emptyDefinePageInfo
is handled as “no info” downstreamAfter auditing,
extractDefinePageInfo()
always returns a (possibly empty) object and nevernull
/undefined
once invoked. Downstream checks insrc/core/context.ts
(around line 165) do not guard against an empty object’s truthiness, e.g.:const definedPageInfo = extractDefinePageInfo(content, filePath) // … if (node.hasDefinePage || definedPageInfo) { // This block will run even when definedPageInfo is {} }To avoid treating
{}
as valid info:
- Update downstream logic to explicitly check for needed keys, e.g.
if (definedPageInfo.name || definedPageInfo.path || definedPageInfo.params) { … }
- Or have
extractDefinePageInfo()
returnnull
whenrouteInfo
is empty.Please revise the checks in
src/core/context.ts
(around line 165) to distinguish an empty info object from real route data.
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
♻️ Duplicate comments (1)
playground-experimental/src/pages/[...path].vue (1)
8-11
: Use route.query for query fields and guard logsThese are query params; also avoid prod logs. (Repeat of a prior comment.)
-console.log('typeof', typeof route.params.active) -console.log('value', route.params.active) -console.log('typeof', typeof route.params.other) -console.log('value', route.params.other) +if (import.meta.env.DEV) { + console.log('typeof', typeof route.query.active) + console.log('value', route.query.active) + console.log('typeof', typeof route.query.other) + console.log('value', route.query.other) +}
🧹 Nitpick comments (11)
playground-experimental/src/pages/events/repeat/[when=date]+.vue (1)
3-3
: Guard console logs for dev onlyAvoid noisy logs in prod; keep them in DEV.
-console.log(route.params.when) +if (import.meta.env.DEV) console.log(route.params.when)playground-experimental/src/params/date.ts (2)
12-19
: Anchor the ISO-midnight replaceUse a regex with end anchor to prevent accidental mid-string replacements.
- .replace('T00:00:00.000Z', '') + .replace(/T00:00:00\.000Z$/, '')
22-31
: Optional: make parser generics explicitHelps readability; no behavior change.
-export const parser = defineParamParser({ +export const parser = defineParamParser<Date, string>({playground-experimental/src/pages/[...path].vue (1)
5-7
: Remove unused variable and no-op expressionAvoid lint noise and dead code.
-const router = useRouter() -route.params.path +// Keep if you need to touch types; otherwise remove: +// route.params.pathsrc/core/context.ts (4)
71-73
: Exclude declaration files from param-parser matching.Avoid picking up .d.ts which won’t export runtime parsers.
Apply:
- const PARAM_PARSER_GLOB = '*.{ts,js}' - const isParamParserMatch = picomatch(PARAM_PARSER_GLOB) + const PARAM_PARSER_GLOB = '*.{ts,js}' + const isParamParserMatch = picomatch([PARAM_PARSER_GLOB, '!*.d.ts'])
131-151
: Filter .d.ts and warn on duplicates during initial scan.Prevents invalid entries and makes overwrites explicit.
- return glob(PARAM_PARSER_GLOB, { + return glob(PARAM_PARSER_GLOB, { cwd: folder, onlyFiles: true, expandDirectories: false, + ignore: ['**/*.d.ts'], }).then((paramParserFiles) => { for (const file of paramParserFiles) { const name = parsePathe(file).name // TODO: could be simplified to only one import that starts with / for vite const absolutePath = resolve(folder, file) + const previous = paramParsersMap.get(name) + if (previous) { + logger.warn( + `Duplicate param parser "${name}": "${absolutePath}" overwrites "${previous.absolutePath}"` + ) + } paramParsersMap.set(name, { name, typeName: `Param_${name}`, absolutePath, relativePath: relative(options.root, absolutePath), }) }
375-377
: Typo in TODO comment.“copmlete” → “complete”.
- // TODO: parser auto copmlete for definePage + // TODO: parser auto complete for definePage
211-229
: UnifyrelativePath
storage and add duplicate-warning in watcherThe code generator already prefixes
./
in imports:`import('./${relativePath}')` // ⇒ import('./parsers/foo')Storing
relativePath
with its own./
causesimport('././parsers/foo')
. Remove the prefix in the watcher and log a warning if a parser name is re-added.– In
setupParamParserWatcher
(src/core/context.ts):
- Add a warning when
paramParsersMap
already has the same name.- Store
relativePath
without the leading./
..on('add', (file) => { const name = parsePathe(file).name const absolutePath = resolve(cwd, file) + const previous = paramParsersMap.get(name) + if (previous) { + logger.warn( + `Duplicate param parser "${name}": "${absolutePath}" overwrites "${previous.absolutePath}"` + ) + } paramParsersMap.set(name, { name, typeName: `Param_${name}`, absolutePath, - relativePath: './' + relative(options.root, absolutePath), + relativePath: relative(options.root, absolutePath), }) writeConfigFiles() })src/core/tree.ts (2)
17-20
: Type alias is fine; consider a brief doc.Optional: add a one-liner describing the encoding (strings for static, 0 splat, 1 param, arrays for multi-subsegments).
356-383
: Unify numeric encoding for matcher parts (avoid 0-only in multi-segment case).Single-subsegment uses 0 (splat) and 1 (param), but multi-subsegment maps any param to 0, losing the 1 distinction. If consumers rely on 1 vs 0, this is inconsistent. Reuse the already computed subSegments array in both branches.
Apply this diff:
const subSegments = node.value.subSegments.map((segment) => typeof segment === 'string' ? segment : // param segment.isSplat ? 0 : 1 ) if (subSegments.length > 1) { - parts.unshift( - node.value.subSegments.map( - (segment) => (typeof segment === 'string' ? segment : 0) /* param */ - ) - ) + parts.unshift(subSegments as Array<string | number>) } else if (subSegments.length === 1) { parts.unshift(subSegments[0]!) }src/codegen/generateRouteParams.ts (1)
30-65
: Optional query params in experimental path: align undefined/null semantics with non-experimental.Current logic adds “| null” for optional non-repeatable path params, but optional query params never include undefined (or null) in the type when not raw. To mirror ParamValue* behavior:
- For optional query params: add “| undefined” (and “| null” when isRaw).
- Keep existing property “?” for isRaw.
Apply this diff:
- extractedType += - isTreePathParam(param) && isOptional && !isRepeatable - ? ' | null' - : '' + if (isOptional) { + if (isTreePathParam(param)) { + if (!isRepeatable) extractedType += ' | null' + } else { + // query param + extractedType += isRaw ? ' | undefined | null' : ' | undefined' + } + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
package.json
(3 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/pages/[...path].vue
(1 hunks)playground-experimental/src/pages/events/repeat/[when=date]+.vue
(1 hunks)playground-experimental/src/pages/users/[userId=int].vue
(1 hunks)playground-experimental/src/params/date.ts
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)src/codegen/generateRouteParams.ts
(2 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/context.ts
(12 hunks)src/core/definePage.ts
(4 hunks)src/core/tree.ts
(3 hunks)src/core/treeNodeValue.ts
(16 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- src/codegen/generateRouteResolver.ts
- playground-experimental/src/pages/users/[userId=int].vue
- playground-experimental/package.json
- src/core/treeNodeValue.ts
- src/core/definePage.ts
- src/codegen/generateRouteResolver.spec.ts
- package.json
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:137-142
Timestamp: 2025-08-23T12:50:36.614Z
Learning: In src/core/context.ts, param parser relative paths must be computed relative to options.root (not relative to DTS file location) and the './' prefix is required for proper path resolution in the generated code.
📚 Learning: 2025-08-23T12:49:31.747Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/tree.ts:313-331
Timestamp: 2025-08-23T12:49:31.747Z
Learning: The regexp getter in src/core/tree.ts correctly processes the root node to add the leading slash. The empty pathSegment from root becomes an empty string when escaped, but the concatenation logic `"" + (re ? '\\/' : '') + re` properly adds the leading slash that routes require.
Applied to files:
src/core/tree.ts
📚 Learning: 2025-08-15T16:11:02.627Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.627Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/core/tree.ts
src/core/context.ts
📚 Learning: 2025-08-23T12:50:36.614Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:137-142
Timestamp: 2025-08-23T12:50:36.614Z
Learning: In src/core/context.ts, param parser relative paths must be computed relative to options.root (not relative to DTS file location) and the './' prefix is required for proper path resolution in the generated code.
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-23T12:54:44.918Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-150
Timestamp: 2025-08-23T12:54:44.918Z
Learning: In src/core/context.ts, when paramParsers is defined in resolved options, the dir property is guaranteed to be an array, so optional chaining on options.experimental.paramParsers?.dir.map() is sufficient protection against undefined values.
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-16T13:00:51.271Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.271Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-16T13:01:42.709Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.709Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
src/core/context.ts
playground-experimental/typed-router.d.ts
🧬 Code graph analysis (4)
src/core/tree.ts (2)
src/core/treeNodeValue.ts (5)
TreePathParam
(285-292)TreeQueryParam
(294-307)params
(146-148)re
(393-406)escapeRegex
(360-361)src/core/extendRoutes.ts (1)
params
(177-179)
src/core/context.ts (7)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(5-13)warnMissingParamParsers
(23-38)generateParamParsersTypesDeclarations
(40-49)src/core/definePage.ts (1)
extractDefinePageInfo
(200-261)src/core/utils.ts (1)
ImportsMap
(310-379)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(66-102)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)
src/codegen/generateRouteParams.ts (1)
src/core/treeNodeValue.ts (3)
isTreeParamOptional
(314-321)isTreeParamRepeatable
(328-335)isTreePathParam
(342-346)
playground-experimental/typed-router.d.ts (1)
playground-experimental/src/router.ts (1)
RouteNamedMap
(21-36)
🔇 Additional comments (14)
playground-experimental/typed-router.d.ts (2)
8-10
: Param_date import path looks correctRoot-relative with './' prefix matches the generator’s expectations.
31-33
: Correct array vs single param typingUsing Extract/Exclude for repeat vs single params is spot on.
src/core/context.ts (6)
3-32
: Imports look consistent and scoped.New imports align with usage below; no issues.
56-58
: Watcher union type is fine; map init OK.The union supports both chokidar and custom watcher; close() exists on both.
109-129
: Good: param-parser watchers respect startWatchers.Conditional registration matches routes’ behavior.
168-177
: Switch to extractDefinePageInfo is correct.Merged info into custom route block cleanly.
252-301
: Resolver generator + HMR wrapper LGTM.API surface and hot-reload flow are coherent with routes HMR.
360-373
: DTS: guarded warning and param-parsers integration are correct.Optional chaining avoids crashes; wiring to routeNamedMap/types is sound.
src/core/tree.ts (4)
4-8
: Using escapeRegex for static segments is correct.This resolves prior concerns about unescaped metacharacters in static path segments.
274-287
: Param order is root-to-leaf; confirm caller alignment.node.params/pathParams build root→leaf order. Please verify generators using index-based mapping (e.g., experimental param types) assume the same order and inclusion (query vs path).
Would you like a quick grep script to check call sites that construct the parallel types array?
Also applies to: 292-312
313-331
: Regexp builder LGTM; keep root processing.Root’s empty segment is needed to add the leading slash. Current logic is correct and matches the resolved guidance.
333-343
: Score/isSplat getters read well.Excludes root consistently; splat detection is straightforward.
Also applies to: 348-350
src/codegen/generateRouteParams.ts (2)
8-28
: Path-only route params generation reads clean.Using node.pathParams and the ParamValue* wrappers keeps raw/non-raw semantics consistent.
30-65
: No mismatches in types orderingThe
types
array is produced bygenerateParamsTypes(node.params, …)
, andEXPERIMENTAL_generateRouteParams
iterates over the samenode.params
array. This ensures eachtypes[i]
aligns withnode.params[i]
, including path→query ordering.
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
♻️ Duplicate comments (1)
src/codegen/generateRouteResolver.spec.ts (1)
770-770
: Fix wrong fixture path for '/b/e/f' (update snapshots too).Use 'b/e/f.vue' to match the inserted route; snapshots should import the same file.
- tree.insert('b/e/f', 'b/c/f.vue') + tree.insert('b/e/f', 'b/e/f.vue')Also update inline snapshots within this file:
- 'default': () => import('b/c/f.vue') + 'default': () => import('b/e/f.vue')Run the quick fix:
#!/bin/bash rg -n "b/c/f\.vue" src/codegen/generateRouteResolver.spec.ts sed -i "s|b/c/f.vue|b/e/f.vue|g" src/codegen/generateRouteResolver.spec.tsAlso applies to: 1015-1015
🧹 Nitpick comments (5)
src/codegen/generateRouteResolver.spec.ts (3)
560-560
: Grammar: “does not includes” → “does not include”.Keeps test descriptions clean.
-it('does not includes query params from parent nodes', () => { +it('does not include query params from parent nodes', () => {
839-843
: Fix typos in FIXME comment.“appeand” → “append”; tighten phrasing.
-// it should appeand a trailing slash to the children route or the parent +// it should append a trailing slash to the child route or the parent -// I think it's better to not have a stable routing to preserve stable trailing slash +// Better to preserve trailing-slash behavior without forcing a “stable” routing order
916-927
: Make route-order extraction resilient to spacing/alignment.Avoid brittle dependency on exact spaces before //.
- // Extract the order from the resolver output - const lines = resolver.split('\n').filter((line) => line.includes('// /')) - return lines.map((line) => line.split('// ')[1] || '') + // Extract the order from the resolver output (robust to spacing) + return Array.from( + resolver.matchAll(/\/\/\s*(\/[^\s]+)\s*$/gm), + (m) => m[1] + )src/core/utils.ts (1)
333-342
: Clarify has() doc: parameter is the alias/key, not the imported nameInternally the map key is the local alias (the “as” identifier). Consider adjusting the JSDoc to avoid confusion.
- /** - * Check if the given path has the given import name. + /** + * Check if the given path has the given import alias (the local identifier). * * @param path - the path to check - * @param name - the import name to check + * @param name - the local alias to check (e.g. 'useRouter' or 'MyUseRouter') */src/core/tree.ts (1)
316-363
: Regex builder handles optional-first segments and separators correctly; minor modernizationsLogic for optional single-param segments and leading slash is sound. Prefer let/const over var for clarity.
- let re = '' - for (var i = 0; i < nodeList.length; i++) { + let re = '' + for (let i = 0; i < nodeList.length; i++) { node = nodeList[i]! - if (node.value.isParam()) { - var nodeRe = node.value.re + if (node.value.isParam()) { + const nodeRe = node.value.re
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/core/tree.ts
(3 hunks)src/core/treeNodeValue.ts
(16 hunks)src/core/utils.ts
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/core/treeNodeValue.ts
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-08-15T16:11:02.627Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.627Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/codegen/generateRouteResolver.spec.ts
src/core/tree.ts
📚 Learning: 2025-08-23T12:50:36.614Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:137-142
Timestamp: 2025-08-23T12:50:36.614Z
Learning: In src/core/context.ts, param parser relative paths must be computed relative to options.root (not relative to DTS file location) and the './' prefix is required for proper path resolution in the generated code.
Applied to files:
src/codegen/generateRouteResolver.spec.ts
📚 Learning: 2025-08-16T13:01:42.709Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.709Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
src/codegen/generateRouteResolver.spec.ts
📚 Learning: 2025-08-23T12:44:23.819Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/utils.ts:202-212
Timestamp: 2025-08-23T12:44:23.819Z
Learning: In JavaScript, spreading undefined or null values in object literals is safe and does not throw errors. `{ ...undefined }` and `{ ...null }` both result in empty objects `{}`. The spread operator ignores falsy values like undefined and null.
Applied to files:
src/core/utils.ts
📚 Learning: 2025-08-23T12:54:44.918Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-150
Timestamp: 2025-08-23T12:54:44.918Z
Learning: In src/core/context.ts, when paramParsers is defined in resolved options, the dir property is guaranteed to be an array, so optional chaining on options.experimental.paramParsers?.dir.map() is sufficient protection against undefined values.
Applied to files:
src/core/utils.ts
📚 Learning: 2025-08-23T12:49:31.747Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/tree.ts:313-331
Timestamp: 2025-08-23T12:49:31.747Z
Learning: The regexp getter in src/core/tree.ts correctly processes the root node to add the leading slash. The empty pathSegment from root becomes an empty string when escaped, but the concatenation logic `"" + (re ? '\\/' : '') + re` properly adds the leading slash that routes require.
Applied to files:
src/core/tree.ts
🧬 Code graph analysis (3)
src/codegen/generateRouteResolver.spec.ts (6)
src/index.ts (1)
DEFAULT_OPTIONS
(28-28)src/options.ts (2)
DEFAULT_OPTIONS
(249-266)resolveOptions
(313-395)src/codegen/generateRouteResolver.ts (4)
generateRouteRecord
(107-219)generateRouteRecordPath
(245-268)generateRouteRecordQuery
(273-317)generateRouteResolver
(66-102)src/core/utils.ts (1)
ImportsMap
(310-395)src/core/tree.ts (1)
PrefixTree
(455-489)src/codegen/generateParamParsers.ts (1)
ParamParsersMap
(5-13)
src/core/utils.ts (3)
src/core/treeNodeValue.ts (2)
TreePathParam
(285-292)path
(91-93)src/core/tree.ts (2)
path
(223-228)name
(244-251)src/core/extendRoutes.ts (4)
path
(138-140)path
(145-155)name
(90-92)name
(97-99)
src/core/tree.ts (2)
src/core/treeNodeValue.ts (5)
TreePathParam
(285-292)TreeQueryParam
(294-307)params
(146-148)re
(393-416)escapeRegex
(360-361)src/core/extendRoutes.ts (1)
params
(177-179)
⏰ 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: Socket Security: Pull Request Alerts
- GitHub Check: Cloudflare Pages
🔇 Additional comments (11)
src/core/utils.ts (3)
2-2
: Type import switch to TreePathParam looks correctMatches the updated interfaces in treeNodeValue.ts.
122-134
: paramToName implementation is consistent with new TreePathParamNo issues spotted.
202-212
: Confirm intended shape change for params overrideThis branch always creates both params.path and params.query objects when params exists on either side. Previously, the merged value could omit one (or both) keys entirely. Verify downstream code doesn’t treat an empty object as “explicit override present”.
src/core/tree.ts (8)
4-8
: New imports align with added featuresImporting escapeRegex and the param types fits the new getters and regex builder.
17-19
: TreeNodeValueMatcherPart alias is fineSignature matches usage in matcherPatternPathDynamicParts.
274-287
: params aggregation (path + query) across ancestorsOrder (ancestors first) looks intentional and consistent. LGTM.
292-304
: pathParams aggregation is correctOnly path params, with ancestors first. LGTM.
306-312
: queryParams getter scope is clearReturns only this node’s query params as documented. LGTM.
365-375
: score aggregation looks goodAncestor-to-leaf ordering preserved. LGTM.
380-382
: isSplat is correct and cheapDirectly checks current node params. LGTM.
417-425
: isMatchable guard is appropriateMatches intended semantics (has component and a non-false name).
dc3f8ef
to
e5a73fc
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: 3
♻️ Duplicate comments (4)
src/runtime.ts (1)
89-96
: Broken TSDoc link.Replace placeholder URL.
- * and any parser in the {@link https://uvr.esm.is/TODO | params folder }. If + * and any parser in the params folder. Ifsrc/core/context.ts (3)
71-73
: Good: narrowed param-parser glob to code files.This addresses the earlier glob('*') issue; combined with picomatch it avoids non-code.
109-129
: Watcher setup gated by startWatchers and cwd passed explicitly.Matches prior guidance; avoids accessing watcher.options and respects startWatchers.
211-229
: Watcher helper refactor looks solid.Passing cwd explicitly and dropping watcher.options access removes a TS/runtime hazard.
🧹 Nitpick comments (28)
playground-experimental/tsconfig.config.json (1)
4-7
: Consider enforcing TS 5/Vite-friendly module resolution (if not inherited).If
@vue/tsconfig
doesn’t already set it, addmoduleResolution: "Bundler"
to match Vite/Vitest setups.Apply if needed:
"compilerOptions": { "composite": true, - "types": ["node"] + "types": ["node"], + "moduleResolution": "Bundler" }playground-experimental/src/params/date.ts (4)
3-10
: Stabilize YYYY-MM-DD parsing across environments.Avoid engine quirks by normalizing bare dates to UTC midnight before fallback parsing.
Apply:
-function toDate(value: string): Date { - const asDate = new Date(value) +function toDate(value: string): Date { + const v = value.trim() + const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(v) + const asDate = m ? new Date(Date.UTC(+m[1], +m[2] - 1, +m[3])) : new Date(v) if (Number.isNaN(asDate.getTime())) { throw miss(`Invalid date: "${value}"`) } - - return asDate + return asDate }
12-20
: Minor nit: cheaper string op for midnight dates.endsWith + slice avoids a generic replace and is a touch clearer.
-function toString(value: Date): string { - return ( - value - .toISOString() - // allows keeping simple dates like 2023-10-01 without time - // while still being able to parse full dates like 2023-10-01T12:00:00.000Z - .replace('T00:00:00.000Z', '') - ) -} +function toString(value: Date): string { + const iso = value.toISOString() + return iso.endsWith('T00:00:00.000Z') ? iso.slice(0, 10) : iso +}
23-26
: Don’t treat empty string as “missing”.Use a nullish check so '' surfaces as an invalid date (with message) instead of a generic miss().
- if (!value) { + if (value == null) { throw miss() }
1-1
: Optional: lock the public type.Annotating parser with ParamParser<Date, string> makes intent explicit and guards future API drift.
-import { defineParamParser, miss } from 'vue-router/experimental' +import { defineParamParser, miss } from 'vue-router/experimental' +import type { ParamParser } from 'vue-router/experimental' @@ -export const parser = defineParamParser({ +export const parser: ParamParser<Date, string> = defineParamParser({Also applies to: 22-22
package.json (2)
142-142
: Keep @babel/generator in dependencies — runtime import found; consider version alignmentgenerate is imported in src/core/definePage.ts:20, so keep it in dependencies. Optionally align @babel/generator's minor with @babel/types in package.json for consistency.
175-176
: Add "engines.node": ">=18.18.0" to top-level package.json (align workspace packages).Verification: no package.json contains an engines.node field — package.json, docs/package.json, examples/nuxt/package.json, playground/package.json, playground-experimental/package.json.
Add (top-level package.json) — outside this hunk:
"license": "MIT", + "engines": { + "node": ">=18.18.0" + },Optionally add the same engines field to the workspace package.json files for consistency.
playground-experimental/src/pages/a.[b].c.[d].vue (1)
1-1
: Remove empty script block.Not needed; drop to reduce noise.
-<script lang="ts" setup></script> +playground-experimental/src/pages/blog/info/(info).vue (1)
1-1
: Remove empty script block.No script logic; safe to remove.
-<script lang="ts" setup></script> +playground-experimental/src/router.ts (2)
11-13
: Honor Vite base URL.Use
createWebHistory(import.meta.env.BASE_URL)
for subpath deployments.- history: createWebHistory(), + history: createWebHistory(import.meta.env.BASE_URL),
20-37
: Consider moving type augmentation to a .d.ts.Keep runtime and types separate; place this
declare module
in a dedicatedrouter-augment.d.ts
.src/core/extendRoutes.spec.ts (9)
72-79
: toMatchObject is fine; add a length guard.Looser matching can hide extra params. Add an explicit length check.
- expect(child.params).toMatchObject([ + expect(child.params).toHaveLength(1) + expect(child.params).toMatchObject([ { paramName: 'id', modifier: '', optional: false, repeatable: false, parser: null, isSplat: false, }, - ]) + ])
93-101
: Also assert length when using toMatchObject.Prevents accidental extra entries.
- expect(child.params).toMatchObject([ + expect(child.params).toHaveLength(1) + expect(child.params).toMatchObject([ { paramName: 'id', parser: null, modifier: '+', optional: false, repeatable: true, isSplat: false, }, - ]) + ])
114-129
: Guard array size for multi-param case.Ensure exactly two params.
- expect(node.params).toMatchObject([ + expect(node.params).toHaveLength(2) + expect(node.params).toMatchObject([
141-163
: Multi‑param with modifiers: assert length, too.Same rationale.
- expect(node.params).toMatchObject([ + expect(node.params).toHaveLength(3) + expect(node.params).toMatchObject([
174-182
: Custom regex case: add length assertion.- expect(node.params).toMatchObject([ + expect(node.params).toHaveLength(1) + expect(node.params).toMatchObject([
193-201
: Empty regex case: add length assertion.- expect(node.params).toMatchObject([ + expect(node.params).toHaveLength(1) + expect(node.params).toMatchObject([
212-220
: Modifier + custom regex: assert length.- expect(node.params).toMatchObject([ + expect(node.params).toHaveLength(1) + expect(node.params).toMatchObject([
231-239
: Modifier + empty regex: assert length.- expect(node.params).toMatchObject([ + expect(node.params).toHaveLength(1) + expect(node.params).toMatchObject([
251-259
: Splat case: assert length.- expect(child.params).toMatchObject([ + expect(child.params).toHaveLength(1) + expect(child.params).toMatchObject([src/core/definePage.ts (3)
200-261
: Return type includes null but never returned.Tighten to
DefinePageInfo | undefined
.-export function extractDefinePageInfo( +export function extractDefinePageInfo( sfcCode: string, id: string -): DefinePageInfo | null | undefined { +): DefinePageInfo | undefined {
428-465
: Typo: “filtere” → “filtered”.Minor doc polish.
- * Generate a filtere import statement based on a set of identifiers that should be kept. + * Generate a filtered import statement based on a set of identifiers that should be kept.
359-377
: Validate path parser values against the project's ParamParsers and warn on unknowns.Check prop.value.value against the native parser names and the project's ParamParsersMap (see src/codegen/generateParamParsers.ts → ParamParsersMap and src/core/context.ts → warnMissingParamParsers); emit a warning when it's not found.
src/core/tree.ts (1)
17-20
: Document numeric markers.0 = splat, 1 = param is implicit. Consider named enum/const for clarity.
const MATCH_SPLAT = 0 as const const MATCH_PARAM = 1 as constsrc/core/context.ts (4)
136-146
: Warn on duplicate param-parser names to avoid silent overrides.If two folders contain the same basename, the later entry overwrites the former. Emit a warning before set().
- for (const file of paramParserFiles) { + for (const file of paramParserFiles) { const name = parsePathe(file).name // TODO: could be simplified to only one import that starts with / for vite const absolutePath = resolve(folder, file) + if (paramParsersMap.has(name)) { + logger.warn(`Duplicate param parser "${name}" from ${absolutePath} (overwriting).`) + } paramParsersMap.set(name, { name, typeName: `Param_${name}`, absolutePath, - relativePath: relative(options.root, absolutePath), + relativePath: './' + relative(options.root, absolutePath), }) }
141-145
: Align relativePath format with watcher “add” for consistency.Initial scan stores relativePath without './' while watcher adds it with './'. Pick one consistently. Given prior decision to include './' relative to options.root, update the initial scan.
- relativePath: relative(options.root, absolutePath), + relativePath: './' + relative(options.root, absolutePath),Note: applying this keeps both paths consistent and avoids flip-flopping in generated DTS. [retrieved_learnings used]
215-223
: Mirror duplicate-name warning on HMR “add”.Add the same overwrite warning here to keep runtime behavior transparent.
.on('add', (file) => { const name = parsePathe(file).name const absolutePath = resolve(cwd, file) + if (paramParsersMap.has(name)) { + logger.warn(`Duplicate param parser "${name}" from ${absolutePath} (overwriting).`) + } paramParsersMap.set(name, { name, typeName: `Param_${name}`, absolutePath, relativePath: './' + relative(options.root, absolutePath), }) writeConfigFiles() })
421-426
: Clear watchers array after closing to allow re-scan.Without clearing, a later scanPages() won’t run after stopWatcher().
function stopWatcher() { if (watchers.length) { logger.log('🛑 stopping watcher') - watchers.forEach((watcher) => watcher.close()) + watchers.forEach((watcher) => watcher.close()) + watchers.length = 0 } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
src/codegen/__snapshots__/generateRouteRecords.spec.ts.snap
is excluded by!**/*.snap
src/core/__snapshots__/definePage.spec.ts.snap
is excluded by!**/*.snap
📒 Files selected for processing (70)
client.d.ts
(2 hunks)examples/nuxt/package.json
(1 hunks)package.json
(3 hunks)playground-experimental/.gitignore
(1 hunks)playground-experimental/auto-imports.d.ts
(1 hunks)playground-experimental/db.json
(1 hunks)playground-experimental/env.d.ts
(1 hunks)playground-experimental/index.html
(1 hunks)playground-experimental/package.json
(1 hunks)playground-experimental/src/App.vue
(1 hunks)playground-experimental/src/main.ts
(1 hunks)playground-experimental/src/page-outside.vue
(1 hunks)playground-experimental/src/pages/(home).vue
(1 hunks)playground-experimental/src/pages/[...path].vue
(1 hunks)playground-experimental/src/pages/a.[b].c.[d].vue
(1 hunks)playground-experimental/src/pages/b.vue
(1 hunks)playground-experimental/src/pages/blog/[[slugOptional]]+.vue
(1 hunks)playground-experimental/src/pages/blog/[slug]+.vue
(1 hunks)playground-experimental/src/pages/blog/info/(info).vue
(1 hunks)playground-experimental/src/pages/blog/info/[[section]].vue
(1 hunks)playground-experimental/src/pages/events/[when=date].vue
(1 hunks)playground-experimental/src/pages/events/repeat/[when=date]+.vue
(1 hunks)playground-experimental/src/pages/tests/[[optional]]/end.vue
(1 hunks)playground-experimental/src/pages/u[name].vue
(1 hunks)playground-experimental/src/pages/u[name]/24.vue
(1 hunks)playground-experimental/src/pages/u[name]/[userId=int].vue
(1 hunks)playground-experimental/src/pages/users/[userId=int].vue
(1 hunks)playground-experimental/src/pages/users/sub-[first]-[second].vue
(1 hunks)playground-experimental/src/params/date.ts
(1 hunks)playground-experimental/src/router.ts
(1 hunks)playground-experimental/tsconfig.config.json
(1 hunks)playground-experimental/tsconfig.json
(1 hunks)playground-experimental/typed-router.d.ts
(1 hunks)playground-experimental/vite.config.ts
(1 hunks)playground/src/pages/(some-layout).vue
(1 hunks)playground/src/params/date.ts
(1 hunks)playground/src/params/number.ts
(1 hunks)playground/tsconfig.json
(2 hunks)playground/vite.config.ts
(2 hunks)pnpm-workspace.yaml
(1 hunks)src/codegen/generateDTS.ts
(2 hunks)src/codegen/generateParamParsers.spec.ts
(1 hunks)src/codegen/generateParamParsers.ts
(1 hunks)src/codegen/generateRouteMap.spec.ts
(20 hunks)src/codegen/generateRouteMap.ts
(1 hunks)src/codegen/generateRouteParams.ts
(2 hunks)src/codegen/generateRouteRecords.ts
(2 hunks)src/codegen/generateRouteResolver.spec.ts
(1 hunks)src/codegen/generateRouteResolver.ts
(1 hunks)src/core/context.ts
(12 hunks)src/core/customBlock.ts
(2 hunks)src/core/definePage.spec.ts
(10 hunks)src/core/definePage.ts
(4 hunks)src/core/extendRoutes.spec.ts
(9 hunks)src/core/extendRoutes.ts
(2 hunks)src/core/moduleConstants.ts
(2 hunks)src/core/tree.spec.ts
(5 hunks)src/core/tree.ts
(3 hunks)src/core/treeNodeValue.ts
(16 hunks)src/core/utils.ts
(4 hunks)src/core/vite/index.ts
(2 hunks)src/data-loaders/meta-extensions.ts
(1 hunks)src/index.ts
(6 hunks)src/options.ts
(3 hunks)src/runtime.ts
(2 hunks)src/types.ts
(1 hunks)src/utils/index.ts
(2 hunks)tsconfig.json
(2 hunks)vitest.config.ts
(1 hunks)vitest.workspace.js
(0 hunks)
💤 Files with no reviewable changes (1)
- vitest.workspace.js
✅ Files skipped from review due to trivial changes (3)
- playground-experimental/.gitignore
- playground-experimental/db.json
- examples/nuxt/package.json
🚧 Files skipped from review as they are similar to previous changes (53)
- playground-experimental/src/pages/blog/info/[[section]].vue
- playground-experimental/src/pages/u[name]/[userId=int].vue
- playground-experimental/src/pages/(home).vue
- playground-experimental/src/main.ts
- src/utils/index.ts
- playground-experimental/src/pages/u[name]/24.vue
- playground-experimental/auto-imports.d.ts
- playground-experimental/src/pages/tests/[[optional]]/end.vue
- vitest.config.ts
- playground/src/params/number.ts
- src/codegen/generateRouteResolver.spec.ts
- playground-experimental/src/pages/blog/[slug]+.vue
- playground-experimental/src/pages/users/sub-[first]-[second].vue
- pnpm-workspace.yaml
- src/core/vite/index.ts
- playground-experimental/src/pages/events/[when=date].vue
- playground/src/params/date.ts
- src/core/tree.spec.ts
- tsconfig.json
- src/core/customBlock.ts
- src/core/extendRoutes.ts
- src/core/moduleConstants.ts
- src/codegen/generateRouteMap.ts
- src/codegen/generateRouteRecords.ts
- playground-experimental/package.json
- playground-experimental/src/page-outside.vue
- src/codegen/generateDTS.ts
- playground-experimental/tsconfig.json
- playground-experimental/src/App.vue
- playground-experimental/env.d.ts
- playground-experimental/src/pages/[...path].vue
- playground-experimental/src/pages/u[name].vue
- src/options.ts
- playground-experimental/src/pages/users/[userId=int].vue
- src/codegen/generateParamParsers.spec.ts
- client.d.ts
- playground-experimental/vite.config.ts
- src/data-loaders/meta-extensions.ts
- playground-experimental/src/pages/blog/[[slugOptional]]+.vue
- src/core/utils.ts
- playground/tsconfig.json
- src/codegen/generateRouteResolver.ts
- src/codegen/generateParamParsers.ts
- playground/vite.config.ts
- src/core/definePage.spec.ts
- src/codegen/generateRouteMap.spec.ts
- playground/src/pages/(some-layout).vue
- playground-experimental/src/pages/events/repeat/[when=date]+.vue
- playground-experimental/src/pages/b.vue
- playground-experimental/index.html
- src/types.ts
- playground-experimental/typed-router.d.ts
- src/codegen/generateRouteParams.ts
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-08-23T12:54:44.918Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-150
Timestamp: 2025-08-23T12:54:44.918Z
Learning: In src/core/context.ts, when paramParsers is defined in resolved options, the dir property is guaranteed to be an array, so optional chaining on options.experimental.paramParsers?.dir.map() is sufficient protection against undefined values.
Applied to files:
src/runtime.ts
src/core/context.ts
📚 Learning: 2025-08-23T12:49:31.747Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/tree.ts:313-331
Timestamp: 2025-08-23T12:49:31.747Z
Learning: The regexp getter in src/core/tree.ts correctly processes the root node to add the leading slash. The empty pathSegment from root becomes an empty string when escaped, but the concatenation logic `"" + (re ? '\\/' : '') + re` properly adds the leading slash that routes require.
Applied to files:
src/core/tree.ts
src/core/treeNodeValue.ts
📚 Learning: 2025-08-15T16:11:02.627Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateRouteResolver.ts:0-0
Timestamp: 2025-08-15T16:11:02.627Z
Learning: In src/codegen/generateRouteResolver.ts, the user wants comment alignment preserved in the generated resolver code, even when fixing potential runtime errors with String.repeat().
Applied to files:
src/core/tree.ts
src/core/treeNodeValue.ts
src/core/context.ts
📚 Learning: 2025-08-16T13:01:42.709Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/codegen/generateParamParsers.ts:91-94
Timestamp: 2025-08-16T13:01:42.709Z
Learning: In the unplugin-vue-router codebase, path normalization for import specifiers is handled elsewhere in the system (not at individual call sites like generateParamParsers.ts). Individual functions should not normalize paths themselves as it violates separation of concerns.
Applied to files:
src/core/treeNodeValue.ts
src/index.ts
src/core/context.ts
📚 Learning: 2025-08-23T12:50:36.614Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:137-142
Timestamp: 2025-08-23T12:50:36.614Z
Learning: In src/core/context.ts, param parser relative paths must be computed relative to options.root (not relative to DTS file location) and the './' prefix is required for proper path resolution in the generated code.
Applied to files:
src/core/context.ts
📚 Learning: 2025-08-16T13:00:51.271Z
Learnt from: posva
PR: posva/unplugin-vue-router#700
File: src/core/context.ts:109-147
Timestamp: 2025-08-16T13:00:51.271Z
Learning: In src/core/context.ts, the options parameter is already resolved, meaning options.experimental.paramMatchers is either an object with a dir property or undefined - never a boolean. The boolean normalization happens earlier in the options resolution phase.
Applied to files:
src/core/context.ts
🧬 Code graph analysis (7)
playground-experimental/src/router.ts (2)
playground-experimental/typed-router.d.ts (1)
RouteNamedMap
(23-41)src/codegen/generateRouteParams.ts (1)
ParamValue
(98-100)
src/runtime.ts (2)
client.d.ts (1)
TypesConfig
(38-40)src/data-loaders/entries/index.ts (1)
TypesConfig
(41-41)
src/core/tree.ts (2)
src/core/treeNodeValue.ts (5)
TreePathParam
(285-292)TreeQueryParam
(294-307)params
(146-148)re
(393-416)escapeRegex
(360-361)src/core/extendRoutes.ts (1)
params
(177-179)
src/core/treeNodeValue.ts (3)
src/core/customBlock.ts (1)
CustomRouteBlockQueryParamOptions
(36-41)src/core/tree.ts (3)
queryParams
(309-311)pathParams
(292-304)params
(277-287)src/core/extendRoutes.ts (2)
parent
(64-66)params
(177-179)
src/core/definePage.ts (5)
src/core/customBlock.ts (1)
CustomRouteBlock
(20-34)src/core/tree.ts (3)
params
(277-287)queryParams
(309-311)pathParams
(292-304)src/core/treeNodeValue.ts (2)
params
(146-148)queryParams
(111-140)src/runtime.ts (1)
ParamParserType
(79-83)src/core/utils.ts (1)
warn
(10-15)
src/index.ts (1)
src/core/moduleConstants.ts (6)
MODULE_RESOLVER_PATH
(7-7)routeBlockQueryRE
(41-41)MODULE_ROUTES_PATH
(6-6)MODULE_VUE_ROUTER_AUTO
(4-4)VIRTUAL_PREFIX
(26-26)ROUTES_LAST_LOAD_TIME
(14-21)
src/core/context.ts (8)
src/core/RoutesFolderWatcher.ts (1)
RoutesFolderWatcher
(15-84)src/codegen/generateParamParsers.ts (3)
ParamParsersMap
(5-13)warnMissingParamParsers
(23-38)generateParamParsersTypesDeclarations
(40-49)src/core/definePage.ts (1)
extractDefinePageInfo
(200-261)src/core/utils.ts (1)
ImportsMap
(310-395)src/codegen/generateRouteResolver.ts (1)
generateRouteResolver
(66-102)src/codegen/generateDTS.ts (1)
generateDTS
(18-83)src/codegen/generateRouteMap.ts (1)
generateRouteNamedMap
(9-35)src/codegen/generateRouteFileInfoMap.ts (1)
generateRouteFileInfoMap
(4-54)
🔇 Additional comments (32)
playground-experimental/tsconfig.config.json (2)
1-8
: LGTM for a config-only tsconfig.Extends Vue base, scopes to tool configs, and sets composite for project refs. Looks correct.
6-6
: Resolve — @types/node presentpackage.json includes "@types/node": "^24.3.0", so Node types are available for playground-experimental/tsconfig.config.json.
playground-experimental/src/params/date.ts (2)
22-31
: LGTM overall.Clean parser, good use of miss(), and array handling is straightforward.
23-31
: Confirm empty-array semantics for repeatable params.Should [] be accepted (round-trip to []) or trigger miss()? If miss() is desired, add a guard:
if (value == null) { throw miss() } + if (Array.isArray(value) && value.length === 0) { + throw miss() + }package.json (1)
208-208
: PR tarball for vue-router in devDependencies: ensure reproducibility and revert before release.File: package.json (lines 208)
"vue-router": "https://pkg.pr.new/vue-router@7d6164a",
Before merging/releasing, revert to a reproducible source (commit tarball or semver):
- "vue-router": "https://pkg.pr.new/vue-router@7d6164a", + "vue-router": "^4.5.1",Repo search in the verification environment returned "No files were searched" — cannot confirm no other pkg.pr.new usages. Please verify locally:
#!/bin/bash rg -n --hidden --no-ignore "pkg\.pr\.new" || git grep -n "pkg.pr.new" || trueplayground-experimental/src/pages/blog/info/(info).vue (1)
9-15
: Verify null param behavior with the experimental resolver.Confirm that
section: null
produces/blog/info
(not/blog/info/null
). If unsure, prefer omittingsection
or usingundefined
.- params: { - section: null, - }, + // if the resolver doesn't strip null, omit the param: + params: {},playground-experimental/src/router.ts (2)
15-17
: HMR hook looks correct.Guarded by
import.meta.hot
; no runtime impact in prod.
1-3
: Verify vue-router exposes experimental importspackage.json pins vue-router to "https://pkg.pr.new/vue-router@7d6164a" — confirm this build exports "vue-router/experimental" and "vue-router/auto-resolver" (inspect node_modules/vue-router/package.json "exports" or run node -e "require.resolve('vue-router/experimental')"). Location: playground-experimental/src/router.ts:1-2
src/index.ts (3)
69-85
: Virtual resolver wiring looks correct.Including MODULE_RESOLVER_PATH in resolveId and returning a virtual id is consistent with routes/auto handling.
92-94
: Async buildStart is good; ensures initial scan completes.Awaiting scanPages avoids race conditions on first load.
121-149
: Load branch for auto‑resolver: update time + generateResolver().
Verified — ctx.generateResolver is defined (src/core/context.ts:252).src/core/definePage.ts (2)
192-199
: New DefinePageInfo API: good shape.Lightweight container with optional fields fits downstream merges.
263-284
: Param extractor is fine.Structure mirrors CustomRouteBlock['params'] cleanly.
src/core/tree.ts (8)
4-8
: Importing escapeRegex and param types: good.Keeps concerns in treeNodeValue and avoids duplication.
223-228
: Path getter logic LGTM.Root-children get leading slash; overrides respected.
274-287
: Params aggregation LGTM.Includes ancestors; matches downstream expectations.
292-304
: Path params aggregation LGTM.Filters strictly to path params.
309-311
: Query params passthrough LGTM.Scoped to current node only as intended.
388-411
: Dynamic parts preserve splat markers.Shape matches TreeNodeValueMatcherPart.
417-421
: isMatchable guard LGTM.Simple and effective.
316-363
: Add tests for optional-first segments and verify the regexp behaviourNo matching tests found in the repo; add unit tests for:
- optional-first only segment
- optional-first + following static segment
- optional optional-in-the-middle segment
src/runtime.ts (2)
62-75
: Params field shape LGTM.Experimental gating is clear.
77-84
: Param parser types look good.Union with TypesConfig ParamParsers + native set is flexible.
src/core/context.ts (2)
252-301
: Resolver + HMR integration: LGTM.API surface and hot replacement flow are coherent with the new resolver.
360-373
: DTS generation hooks in param-parsers correctly.Guard + warnMissingParamParsers + type declarations wiring look correct.
src/core/treeNodeValue.ts (7)
18-26
: RouteRecordOverride.params surface: LGTM.Shape for path/query parsers is clear and extensible.
108-140
: Prefer Object.keys to avoid inherited keys in query params loop.Safer enumeration; keeps behavior stable if the object prototype is altered.
[ suggest_nitpick_refactor ]
- for (var paramName in paramsQuery) { - var config = paramsQuery[paramName] + for (const paramName of Object.keys(paramsQuery)) { + const config = paramsQuery[paramName]!
257-258
: Static/group scores: LGTM.Setting a fixed [300] baseline aligns with sorting expectations.
Also applies to: 272-273
352-362
: Regex escaping utility: LGTM.Covers all needed special chars for path literals.
376-391
: Param subsegment scoring: LGTM.Clear malus model; negative scores for splats are fine given comparator semantics.
393-416
: Param regex builder: LGTM.Escapes statics and handles splat/optional/repeatable correctly.
563-700
: File-segment parser: solid state machine.Handles optional, splat, repeatable, and parser syntax [param=type] cleanly with good errors.
@@ -1,4 +1,4 @@ | |||
import type { RouteRecordRaw } from 'vue-router' | |||
import type { RouteRecordRaw, TypesConfig } from 'vue-router' |
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.
🧩 Analysis chain
TypesConfig import path likely wrong.
TypesConfig is defined in this package’s client types, not exported by vue-router. Import from unplugin-vue-router’s client types to prevent TS errors.
-import type { RouteRecordRaw, TypesConfig } from 'vue-router'
+import type { RouteRecordRaw } from 'vue-router'
+import type { TypesConfig } from '../client' // or 'unplugin-vue-router/client'
🏁 Script executed:
#!/usr/bin/env bash
# Confirm where TypesConfig is declared
rg -n "export interface TypesConfig" -S
rg -n "declare module ['\"]vue-router['\"]" -S client.d.ts
Length of output: 147
Import TypesConfig from this package's client types, not from 'vue-router'.
client.d.ts and src/data-loaders/types-config.ts declare/export TypesConfig; adjust import in src/runtime.ts:
-import type { RouteRecordRaw, TypesConfig } from 'vue-router'
+import type { RouteRecordRaw } from 'vue-router'
+import type { TypesConfig } from '../client' // or 'unplugin-vue-router/client'
📝 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 type { RouteRecordRaw, TypesConfig } from 'vue-router' | |
import type { RouteRecordRaw } from 'vue-router' | |
import type { TypesConfig } from '../client' // or 'unplugin-vue-router/client' |
🤖 Prompt for AI Agents
In src/runtime.ts around line 1, the file currently imports TypesConfig from
'vue-router'; replace that import so TypesConfig is imported from this package's
client types (either the generated client.d.ts export or from
src/data-loaders/types-config.ts). Update the import line to import type {
TypesConfig } from the package's client types file (or from
'./src/data-loaders/types-config' relative path) and remove TypesConfig from the
'vue-router' import so the type comes from the correct local/client declaration.
Summary by CodeRabbit
New Features
Improvements
Tests
Chores