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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open

feat: volar plugins #609

wants to merge 17 commits into from

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

Copy link

pkg-pr-new bot commented Mar 19, 2025

Open in Stackblitz

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

commit: a5e63cb

Copy link

codecov bot commented Mar 19, 2025

Codecov Report

Attention: Patch coverage is 29.55665% with 143 lines in your changes missing coverage. Please review.

Project coverage is 59.85%. Comparing base (cd4a3f8) to head (a5e63cb).
Report is 4 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 96.72% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #609      +/-   ##
==========================================
- Coverage   61.72%   59.85%   -1.87%     
==========================================
  Files          32       36       +4     
  Lines        3135     3333     +198     
  Branches      580      601      +21     
==========================================
+ Hits         1935     1995      +60     
- Misses       1194     1330     +136     
- Partials        6        8       +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 :)


/*
Future ideas:
- Enhance typing of `useRouter().currentRoute` and `$router.currentRoute`
Copy link
Owner

Choose a reason for hiding this comment

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

It's better not to because the router instance can be passed to functions and it's safer to keep that route as any route

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you elaborate on how this is different from passing a typed useRoute() or $route to functions like composables? The function you pass it to can choose to either accept a generalize route/router, like ReturnType<typeof useRouter>/ReturnType<typeof useRoute> or make it more specific by requiring the passed router/route with a generic, like ReturnType<typeof useRoute<'blogs'>>. As far as I can remember, useRouter does not accept a generic (yet), but it could and then it should be fine, I think?

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.

"export interface RouteFileInfoMap {
'src/pages/index.vue': {
routes: '/',
views: never,
Copy link
Owner

Choose a reason for hiding this comment

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

Shouldn't they be default in most cases?

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 route file does not have any child routes, you shouldn't be using <RouterView>, right? Or are there edge cases I'm missing here?

node: TreeNode,
options: GenerateRouteFileInfoMapOptions
): string {
const children = node.children.size > 0 ? node.getSortedChildren() : null
Copy link
Owner

Choose a reason for hiding this comment

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

we should be able to update the branch and use the new deep version of this

replace(
embeddedCode.content,
'{',
'{\n "$schema": "https://raw.githubusercontent.com/posva/unplugin-vue-router/main/route.schema.json",'
Copy link
Owner

Choose a reason for hiding this comment

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

neat!


// 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

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

Successfully merging this pull request may close these issues.

2 participants