Skip to content

feat: volar plugins #609

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

Merged
merged 39 commits into from
Jul 4, 2025
Merged

feat: volar plugins #609

merged 39 commits into from
Jul 4, 2025

Conversation

Anoesj
Copy link
Contributor

@Anoesj Anoesj commented Mar 19, 2025

  • Moved existing Volar plugin for <route> block IntelliSense into src, added it to the building process and added it to exports
    • explain in the docs how users can enable the plugin (by adding it to vueCompilerOptions.plugins)
  • Added Volar plugin for improved useRoute and $route typings
    • explain in the docs how users can enable the plugin (by adding it to vueCompilerOptions.plugins)
    • add tests
    • update playground examples
    • use relative paths

Future

  • Explore other solutions for structuring types, e.g. like how they do it in SvelteKit
  • Explore adding enhanced typing for <RouterView> slots regarding named views

Summary by CodeRabbit

  • New Features
    • Added new Volar plugins for enhanced Vue SFC support: handling <route> blocks and providing typed useRoute() calls.
    • Introduced named views with multiple router views and corresponding example components.
    • Enhanced TypeScript declaration generation with detailed route file metadata and route-to-file mappings.
  • Bug Fixes
    • Simplified and improved route access patterns in example pages by removing explicit route arguments and refining template logic.
  • Chores
    • Updated dependencies including @vue/language-core, muggle-string, and vue-tsc.
    • Updated build configuration and package exports to support new Volar plugins.
    • Removed deprecated Volar plugin file and updated TypeScript config to use new plugins.
  • Tests
    • Added tests for route file info mapping to validate new type generation features.

Copy link

pkg-pr-new bot commented Mar 19, 2025

Open in StackBlitz

npm i https://pkg.pr.new/unplugin-vue-router@609

commit: 8cb3a3d

Copy link

codecov bot commented Mar 19, 2025

Codecov Report

Attention: Patch coverage is 33.02326% with 144 lines in your changes missing coverage. Please review.

Project coverage is 60.95%. Comparing base (98f2c0b) to head (8cb3a3d).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/volar/entries/sfc-typed-router.ts 0.00% 55 Missing and 1 partial ⚠️
src/volar/entries/sfc-route-blocks.ts 0.00% 49 Missing and 1 partial ⚠️
src/volar/utils/augment-vls-ctx.ts 4.76% 20 Missing ⚠️
src/codegen/generateDTS.ts 0.00% 11 Missing ⚠️
src/core/context.ts 0.00% 4 Missing ⚠️
src/codegen/generateRouteFileInfoMap.ts 95.58% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #609      +/-   ##
==========================================
- Coverage   62.75%   60.95%   -1.80%     
==========================================
  Files          32       36       +4     
  Lines        3168     3373     +205     
  Branches      594      617      +23     
==========================================
+ Hits         1988     2056      +68     
- Misses       1175     1310     +135     
- Partials        5        7       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@posva posva moved this from 🆕 New to 📋 Backlog in unplugin-vue-router Mar 19, 2025
@posva posva moved this from 📋 Backlog to 🏗 In progress in unplugin-vue-router Mar 19, 2025

// TODO: Do we want to apply this to EVERY .vue file or only to components that the user wrote themselves?

const relativeFilePath = ctx.compilerOptions.baseUrl
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will ctx.compilerOptions.baseUrl always equal the VueRouter plugin's root? If not, this might not always work.

Copy link
Owner

Choose a reason for hiding this comment

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

Good question

@@ -0,0 +1,7 @@
<template>
<RouterView name="default" />
<RouterView name="a" />
Copy link
Owner

Choose a reason for hiding this comment

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

You are typing these too? Pretty neat! It might be worth to adapt types in vue router too first?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it was just an experiment, but not sure if I actually finished this. I'll check in a few days!


const plugin: VueLanguagePlugin = (ctx) => {
const RE = {
USE_ROUTE: {
Copy link
Owner

Choose a reason for hiding this comment

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

are regexes the recommended way? Isn't there an ast to traverse instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If AST is truly possible, that would be better, but I noticed other Volar plugins usually use regexes too. Maybe @KazariEX could give us his two cents here :)

return
}

// TODO: Do we want to apply this to EVERY .vue file or only to components that the user wrote themselves?
Copy link
Owner

Choose a reason for hiding this comment

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

I think we only want to apply this to page components. The question might be how do we pass those paths from the config

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If a file isn't matched, because it's not in the generated RouteFileInfoMap, the route is generalized, I believe. But it needs extensive testing.

*/
export type GetPossibleRouteNamesByFilePath<T extends string> = T extends keyof RouteFileInfoMap
? RouteFileInfoMap[T]['routes']
: keyof import('vue-router/auto-routes').RouteNamedMap
Copy link
Owner

Choose a reason for hiding this comment

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

Why do we need the dynamic import?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it had something to do with interface declaration merging. Maybe test if it works without the dynamic import when you extend the RouteNamedMap elsewhere in a project.

Copy link
Owner

Choose a reason for hiding this comment

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

Do you remember what test case was failing? I'm trying to see if it's okay to just do RouteNamedMap


// TODO: Do we want to apply this to EVERY .vue file or only to components that the user wrote themselves?

const relativeFilePath = ctx.compilerOptions.baseUrl
Copy link
Owner

Choose a reason for hiding this comment

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

Good question

? relative(ctx.compilerOptions.baseUrl, fileName).replaceAll('\\', '/')
: fileName

const routeNameGetter = `import('vue-router/auto-routes').GetPossibleRouteNamesByFilePath<'${relativeFilePath}'>`
Copy link
Owner

Choose a reason for hiding this comment

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

now we should be able to just pass the name of the route associated with the file

@posva posva force-pushed the feat/volar-plugins branch from a5e63cb to ef08b4b Compare June 4, 2025 12:55
Copy link

coderabbitai bot commented Jun 4, 2025

Walkthrough

This update introduces new Volar language plugins for handling Vue SFC route blocks and typed router support, updates TypeScript declaration generation to include route file info mapping, and adds new runtime dependencies. Several playground Vue components are adjusted for route usage, and new components for named views are added. The Volar plugin implementation is refactored from CommonJS to TypeScript modules.

Changes

Files/Group Change Summary
package.json Added Volar export entries for sfc-route-blocks and sfc-typed-router; added dependencies @vue/language-core, muggle-string; upgraded vue-tsc version.
playground/src/pages/*.vue Removed explicit path args from useRoute calls; added lang="json" to blocks; simplified template expressions; removed "$schema" from some routes; added type assertions in script.
playground/src/pages/named-views/* Added new Vue components: parent.vue with multiple named RouterViews, index.vue, index@a.vue, index@b.vue.
playground/tsconfig.json Replaced single Volar plugin with two plugins: unplugin-vue-router/volar/sfc-route-blocks and sfc-typed-router.
playground/typed-router.d.ts Added RouteNamedMap entries for named views; introduced _RouteFileInfoMap interface and _RouteNamesForFilePath type alias.
src/codegen/generateDTS.ts Added normalizeLines helper; extended generateDTS to accept routeFileInfoMap; included route file info and new type alias in declarations.
src/codegen/generateRouteFileInfoMap.ts New module generating _RouteFileInfoMap interface from route tree data.
src/codegen/generateRouteFileInfoMap.spec.ts Added Vitest tests for generateRouteFileInfoMap function covering various route scenarios.
src/core/context.ts Integrated generateRouteFileInfoMap call into DTS generation to produce routeFileInfoMap data.
src/volar/entries/sfc-route-blocks.ts New Volar plugin extracting and resolving custom blocks; injects JSON schema if missing.
src/volar/entries/sfc-typed-router.ts New Volar plugin enhancing useRoute() and $route typings in SFC scripts and templates via code transformations and context augmentation.
src/volar/utils/augment-vls-ctx.ts Added augmentVlsCtx utility to inject code into virtual language service context at __VLS_ctx declaration.
tsdown-runtime.config.ts Added build config for Volar submodule with entries for sfc-route-blocks and sfc-typed-router; set output to CommonJS; added externals.
volar/index.cjs Removed legacy Volar plugin implementation handling route custom blocks in CommonJS.
playground/src/pages/articles.vue Added typed route param checks and assertions in script setup block for route params and names.
src/codegen/generateRouteMap.ts Replaced node.getSortedChildren() calls with node.getChildrenSorted().
src/codegen/generateRouteRecords.ts Replaced node.getSortedChildren() calls with node.getChildrenSorted().
src/core/tree.ts Renamed TreeNode.getSortedChildren() method to getChildrenSorted().

Possibly related PRs


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e61a41 and 8cb3a3d.

📒 Files selected for processing (1)
  • src/volar/utils/augment-vls-ctx.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/volar/utils/augment-vls-ctx.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: test (lts/*, windows-latest)
  • GitHub Check: test (18.x, windows-latest)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Post Copyable Unit Tests in a Comment

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/volar/utils/augment-vls-ctx.ts (1)

26-26: Consider documenting array mutation behavior.

The function mutates the input array directly, which may be unexpected. Consider adding JSDoc to clarify this behavior.

+/**
+ * Augments VLS context by injecting code before the first semicolon after __VLS_ctx declaration.
+ * @param content - Array of Code elements (will be mutated)
+ * @param getCodes - Function that returns code to inject
+ */
 export function augmentVlsCtx(content: Code[], getCodes: () => ` & ${string}`) {
playground/src/pages/named-views/parent.vue (1)

5-6: Address the TODO comment for the "c" named view.

The @vue-expect-error with TODO indicates incomplete implementation. Consider either implementing the corresponding component or removing this RouterView if it's not needed.

tsdown-runtime.config.ts (1)

24-24: Address placement uncertainty.

The TODO comment suggests uncertainty about config placement. The current location is reasonable for now, but consider a separate config file if Volar builds become more complex.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2fb721 and ca8be5e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • package.json (2 hunks)
  • playground/src/pages/@[profileId].vue (1 hunks)
  • playground/src/pages/[...path]+.vue (2 hunks)
  • playground/src/pages/[...path].vue (1 hunks)
  • playground/src/pages/[name].vue (3 hunks)
  • playground/src/pages/custom-name-and-path.vue (1 hunks)
  • playground/src/pages/deep/nesting/works/custom-name-and-path.vue (0 hunks)
  • playground/src/pages/named-views/parent.vue (1 hunks)
  • playground/src/pages/named-views/parent/index.vue (1 hunks)
  • playground/src/pages/named-views/parent/index@a.vue (1 hunks)
  • playground/src/pages/named-views/parent/index@b.vue (1 hunks)
  • playground/src/pages/partial-[name].vue (1 hunks)
  • playground/src/pages/test-[a-id].vue (1 hunks)
  • playground/src/pages/users/[id].vue (1 hunks)
  • playground/src/pages/users/colada-loader.[id].vue (1 hunks)
  • playground/tsconfig.json (1 hunks)
  • playground/typed-router.d.ts (2 hunks)
  • src/codegen/generateDTS.ts (2 hunks)
  • src/codegen/generateRouteFileInfoMap.spec.ts (1 hunks)
  • src/codegen/generateRouteFileInfoMap.ts (1 hunks)
  • src/core/context.ts (2 hunks)
  • src/volar/entries/sfc-route-blocks.ts (1 hunks)
  • src/volar/entries/sfc-typed-router.ts (1 hunks)
  • src/volar/utils/augment-vls-ctx.ts (1 hunks)
  • tsdown-runtime.config.ts (1 hunks)
  • volar/index.cjs (0 hunks)
💤 Files with no reviewable changes (2)
  • playground/src/pages/deep/nesting/works/custom-name-and-path.vue
  • volar/index.cjs
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/codegen/generateRouteFileInfoMap.spec.ts (3)
src/options.ts (1)
  • resolveOptions (295-352)
src/core/tree.ts (1)
  • PrefixTree (277-311)
src/codegen/generateRouteFileInfoMap.ts (1)
  • generateRouteFileInfoMap (8-18)
src/core/context.ts (1)
src/codegen/generateRouteFileInfoMap.ts (1)
  • generateRouteFileInfoMap (8-18)
tsdown-runtime.config.ts (1)
tsdown.config.ts (1)
  • commonOptions (3-13)
src/codegen/generateRouteFileInfoMap.ts (2)
src/core/tree.ts (1)
  • PrefixTree (277-311)
src/core/extendRoutes.ts (1)
  • children (190-194)
src/volar/entries/sfc-typed-router.ts (1)
src/volar/utils/augment-vls-ctx.ts (1)
  • augmentVlsCtx (3-27)
🔇 Additional comments (43)
src/volar/utils/augment-vls-ctx.ts (1)

3-27: 🛠️ Refactor suggestion

Handle edge case where start pattern is found but end pattern isn't.

The function returns early if to === -1 but doesn't reset from when the end pattern isn't found after finding the start pattern.

  if (to === -1) {
+   // Reset from if end pattern not found
+   from = -1
    return
  }

Likely an incorrect or invalid review comment.

playground/src/pages/test-[a-id].vue (1)

3-3: Good simplification for improved type inference.

Removing the explicit route path allows the new Volar plugins to provide better type safety through automatic inference.

playground/src/pages/named-views/parent/index@a.vue (1)

1-3: Simple and appropriate for named views demo.

Clean implementation for demonstrating named view functionality.

playground/src/pages/users/[id].vue (1)

44-44: Consistent with type inference improvements.

Removing explicit route path aligns with the new Volar plugin approach for automatic type inference.

playground/src/pages/@[profileId].vue (1)

3-3: Change aligns with new Volar plugin infrastructure.

Removing the explicit route path argument is consistent with the enhanced type inference provided by the new Volar plugins.

playground/src/pages/users/colada-loader.[id].vue (1)

12-12: Consistent with new typing approach.

Removing the explicit route path follows the pattern for leveraging enhanced type inference.

playground/src/pages/named-views/parent/index.vue (1)

1-3: Simple and appropriate for named views demo.

Clean implementation that effectively demonstrates the default named view.

playground/tsconfig.json (1)

38-39: Properly configures new Volar plugins.

The plugin configuration correctly replaces the legacy plugin with the new modular approach.

playground/src/pages/named-views/parent/index@b.vue (1)

1-3: LGTM!

Simple, valid Vue component for named view "b".

playground/src/pages/partial-[name].vue (1)

2-2: API improvement aligns with pattern.

Simplified useRoute() call is consistent with broader changes across route components.

playground/src/pages/[...path].vue (2)

4-4: Template simplified correctly.

Removing conditional check and directly displaying route params is cleaner.


8-8: Good addition for Volar plugin support.

Adding lang="json" enables better tooling support for route blocks.

playground/src/pages/[...path]+.vue (2)

4-4: Consistent API simplification.

useRoute() change follows the established pattern across route components.


14-14: Enables better tooling support.

Adding lang="json" to route block aligns with Volar plugin enhancements.

playground/src/pages/custom-name-and-path.vue (1)

13-15: Good demonstration of enhanced type safety.

The type assertion correctly validates the route name against the expected literal type, showcasing the improved typing capabilities.

playground/src/pages/[name].vue (3)

52-52: Good addition of RouteNamedMap import for enhanced typing.


94-95: LGTM on standardized useRoute calls.

The removal of explicit route path arguments and use of generic type parameters aligns with the broader refactoring for improved type inference.


137-137: Template expression correctly simplified.

Removing the conditional check is appropriate since the route parameter is guaranteed to be available.

src/core/context.ts (2)

6-6: Import added correctly for route file info map generation.


245-247: Proper integration of route file info map generation.

The function call correctly passes the routeTree and root options to generate enhanced type information for route files.

src/codegen/generateRouteFileInfoMap.spec.ts (1)

1-87: LGTM! Comprehensive test coverage.

The test suite properly covers the main scenarios for route file info map generation. The snapshots correctly reflect that parent routes with children should have views: 'default' while leaf routes have views: never.

package.json (2)

80-85: Missing ESM exports for consistency.

The new Volar plugin exports only provide CommonJS (require) paths but lack ESM (import) paths, unlike other exports in this file. Is this intentional due to Volar's runtime requirements?


142-152: LGTM! Dependencies support new Volar functionality.

The added dependencies (@babel/types, @vue/language-core, muggle-string) are appropriate for the new Volar plugin implementation.

tsdown-runtime.config.ts (1)

25-39: LGTM! Build configuration is appropriate.

The Volar build config correctly uses CJS format to match the package.json exports and properly lists external dependencies.

src/codegen/generateDTS.ts (3)

3-12: LGTM! Good refactoring to eliminate duplication.

The normalizeLines helper function properly extracts the common formatting logic for reuse.


17-22: LGTM! Clean parameter addition.

The routeFileInfoMap parameter is properly integrated into the function signature.


44-56: LGTM! Proper integration of route file info mapping.

The route file info map is correctly included in the generated module, and the GetPossibleRouteNamesByFilePath type provides a useful conditional lookup with appropriate fallback.

src/volar/entries/sfc-route-blocks.ts (4)

4-18: Plugin structure and initialization look good.

The plugin correctly implements the VueLanguagePlugin interface and properly handles route block enumeration.


29-46: Robust embedded code resolution with proper bounds checking.

Good defensive programming with the block existence check and proper content mapping to embedded code.


48-60: JSON schema injection implementation.

The schema injection logic correctly identifies JSON objects and adds the schema reference. The URL points to the correct repository location.


21-25:

❓ Verification inconclusive

Verify language mapping for route blocks.

The fallback from 'txt' to 'json' addresses the TODO limitation but may need verification that this mapping is consistent with Vue language tools behavior.


🏁 Script executed:

#!/bin/bash
# Description: Check how Vue language tools handle custom block language detection
# Expected: Find documentation or examples of custom block language handling

rg -A 5 -B 5 "custom.*block.*lang" --type ts --type js

Length of output: 56


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' src/volar/entries/sfc-route-blocks.ts

Length of output: 2132


Confirm fallback from ‘txt’ to ‘json’ aligns with Vue’s behavior.

I couldn’t find any explicit mapping for custom‐block languages in the Vue language tools. Please verify that a <route> block without lang="json" is indeed treated as JSON by the Vue tooling. You can test this by authoring a <route> block without any lang attribute and checking how it’s parsed in your editor or language server.

src/volar/entries/sfc-typed-router.ts (4)

16-32: Regex patterns for code transformation.

The regex patterns correctly target useRoute() calls and $route usage. Using named capture groups and lookbehind/lookahead assertions provides precise targeting.


43-49: File path resolution and type helper usage.

The relative path calculation and type helper construction look correct. The import statement properly references the generated types.


51-68: TypeScript and JavaScript code transformations.

Both transformation approaches (generic injection for TS, type casting for JS) are implemented correctly and handle the different language contexts appropriately.


74-91: VLS context augmentation for template typing.

The $route typing augmentation correctly uses the utility function and provides proper type overrides for template usage.

playground/typed-router.d.ts (3)

58-59: New named views routes properly defined.

The route entries for named views are correctly structured with proper parameter and query types.


83-320: Comprehensive file-to-route mapping interface.

The RouteFileInfoMap provides thorough mapping of component files to their route names and views. The structure correctly handles both simple routes and complex nested/named view scenarios.


325-327: Type helper with proper fallback handling.

The conditional type correctly resolves route names by file path and falls back to all route names when the file isn't found in the map.

src/codegen/generateRouteFileInfoMap.ts (5)

8-18: Function signature and root validation.

Good defensive programming with proper root node validation and clear error messaging.


24-41: Route names and named views collection logic.

The logic correctly collects route names recursively and extracts unique named views from component keys. The Set usage prevents duplicates.


46-58: Component mapping and recursive traversal.

The code properly maps components to file info entries and recursively processes child nodes. The flat array join ensures proper nesting structure.


67-80: File info entry generation with proper path handling.

The relative path calculation and template generation correctly format the interface entries. Cross-platform path handling with replaceAll('\\', '/') is appropriate.


85-90: Recursive route name collection.

The recursive function correctly flattens the tree structure to collect all route names from a node and its descendants.

@posva posva force-pushed the feat/volar-plugins branch from ca8be5e to 993b30a Compare June 5, 2025 07:19
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
playground/typed-router.d.ts (1)

325-327: Address previous feedback on dynamic import usage.

The implementation still uses dynamic import despite previous suggestions to avoid it and make the type internal.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca8be5e and 993b30a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • package.json (2 hunks)
  • playground/src/pages/@[profileId].vue (1 hunks)
  • playground/src/pages/[...path]+.vue (2 hunks)
  • playground/src/pages/[...path].vue (1 hunks)
  • playground/src/pages/[name].vue (3 hunks)
  • playground/src/pages/custom-name-and-path.vue (1 hunks)
  • playground/src/pages/deep/nesting/works/custom-name-and-path.vue (0 hunks)
  • playground/src/pages/named-views/parent.vue (1 hunks)
  • playground/src/pages/named-views/parent/index.vue (1 hunks)
  • playground/src/pages/named-views/parent/index@a.vue (1 hunks)
  • playground/src/pages/named-views/parent/index@b.vue (1 hunks)
  • playground/src/pages/partial-[name].vue (1 hunks)
  • playground/src/pages/test-[a-id].vue (1 hunks)
  • playground/src/pages/users/[id].vue (1 hunks)
  • playground/src/pages/users/colada-loader.[id].vue (1 hunks)
  • playground/tsconfig.json (1 hunks)
  • playground/typed-router.d.ts (2 hunks)
  • src/codegen/generateDTS.ts (2 hunks)
  • src/codegen/generateRouteFileInfoMap.spec.ts (1 hunks)
  • src/codegen/generateRouteFileInfoMap.ts (1 hunks)
  • src/core/context.ts (2 hunks)
  • src/volar/entries/sfc-route-blocks.ts (1 hunks)
  • src/volar/entries/sfc-typed-router.ts (1 hunks)
  • src/volar/utils/augment-vls-ctx.ts (1 hunks)
  • tsdown-runtime.config.ts (1 hunks)
  • volar/index.cjs (0 hunks)
💤 Files with no reviewable changes (2)
  • playground/src/pages/deep/nesting/works/custom-name-and-path.vue
  • volar/index.cjs
✅ Files skipped from review due to trivial changes (3)
  • playground/src/pages/named-views/parent/index@a.vue
  • src/codegen/generateRouteFileInfoMap.spec.ts
  • src/volar/entries/sfc-route-blocks.ts
🚧 Files skipped from review as they are similar to previous changes (20)
  • playground/src/pages/users/[id].vue
  • playground/src/pages/test-[a-id].vue
  • playground/src/pages/users/colada-loader.[id].vue
  • playground/src/pages/named-views/parent/index@b.vue
  • playground/src/pages/partial-[name].vue
  • playground/src/pages/named-views/parent/index.vue
  • playground/src/pages/[...path].vue
  • playground/src/pages/@[profileId].vue
  • playground/src/pages/[name].vue
  • playground/src/pages/[...path]+.vue
  • playground/tsconfig.json
  • playground/src/pages/custom-name-and-path.vue
  • playground/src/pages/named-views/parent.vue
  • src/core/context.ts
  • tsdown-runtime.config.ts
  • package.json
  • src/volar/utils/augment-vls-ctx.ts
  • src/codegen/generateRouteFileInfoMap.ts
  • src/codegen/generateDTS.ts
  • src/volar/entries/sfc-typed-router.ts
🔇 Additional comments (1)
playground/typed-router.d.ts (1)

58-59: LGTM: Named view routes added correctly.

The new route entries follow the existing pattern and are properly typed.

@posva posva force-pushed the feat/volar-plugins branch from 993b30a to b042ca2 Compare June 5, 2025 07:41
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/codegen/generateRouteFileInfoMap.ts (2)

73-73: Use proper path normalization.

replaceAll('\\', '/') may not handle all edge cases. Use path.posix.normalize() or similar.

-    relative(options.root, file).replaceAll('\\', '/')
+    relative(options.root, file).split(path.sep).join('/')

75-75: Address TODO comment for views implementation.

The TODO suggests the views implementation needs verification.

Do you want me to help investigate the views implementation or create an issue to track this?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 993b30a and b042ca2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • package.json (2 hunks)
  • playground/src/pages/@[profileId].vue (1 hunks)
  • playground/src/pages/[...path]+.vue (2 hunks)
  • playground/src/pages/[...path].vue (1 hunks)
  • playground/src/pages/[name].vue (3 hunks)
  • playground/src/pages/custom-name-and-path.vue (1 hunks)
  • playground/src/pages/deep/nesting/works/custom-name-and-path.vue (0 hunks)
  • playground/src/pages/named-views/parent.vue (1 hunks)
  • playground/src/pages/named-views/parent/index.vue (1 hunks)
  • playground/src/pages/named-views/parent/index@a.vue (1 hunks)
  • playground/src/pages/named-views/parent/index@b.vue (1 hunks)
  • playground/src/pages/partial-[name].vue (1 hunks)
  • playground/src/pages/test-[a-id].vue (1 hunks)
  • playground/src/pages/users/[id].vue (1 hunks)
  • playground/src/pages/users/colada-loader.[id].vue (1 hunks)
  • playground/tsconfig.json (1 hunks)
  • playground/typed-router.d.ts (2 hunks)
  • src/codegen/generateDTS.ts (2 hunks)
  • src/codegen/generateRouteFileInfoMap.spec.ts (1 hunks)
  • src/codegen/generateRouteFileInfoMap.ts (1 hunks)
  • src/core/context.ts (2 hunks)
  • src/volar/entries/sfc-route-blocks.ts (1 hunks)
  • src/volar/entries/sfc-typed-router.ts (1 hunks)
  • src/volar/utils/augment-vls-ctx.ts (1 hunks)
  • tsdown-runtime.config.ts (1 hunks)
  • volar/index.cjs (0 hunks)
💤 Files with no reviewable changes (2)
  • playground/src/pages/deep/nesting/works/custom-name-and-path.vue
  • volar/index.cjs
✅ Files skipped from review due to trivial changes (1)
  • src/codegen/generateRouteFileInfoMap.spec.ts
🚧 Files skipped from review as they are similar to previous changes (22)
  • playground/src/pages/@[profileId].vue
  • playground/src/pages/users/[id].vue
  • playground/src/pages/users/colada-loader.[id].vue
  • playground/src/pages/test-[a-id].vue
  • playground/src/pages/[...path]+.vue
  • playground/src/pages/named-views/parent/index@b.vue
  • playground/src/pages/named-views/parent/index@a.vue
  • playground/src/pages/named-views/parent/index.vue
  • playground/src/pages/[...path].vue
  • playground/src/pages/partial-[name].vue
  • playground/tsconfig.json
  • src/core/context.ts
  • playground/src/pages/[name].vue
  • src/volar/entries/sfc-route-blocks.ts
  • tsdown-runtime.config.ts
  • playground/src/pages/custom-name-and-path.vue
  • src/volar/utils/augment-vls-ctx.ts
  • src/codegen/generateDTS.ts
  • src/volar/entries/sfc-typed-router.ts
  • package.json
  • playground/src/pages/named-views/parent.vue
  • playground/typed-router.d.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/codegen/generateRouteFileInfoMap.ts (2)
src/core/tree.ts (1)
  • PrefixTree (277-311)
src/core/extendRoutes.ts (1)
  • children (190-194)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/codegen/generateRouteFileInfoMap.ts (1)

94-94: Handle empty route names union.

If routesNames is empty, this generates an invalid TypeScript union type.

- routes: ${routesNames.join(' | ')}
+ routes: ${routesNames.length > 0 ? routesNames.join(' | ') : 'never'}
🧹 Nitpick comments (1)
src/codegen/generateRouteFileInfoMap.ts (1)

16-16: Address FIXME/TODO comments before merge.

Multiple areas marked for improvement should be resolved.

Would you like me to help implement the fixes mentioned in these comments?

Also applies to: 31-31, 51-51, 86-86, 90-91

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b042ca2 and ea53ed1.

📒 Files selected for processing (3)
  • playground/typed-router.d.ts (2 hunks)
  • src/codegen/generateRouteFileInfoMap.spec.ts (1 hunks)
  • src/codegen/generateRouteFileInfoMap.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/codegen/generateRouteFileInfoMap.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • playground/typed-router.d.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/codegen/generateRouteFileInfoMap.ts (2)
src/core/tree.ts (1)
  • PrefixTree (277-311)
src/core/extendRoutes.ts (1)
  • children (190-194)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (2)
src/codegen/generateRouteFileInfoMap.ts (2)

4-25: LGTM - Main function structure is solid.

The validation and template generation logic is correct.


59-69: 🛠️ Refactor suggestion

Logic inconsistency with empty route names.

When routeNames.length === 0, the function returns an empty array but still processes components. This could generate invalid entries.

Consider this fix:

 const currentRouteInfo =
   routeNames.length === 0
-    ? []
+    ? []
     : Array.from(node.value.components.values()).map((file) =>
         generateRouteFileInfoEntry(
           file,
           routeNames,
           childrenNamedViewsUnion,
           rootDir
         )
       )

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/codegen/generateRouteFileInfoMap.ts (1)

45-45: Handle empty routes array to prevent invalid TypeScript.

If the routes array is empty, this generates an empty string instead of a valid TypeScript union type.

-    routes: ${routes.map((name) => `'${name}'`).join(' | ')}
+    routes: ${routes.length > 0 ? routes.map((name) => `'${name}'`).join(' | ') : 'never'}
🧹 Nitpick comments (2)
src/codegen/generateRouteFileInfoMap.ts (2)

88-88: Use path normalization library instead of string replacement.

Manual path separator replacement may not handle all edge cases across platforms.

-          key: relative(rootDir, file).replaceAll('\\', '/'),
+          key: relative(rootDir, file).replace(/\\/g, '/'),

68-68: Simplify children collection logic.

The conditional can be simplified since getSortedChildrenDeep() should handle empty children gracefully.

-  const children = node.children.size > 0 ? node.getSortedChildrenDeep() : null
+  const children = node.getSortedChildrenDeep()
+  const hasChildren = children.length > 0
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea53ed1 and f85577e.

📒 Files selected for processing (3)
  • playground/typed-router.d.ts (2 hunks)
  • src/codegen/generateRouteFileInfoMap.spec.ts (1 hunks)
  • src/codegen/generateRouteFileInfoMap.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/codegen/generateRouteFileInfoMap.spec.ts
  • playground/typed-router.d.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Socket Security: Pull Request Alerts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd104fc and e6388d9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • package.json (2 hunks)
  • playground/src/pages/articles.vue (1 hunks)
  • playground/typed-router.d.ts (2 hunks)
  • src/codegen/generateDTS.ts (2 hunks)
  • src/codegen/generateRouteFileInfoMap.spec.ts (1 hunks)
  • src/codegen/generateRouteFileInfoMap.ts (1 hunks)
  • src/volar/entries/sfc-typed-router.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/codegen/generateRouteFileInfoMap.spec.ts
  • playground/src/pages/articles.vue
  • package.json
  • src/codegen/generateRouteFileInfoMap.ts
  • playground/typed-router.d.ts
  • src/volar/entries/sfc-typed-router.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (1)
src/codegen/generateDTS.ts (1)

21-21:

✅ Verification successful

Verify backward compatibility of function signature change.

Adding the required routeFileInfoMap parameter is a breaking change. Ensure all callers have been updated accordingly.

Also applies to: 26-26


🏁 Script executed:

#!/bin/bash
# Description: Verify all callers of generateDTS have been updated with the new parameter
rg -A 10 "generateDTS\(" --type ts

Length of output: 1751


generateDTS signature update is safe
The only invocation of the codegen function lives in src/core/context.ts and already supplies routeFileInfoMap via the _generateDTS wrapper. No other callers were found.

return
}

content.splice(to, 0, ...getCodes())
Copy link
Owner

Choose a reason for hiding this comment

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

This seems to work with just getCodes() (no spread). Is there a reason to spread the code character per character?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I copied this bit from something @KazariEX created. But Array.prototype.splice is designed like this AFAIK: splice(start, deleteCount, item1, item2, /* …, */ itemN)

Copy link
Contributor

@KazariEX KazariEX Jun 10, 2025

Choose a reason for hiding this comment

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

getCodes should be () => Code[] instead of () => string.

Copy link
Owner

Choose a reason for hiding this comment

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

This part is still not clear to me. I struggle a lot to find comprehensible docs:

I think I will merge this without the ... (it works) and add a TODO for later.

@posva posva mentioned this pull request Jun 23, 2025
@posva posva force-pushed the feat/volar-plugins branch from e6388d9 to 41bbb3c Compare July 4, 2025 13:09
Copy link

socket-security bot commented Jul 4, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​vue/​language-core@​3.0.1991007196100
Updated@​babel/​types@​7.27.7 ⏵ 7.28.098 +110080 +198100
Updated@​types/​node@​24.0.7 ⏵ 22.16.01001008096100
Updatedfirebase@​11.9.1 ⏵ 11.10.099 +1100100 +19980

View full report

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b5c32d and 4e61a41.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • package.json (3 hunks)
  • playground/src/components/TestSetup.vue (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: test (lts/*, windows-latest)
  • GitHub Check: test (18.x, windows-latest)
🔇 Additional comments (1)
playground/src/components/TestSetup.vue (1)

4-6: Good type safety implementation.

The useRoute() call and type assertion using satisfies correctly ensure type safety for the route name.

@@ -1,5 +1,9 @@
<script setup lang="ts">
import { onBeforeRouteLeave } from 'vue-router'
import { onBeforeRouteLeave, type RouteMap } from 'vue-router'
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing import for useRoute.

The RouteMap type import is correct, but useRoute is used on line 4 without being imported.

-import { onBeforeRouteLeave, type RouteMap } from 'vue-router'
+import { onBeforeRouteLeave, useRoute, type RouteMap } from 'vue-router'
📝 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.

Suggested change
import { onBeforeRouteLeave, type RouteMap } from 'vue-router'
-import { onBeforeRouteLeave, type RouteMap } from 'vue-router'
+import { onBeforeRouteLeave, useRoute, type RouteMap } from 'vue-router'
🤖 Prompt for AI Agents
In playground/src/components/TestSetup.vue at line 2, the import statement
includes onBeforeRouteLeave and RouteMap from 'vue-router' but is missing the
import for useRoute, which is used on line 4. Add useRoute to the import
statement alongside onBeforeRouteLeave and RouteMap to properly import it from
'vue-router'.

Copy link
Owner

@posva posva left a comment

Choose a reason for hiding this comment

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

Thanks a lot for this! 🙏

Next step will be adding docs and we can ship it

@github-project-automation github-project-automation bot moved this from 🏗 In progress to 👀 In review in unplugin-vue-router Jul 4, 2025
@posva posva merged commit a8a0dcc into posva:main Jul 4, 2025
14 checks passed
@github-project-automation github-project-automation bot moved this from 👀 In review to ✅ Done in unplugin-vue-router Jul 4, 2025
@darkbasic
Copy link

@Anoesj unfortunately one of my routes don't want to automatically infer the route type:

[esg]: src/pages/suppliers/[id].vue(6,59): error TS18046: '__VLS_ctx.route' is of type 'unknown'.
[esg]: src/pages/suppliers/[id].vue(8,76): error TS18046: '__VLS_ctx.route' is of type 'unknown'.
[esg]: src/pages/suppliers/[id].vue(18,16): error TS18046: 'route' is of type 'unknown'.

relevant typed-router.d.ts:

    'src/pages/suppliers/index.vue': {
      routes: '/suppliers/'
      views: never
    }
    'src/pages/suppliers/[id].vue': {
      routes: '/suppliers/[id]' | '/suppliers/[id]/results/[[version]]' | '/suppliers/[id]/general' | '/suppliers/[id]/results'
      views: 'default'
    }
    'src/pages/suppliers/[id]/general.vue': {
      routes: '/suppliers/[id]/general'
      views: never
    }
    'src/pages/suppliers/[id]/results/[[version]].vue': {
      routes: '/suppliers/[id]/results/[[version]]'
      views: never
    }
    'src/pages/suppliers/create.vue': {
      routes: '/suppliers/create'
      views: never
    }

Also I think that definePage's redirect to param should be automatically typed as well:

definePage({
  redirect: (to) => ({
    name: '/members/[id]/roles',
    // params: { id: route.params.id }
    // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we know where we are
    params: { id: (to.params as RouteNamedMap['/members/[id]']['params']).id }
  })
})

That's because you can't refer to useRoute inside definePage or you will get the following error:

  [unplugin-vue-router] `definePage()` in <script setup> cannot reference locally declared variables (route) because it will be hoisted outside of the setup() function.

@Anoesj
Copy link
Contributor Author

Anoesj commented Aug 5, 2025

@darkbasic Thanks for the feedback! Would it be possible to create a repro of the first issue (public repo, because testing this in Stackblitz doesn't fully work as far as I'm aware of)? The second issue seems like a separate request. Could you open a new issue for that?

@darkbasic
Copy link

@Anoesj here it is: https://github.com/darkbasic/unplugin-vue-router-609
And #684

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants