From 77b584b6fb28128a3e62fb32a1135a4848e41c2f Mon Sep 17 00:00:00 2001 From: liujiayii Date: Thu, 14 Nov 2024 09:38:57 +0800 Subject: [PATCH 1/2] feat: fix types --- .devcontainer/Dockerfile | 16 - .devcontainer/devcontainer.json | 31 - LICENSE | 2 +- README.en.md | 632 +++++++++++++++++ README.md | 644 +----------------- .../react.d.ts => client-react.d.ts | 0 .../solid.d.ts => client-solid.d.ts | 0 virtual-package/vue.d.ts => client-vue.d.ts | 0 examples/react-rsbuild/env.d.ts | 1 + examples/react/env.d.ts | 1 + examples/remix-style/vite.config.ts | 4 +- examples/vue-rsbuild/.gitignore | 13 + examples/vue-rsbuild/package.json | 20 + examples/vue-rsbuild/rsbuild.config.ts | 36 + examples/vue-rsbuild/src/App.vue | 3 + .../vue-rsbuild/src/admin/pages/index.vue | 22 + examples/vue-rsbuild/src/env.d.ts | 10 + .../src/features/admin/pages/admin.vue | 12 + .../features/dashboard/pages/dashboard.vue | 12 + .../src/features/dashboard/pages/welcome.vue | 12 + examples/vue-rsbuild/src/index.css | 0 examples/vue-rsbuild/src/index.ts | 25 + examples/vue-rsbuild/src/pages/[...all].vue | 3 + examples/vue-rsbuild/src/pages/[sensor].vue | 4 + .../src/pages/[sensor]/current.vue | 3 + .../vue-rsbuild/src/pages/__test__/index.vue | 3 + examples/vue-rsbuild/src/pages/about.vue | 11 + examples/vue-rsbuild/src/pages/about/[id].vue | 26 + .../vue-rsbuild/src/pages/about/[id]/more.vue | 5 + .../src/pages/about/[id]/nested.vue | 5 + .../vue-rsbuild/src/pages/about/index.vue | 8 + examples/vue-rsbuild/src/pages/blog/[id].vue | 23 + examples/vue-rsbuild/src/pages/blog/index.vue | 14 + .../src/pages/blog/today/[...all].vue | 3 + .../src/pages/blog/today/index.vue | 5 + examples/vue-rsbuild/src/pages/components.vue | 8 + examples/vue-rsbuild/src/pages/index.vue | 38 ++ examples/vue-rsbuild/src/pages/jsx.jsx | 18 + examples/vue-rsbuild/src/pages/markdown.md | 1 + examples/vue-rsbuild/tsconfig.json | 18 + examples/vue/env.d.ts | 1 + examples/vue/src/main.ts | 2 +- examples/vue/vite.config.ts | 11 +- package.json | 12 +- pnpm-lock.yaml | 59 ++ src/core/context.ts | 2 +- src/core/index.ts | 87 --- src/core/resolvers/react.ts | 2 +- src/core/resolvers/solid.ts | 2 +- src/core/resolvers/vue.ts | 2 +- src/core/types.ts | 30 +- src/index.ts | 4 +- 52 files changed, 1111 insertions(+), 795 deletions(-) delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/devcontainer.json create mode 100644 README.en.md rename virtual-package/react.d.ts => client-react.d.ts (100%) rename virtual-package/solid.d.ts => client-solid.d.ts (100%) rename virtual-package/vue.d.ts => client-vue.d.ts (100%) create mode 100644 examples/react-rsbuild/env.d.ts create mode 100644 examples/react/env.d.ts create mode 100644 examples/vue-rsbuild/.gitignore create mode 100644 examples/vue-rsbuild/package.json create mode 100644 examples/vue-rsbuild/rsbuild.config.ts create mode 100644 examples/vue-rsbuild/src/App.vue create mode 100644 examples/vue-rsbuild/src/admin/pages/index.vue create mode 100644 examples/vue-rsbuild/src/env.d.ts create mode 100644 examples/vue-rsbuild/src/features/admin/pages/admin.vue create mode 100644 examples/vue-rsbuild/src/features/dashboard/pages/dashboard.vue create mode 100644 examples/vue-rsbuild/src/features/dashboard/pages/welcome.vue create mode 100644 examples/vue-rsbuild/src/index.css create mode 100644 examples/vue-rsbuild/src/index.ts create mode 100644 examples/vue-rsbuild/src/pages/[...all].vue create mode 100644 examples/vue-rsbuild/src/pages/[sensor].vue create mode 100644 examples/vue-rsbuild/src/pages/[sensor]/current.vue create mode 100644 examples/vue-rsbuild/src/pages/__test__/index.vue create mode 100644 examples/vue-rsbuild/src/pages/about.vue create mode 100644 examples/vue-rsbuild/src/pages/about/[id].vue create mode 100644 examples/vue-rsbuild/src/pages/about/[id]/more.vue create mode 100644 examples/vue-rsbuild/src/pages/about/[id]/nested.vue create mode 100644 examples/vue-rsbuild/src/pages/about/index.vue create mode 100644 examples/vue-rsbuild/src/pages/blog/[id].vue create mode 100644 examples/vue-rsbuild/src/pages/blog/index.vue create mode 100644 examples/vue-rsbuild/src/pages/blog/today/[...all].vue create mode 100644 examples/vue-rsbuild/src/pages/blog/today/index.vue create mode 100644 examples/vue-rsbuild/src/pages/components.vue create mode 100644 examples/vue-rsbuild/src/pages/index.vue create mode 100644 examples/vue-rsbuild/src/pages/jsx.jsx create mode 100644 examples/vue-rsbuild/src/pages/markdown.md create mode 100644 examples/vue-rsbuild/tsconfig.json create mode 100644 examples/vue/env.d.ts delete mode 100644 src/core/index.ts diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 44dfd57..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.208.0/containers/javascript-node/.devcontainer/base.Dockerfile - -# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 16, 14, 12, 16-bullseye, 14-bullseye, 12-bullseye, 16-buster, 14-buster, 12-buster -ARG VARIANT="20-bullseye" -FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT} - -# [Optional] Uncomment this section to install additional OS packages. -# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && apt-get -y install --no-install-recommends - -# [Optional] Uncomment if you want to install an additional version of node using nvm -# ARG EXTRA_NODE_VERSION=10 -# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" - -# [Optional] Uncomment if you want to install more global node modules -RUN su node -c "npm install -g pnpm @antfu/ni" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index fecc0ed..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,31 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: -// https://github.com/microsoft/vscode-dev-containers/tree/v0.208.0/containers/javascript-node -{ - "name": "Node.js", - "build": { - "dockerfile": "Dockerfile", - // Update 'VARIANT' to pick a Node version: 16, 14, 12. - // Append -bullseye or -buster to pin to an OS version. - // Use -bullseye variants on local arm64/Apple Silicon. - "args": { "VARIANT": "20-bullseye" } - }, - - "customizations": { - "vscode": { - "settings": {}, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "dbaeumer.vscode-eslint" - ] - } - }, - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "pnpm install", - - // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. - "remoteUser": "node" -} diff --git a/LICENSE b/LICENSE index 0dedc01..e2b352b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 hannoeru +Copyright (c) 2021 liujiayii Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..a96aaee --- /dev/null +++ b/README.en.md @@ -0,0 +1,632 @@ +# unplugin-convention-routes + +[![npm version](https://badgen.net/npm/v/unplugin-convention-routes)](https://www.npmjs.com/package/unplugin-convention-routes) +[![monthly downloads](https://badgen.net/npm/dm/unplugin-convention-routes)](https://www.npmjs.com/package/unplugin-convention-routes) +[![types](https://badgen.net/npm/types/unplugin-convention-routes)](https://github.com/liujiayii/unplugin-convention-routes/blob/main/src/types.ts) +[![license](https://badgen.net/npm/license/unplugin-convention-routes)](https://github.com/liujiayii/unplugin-convention-routes/blob/main/LICENSE) + +> File system based routing for Vue 3 / React / Solid applications using +> [Vite](https://github.com/vitejs/vite) + +## Getting Started + +### Vue + +**๐ŸšจImportant Notes๐Ÿšจ** + +We recommend that Vue users use [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) instead of this plugin. + +[unplugin-vue-router](https://github.com/posva/unplugin-vue-router) is a unplugin library created by [@posva](https://github.com/posva), same auther as vue-router. It provide almost same feature as [unplugin-convention-routes](https://github.com/liujiayii/unplugin-convention-routes) but better intergration with vue-router, include some cool feature like auto generate route types base on your route files to provide autocomplete for vue-router. + +#### Install: + +```bash +npm install -D unplugin-convention-routes +npm install vue-router +``` + +### React + +> since v0.19.0 we only support react-router v6, if you are using react-router v5 use v0.18.2. + +#### Install: + +```bash +npm install -D unplugin-convention-routes +npm install react-router react-router-dom +``` + +### Solid + +#### Install: + +```bash +npm install -D unplugin-convention-routes +npm install @solidjs/router +``` + +### Vite config + +Add to your `vite.config.js`: + +```js +import Pages from 'unplugin-convention-routes' + +export default { + plugins: [ + // ... + Pages(), + ], +} +``` + +## Overview + +By default a page is a Vue component exported from a `.vue` or `.js` file in the +`src/pages` directory. + +You can access the generated routes by importing the `~pages` +module in your application. + +### Vue + +```ts +import routes from '~pages' +import { createRouter } from 'vue-router' + +const router = createRouter({ + // ... + routes, +}) +``` + +**Type** + +```ts +// vite-env.d.ts +/// +``` + +### React + +**experimental** + +```tsx +import routes from '~react-pages' +import { StrictMode, Suspense } from 'react' +import { createRoot } from 'react-dom/client' + +import { + BrowserRouter, + useRoutes, +} from 'react-router-dom' + +function App() { + return ( + Loading...

}> + {useRoutes(routes)} +
+ ) +} + +const app = createRoot(document.getElementById('root')!) + +app.render( + + + + + , +) +``` + +**Type** + +```ts +// vite-env.d.ts +/// +``` + +### Solid + +**experimental** + +```tsx +import { Router, useRoutes } from '@solidjs/router' +import routes from '~solid-pages' +import { render } from 'solid-js/web' + +render( + () => { + const Routes = useRoutes(routes) + return ( + + + + ) + }, + document.getElementById('root') as HTMLElement, +) +``` + +**Type** + +```ts +// vite-env.d.ts +/// +``` + +## Configuration + +To use custom configuration, pass your options to Pages when instantiating the +plugin: + +```js +// vite.config.js +import Pages from 'unplugin-convention-routes' + +export default { + plugins: [ + Pages({ + dirs: 'src/views', + }), + ], +} +``` + +### dirs + +- **Type:** `string | (string | PageOptions)[]` +- **Default:** `'src/pages'` + +Paths to the pages directory. Supports globs. + +Can be: + +- single path: routes point to `/` +- array of paths: all routes in the paths point to `/` +- array of `PageOptions`, Check below ๐Ÿ‘‡ + +```ts +interface PageOptions { + /** + * Page base directory. + * @default 'src/pages' + */ + dir: string + /** + * Page base route. + */ + baseRoute: string + /** + * Page file pattern. + * @example `**\/*.page.vue` + */ + filePattern?: string +} +``` + +Specifying a glob or an array of `PageOptions` allow you to use multiple +pages folder, and specify the base route to append to the path and the route +name. + +Additionally, you can specify a `filePattern` to filter the files that will be used as pages. + +#### Example + +Folder structure + +```bash +src/ + โ”œโ”€โ”€ features/ + โ”‚ โ””โ”€โ”€ dashboard/ + โ”‚ โ”œโ”€โ”€ code/ + โ”‚ โ”œโ”€โ”€ components/ + โ”‚ โ””โ”€โ”€ pages/ + โ”œโ”€โ”€ admin/ + โ”‚ โ”œโ”€โ”€ code/ + โ”‚ โ”œโ”€โ”€ components/ + โ”‚ โ””โ”€โ”€ pages/ + โ””โ”€โ”€ pages/ +``` + +Config + +```js +// vite.config.js +export default { + plugins: [ + Pages({ + dirs: [ + // basic + { dir: 'src/pages', baseRoute: '' }, + // features dir for pages + { dir: 'src/features/**/pages', baseRoute: 'features' }, + // with custom file pattern + { dir: 'src/admin/pages', baseRoute: 'admin', filePattern: '**/*.page.*' }, + ], + }), + ], +} +``` + +### extensions + +- **Type:** `string[]` +- **Default:** + - Vue: `['vue', 'ts', 'js']` + - React: `['tsx', 'jsx', 'ts', 'js']` + - Solid: `['tsx', 'jsx', 'ts', 'js']` + +An array of valid file extensions for pages. If multiple extensions match for a file, the first one is used. + +### exclude + +- **Type:** `string[]` +- **Default:** `[]` + +An array of glob patterns to exclude matches. + +```bash +# folder structure +src/pages/ + โ”œโ”€โ”€ users/ + โ”‚ โ”œโ”€โ”€ components + โ”‚ โ”‚ โ””โ”€โ”€ form.vue + โ”‚ โ”œโ”€โ”€ [id].vue + โ”‚ โ””โ”€โ”€ index.vue + โ””โ”€โ”€ home.vue +``` + +```js +// vite.config.js +export default { + plugins: [ + Pages({ + exclude: ['**/components/*.vue'], + }), + ], +} +``` + +### importMode + +- **Type:** `'sync' | 'async' | (filepath: string, pluginOptions: ResolvedOptions) => 'sync' | 'async')` +- **Default:** + - Top level index file: `'sync'`, others: `async`. + +Import mode can be set to either `async`, `sync`, or a function which returns +one of those values. + +To get more fine-grained control over which routes are loaded sync/async, you +can use a function to resolve the value based on the route path. For example: + +```js +// vite.config.js +export default { + plugins: [ + Pages({ + importMode(filepath, options) { + // default resolver + // for (const page of options.dirs) { + // if (page.baseRoute === '' && filepath.startsWith(`/${page.dir}/index`)) + // return 'sync' + // } + // return 'async' + + // Load about page synchronously, all other pages are async. + return filepath.includes('about') ? 'sync' : 'async' + }, + }), + ], +} +``` + +If you are using `async` mode with `react-router`, you will need to wrap your route components with `Suspense`: + +```jsx +function App() { + return ( + Loading...

}> + {useRoutes(routes)} +
+ ) +} +``` + +### importPath + +- **Type:** `'absolute' | 'relative'` +- **Default:** `'relative'` + +Import page components from absolute or relative paths. The default behavior is to import from relative paths, but in some special cases, it can be set to `'absolute'` to import from absolute paths. + +For example, if your page components are located in the `app/pages` directory and you have set `base: /app/` in your `vite.config.js`, you should set `importPath` to `'absolute'` in order to correctly import the page components. + +```js +// vite.config.js +export default { + base: '/app/', + plugins: [ + Pages({ + dirs: 'app/pages', + + // It should be set to 'absolute' in this case. + importPath: 'absolute', + }), + ], +} +``` + +See [#492](https://github.com/liujiayii/unplugin-convention-routes/issues/492) for more details. + +### routeBlockLang + +- **Type:** `string` +- **Default:** `'json5'` + +Default SFC route block parser. + +### routeStyle + +- **Type:** `'next' | 'nuxt' | 'remix'` +- **Default:** `next` + +Use file system dynamic routing supporting: + +- [Nextjs Routing](https://nextjs.org/docs/routing/introduction) +- [Nuxtjs Routing](https://nuxtjs.org/docs/2.x/features/file-system-routing) +- [Remix Routing](https://remix.run/docs/en/v1/guides/routing) + +### routeNameSeparator + +- **Type:** `string` +- **Default:** `-` + +Separator for generated route names. + +### resolver + +- **Type:** `'vue' | 'react' | 'solid' | PageResolver` +- **Default:** `'auto detect'` + +Route resolver, support `vue`, `react`, `solid` or custom `PageResolver`. + +### moduleId + +- **Type:** `string` +- **Default:** + - Vue: `'~pages'` + - React: `'~react-pages'` + - Solid: `'~solid-pages'` + +Module id for routes import, useful when you what to use multiple pages plugin in one project. + +### extendRoute + +- **Type:** + `(route: any, parent: any | undefined) => any | void` + +A function that takes a route and optionally returns a modified route. This is +useful for augmenting your routes with extra data (e.g. route metadata). + +```js +// vite.config.js +export default { + // ... + plugins: [ + Pages({ + extendRoute(route, parent) { + if (route.path === '/') { + // Index is unauthenticated. + return route + } + + // Augment the route with meta that indicates that the route requires authentication. + return { + ...route, + meta: { auth: true }, + } + }, + }), + ], +} +``` + +### onRoutesGenerated + +- **Type:** `(routes: any[]) => Awaitable` + +A function that takes a generated routes and optionally returns a modified +generated routes. + +### onClientGenerated + +- **Type:** `(clientCode: string) => Awaitable` + +A function that takes a generated client code and optionally returns a modified +generated client code. + +### SFC custom block for Route Data + +Add route meta to the route by adding a `` block to the SFC. This will be +directly added to the route after it is generated, and will override it. + +You can specific a parser to use using ``, or set a default +parser using `routeBlockLang` option. + +- **Supported parser:** JSON, JSON5, YAML +- **Default:** JSON5 + +JSON/JSON5: + +```html + +{ + name: "name-override", + meta: { + requiresAuth: false + } +} + +``` + +YAML: + +```html + +name: name-override +meta: + requiresAuth: true + +``` + +#### Syntax Highlighting `` + +To enable syntax highlighting `` in VS Code using [Vetur's Custom Code Blocks](https://vuejs.github.io/vetur/highlighting.html#custom-block) add the following snippet to your preferences... + + 1. update setting + + ``` + "vetur.grammar.customBlocks": { + "route": "json" + } +``` + + 2. Run the command in vscode + + `Vetur: Generate grammar from vetur.grammar.customBlocks` + + 3. Restart VS Code to get syntax highlighting for custom blocks. + +### JSX/TSX YAML format comments for Route Data(In Vue) + +Add route meta to the route by adding a comment block starts with `route` to the JSX or TSX file(In Vue). This will be directly added to the route after it is generated, and will override it. + +This feature only support JSX/TSX in vue, and will parse only the first block of comments which should also start with `route`. + +Now only `yaml` parser supported. + +- **Type:** `'vue'` +- **Supported parser:** YAML + +```jsx +/* +route + +name: name-override +meta: + requiresAuth: false + id: 1234 + string: "1234" +*/ +``` + +## File System Routing + +Inspired by the routing from +[NuxtJS](https://nuxtjs.org/guides/features/file-system-routing) ๐Ÿ’š + +Pages automatically generates an array of routes for you to plug-in to your +instance of Vue Router. These routes are determined by the structure of the +files in your pages directory. Simply create `.vue` files in your pages +directory and routes will automatically be created for you, no additional +configuration required! + +For more advanced use cases, you can tailor Pages to fit the needs of your app +through [configuration](#configuration). + +- [Basic Routing](#basic-routing) +- [Index Routes](#index-routes) +- [Dynamic Routes](#dynamic-routes) +- [Nested Routes](#nested-routes) +- [Catch-all Routes](#catch-all-routes) + +### Basic Routing + +Pages will automatically map files from your pages directory to a route with the +same name: + +- `src/pages/users.vue` -> `/users` +- `src/pages/users/profile.vue` -> `/users/profile` +- `src/pages/settings.vue` -> `/settings` + +### Index Routes + +Files with the name `index` are treated as the index page of a route: + +- `src/pages/index.vue` -> `/` +- `src/pages/users/index.vue` -> `/users` + +### Dynamic Routes + +Dynamic routes are denoted using square brackets. Both directories and pages can +be dynamic: + +- `src/pages/users/[id].vue` -> `/users/:id` (`/users/one`) +- `src/pages/[user]/settings.vue` -> `/:user/settings` (`/one/settings`) + +Any dynamic parameters will be passed to the page as props. For example, given +the file `src/pages/users/[id].vue`, the route `/users/abc` will be passed the +following props: + +```json +{ "id": "abc" } +``` + +### Nested Routes + +We can make use of Vue Routers child routes to create nested layouts. The parent +component can be defined by giving it the same name as the directory that +contains your child routes. + +For example, this directory structure: + +``` +src/pages/ + โ”œโ”€โ”€ users/ + โ”‚ โ”œโ”€โ”€ [id].vue + โ”‚ โ””โ”€โ”€ index.vue + โ””โ”€โ”€ users.vue +``` + +will result in this routes configuration: + +```json5 +[ + { + "path": "/users", + "component": "/src/pages/users.vue", + "children": [ + { + "path": "", + "component": "/src/pages/users/index.vue", + "name": "users" + }, + { + "path": ":id", + "component": "/src/pages/users/[id].vue", + "name": "users-id" + } + ] + } +] +``` + +### Catch-all Routes + +Catch-all routes are denoted with square brackets containing an ellipsis: + +- `src/pages/[...all].vue` -> `/*` (`/non-existent-page`) + +The text after the ellipsis will be used both to name the route, and as the name +of the prop in which the route parameters are passed. + +## Sitemap generation + +If you need to generate a sitemap from generated routes, you can use [unplugin-convention-routes-sitemap](https://github.com/jbaubree/unplugin-convention-routes-sitemap). +This plugin allow you to automatically generate sitemap.xml and robots.xml files with customization. + +## License + +MIT License ยฉ 2021-PRESENT [liujiayii](https://github.com/liujiayii) diff --git a/README.md b/README.md index 01b2455..904ce1d 100644 --- a/README.md +++ b/README.md @@ -1,634 +1,30 @@ -# unplugin-convention-routes +

+ + + + + + +
+

-[![npm version](https://badgen.net/npm/v/unplugin-convention-routes)](https://www.npmjs.com/package/unplugin-convention-routes) -[![monthly downloads](https://badgen.net/npm/dm/unplugin-convention-routes)](https://www.npmjs.com/package/unplugin-convention-routes) -[![types](https://badgen.net/npm/types/unplugin-convention-routes)](https://github.com/hannoeru/unplugin-convention-routes/blob/main/src/types.ts) -[![license](https://badgen.net/npm/license/unplugin-convention-routes)](https://github.com/hannoeru/unplugin-convention-routes/blob/main/LICENSE) +

unplugin-convention-routes

-[![Open in Visual Studio Code](https://img.shields.io/static/v1?logo=visualstudiocode&label=&message=Open%20in%20Visual%20Studio%20Code&labelColor=2c2c32&color=007acc&logoColor=007acc)](https://open.vscode.dev/hannoeru/unplugin-convention-routes) +- ๐Ÿ”ฅ ๅŸบไบŽๆ–‡ไปถ็ณป็ปŸ็š„็บฆๅฎšๅผ่ทฏ็”ฑ่งฃๅ†ณๆ–นๆกˆใ€‚ +- ๐Ÿ”ฅ ๅŸบไบŽ`unplugin`ๅฏน`vite-plugin-pages`่ฟ›่กŒไบ†็งปๆค๏ผŒ่ƒฝๅŒๆ—ถๆ”ฏๆŒ`vite`ใ€`webpack`ใ€`rsbuild`ใ€`farm`็ญ‰ๆž„ๅปบๆก†ๆžถ๏ผŒๅนถๅŒๆ—ถๆ”ฏๆŒ`react`ใ€`vue`ใ€`solid`ไธ‰ๅคงๆก†ๆžถใ€‚ +- โš ๏ธ ๅฐšๅœจๅผ€ๅ‘ไธญ๏ผŒ่ฟ˜ไธ่ƒฝๆญฃๅธธไฝฟ็”จ๏ผŒ่ฏทๅ‹ฟไฝฟ็”จใ€‚ไธชไบบไป…ๆต‹่ฏ•ไบ†`vue+vite`ใ€`vue`+`rsbuild`ใ€`react`+`vite`ใ€`react`+`rsbuild`ใ€‚ -> File system based routing for Vue 3 / React / Solid applications using -> [Vite](https://github.com/vitejs/vite) +--- -## Getting Started +## ๅ‹ๆƒ…้“พๆŽฅ -### Vue +- [unplugin](https://github.com/unjs/unplugin) +- [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages) -**๐ŸšจImportant Notes๐Ÿšจ** - -We recommend that Vue users use [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) instead of this plugin. - -[unplugin-vue-router](https://github.com/posva/unplugin-vue-router) is a unplugin library created by [@posva](https://github.com/posva), same auther as vue-router. It provide almost same feature as [unplugin-convention-routes](https://github.com/hannoeru/unplugin-convention-routes) but better intergration with vue-router, include some cool feature like auto generate route types base on your route files to provide autocomplete for vue-router. - -#### Install: - -```bash -npm install -D unplugin-convention-routes -npm install vue-router -``` - -### React - -> since v0.19.0 we only support react-router v6, if you are using react-router v5 use v0.18.2. - -#### Install: - -```bash -npm install -D unplugin-convention-routes -npm install react-router react-router-dom -``` - -### Solid - -#### Install: +## ๐Ÿ“ฆ ๅฎ‰่ฃ… ```bash -npm install -D unplugin-convention-routes -npm install @solidjs/router -``` - -### Vite config - -Add to your `vite.config.js`: - -```js -import Pages from 'unplugin-convention-routes' - -export default { - plugins: [ - // ... - Pages(), - ], -} -``` - -## Overview - -By default a page is a Vue component exported from a `.vue` or `.js` file in the -`src/pages` directory. - -You can access the generated routes by importing the `~pages` -module in your application. - -### Vue - -```ts -import routes from '~pages' -import { createRouter } from 'vue-router' - -const router = createRouter({ - // ... - routes, -}) -``` - -**Type** - -```ts -// vite-env.d.ts -/// -``` - -### React - -**experimental** - -```tsx -import routes from '~react-pages' -import { StrictMode, Suspense } from 'react' -import { createRoot } from 'react-dom/client' - -import { - BrowserRouter, - useRoutes, -} from 'react-router-dom' - -function App() { - return ( - Loading...

}> - {useRoutes(routes)} -
- ) -} - -const app = createRoot(document.getElementById('root')!) - -app.render( - - - - - , -) -``` - -**Type** - -```ts -// vite-env.d.ts -/// -``` - -### Solid - -**experimental** - -```tsx -import { Router, useRoutes } from '@solidjs/router' -import routes from '~solid-pages' -import { render } from 'solid-js/web' - -render( - () => { - const Routes = useRoutes(routes) - return ( - - - - ) - }, - document.getElementById('root') as HTMLElement, -) -``` - -**Type** - -```ts -// vite-env.d.ts -/// -``` - -## Configuration - -To use custom configuration, pass your options to Pages when instantiating the -plugin: - -```js -// vite.config.js -import Pages from 'unplugin-convention-routes' - -export default { - plugins: [ - Pages({ - dirs: 'src/views', - }), - ], -} -``` - -### dirs - -- **Type:** `string | (string | PageOptions)[]` -- **Default:** `'src/pages'` - -Paths to the pages directory. Supports globs. - -Can be: - -- single path: routes point to `/` -- array of paths: all routes in the paths point to `/` -- array of `PageOptions`, Check below ๐Ÿ‘‡ - -```ts -interface PageOptions { - /** - * Page base directory. - * @default 'src/pages' - */ - dir: string - /** - * Page base route. - */ - baseRoute: string - /** - * Page file pattern. - * @example `**\/*.page.vue` - */ - filePattern?: string -} -``` - -Specifying a glob or an array of `PageOptions` allow you to use multiple -pages folder, and specify the base route to append to the path and the route -name. - -Additionally, you can specify a `filePattern` to filter the files that will be used as pages. - -#### Example - -Folder structure - -```bash -src/ - โ”œโ”€โ”€ features/ - โ”‚ โ””โ”€โ”€ dashboard/ - โ”‚ โ”œโ”€โ”€ code/ - โ”‚ โ”œโ”€โ”€ components/ - โ”‚ โ””โ”€โ”€ pages/ - โ”œโ”€โ”€ admin/ - โ”‚ โ”œโ”€โ”€ code/ - โ”‚ โ”œโ”€โ”€ components/ - โ”‚ โ””โ”€โ”€ pages/ - โ””โ”€โ”€ pages/ -``` - -Config - -```js -// vite.config.js -export default { - plugins: [ - Pages({ - dirs: [ - // basic - { dir: 'src/pages', baseRoute: '' }, - // features dir for pages - { dir: 'src/features/**/pages', baseRoute: 'features' }, - // with custom file pattern - { dir: 'src/admin/pages', baseRoute: 'admin', filePattern: '**/*.page.*' }, - ], - }), - ], -} -``` - -### extensions - -- **Type:** `string[]` -- **Default:** - - Vue: `['vue', 'ts', 'js']` - - React: `['tsx', 'jsx', 'ts', 'js']` - - Solid: `['tsx', 'jsx', 'ts', 'js']` - -An array of valid file extensions for pages. If multiple extensions match for a file, the first one is used. - -### exclude - -- **Type:** `string[]` -- **Default:** `[]` - -An array of glob patterns to exclude matches. - -```bash -# folder structure -src/pages/ - โ”œโ”€โ”€ users/ - โ”‚ โ”œโ”€โ”€ components - โ”‚ โ”‚ โ””โ”€โ”€ form.vue - โ”‚ โ”œโ”€โ”€ [id].vue - โ”‚ โ””โ”€โ”€ index.vue - โ””โ”€โ”€ home.vue -``` - -```js -// vite.config.js -export default { - plugins: [ - Pages({ - exclude: ['**/components/*.vue'], - }), - ], -} -``` - -### importMode - -- **Type:** `'sync' | 'async' | (filepath: string, pluginOptions: ResolvedOptions) => 'sync' | 'async')` -- **Default:** - - Top level index file: `'sync'`, others: `async`. - -Import mode can be set to either `async`, `sync`, or a function which returns -one of those values. - -To get more fine-grained control over which routes are loaded sync/async, you -can use a function to resolve the value based on the route path. For example: - -```js -// vite.config.js -export default { - plugins: [ - Pages({ - importMode(filepath, options) { - // default resolver - // for (const page of options.dirs) { - // if (page.baseRoute === '' && filepath.startsWith(`/${page.dir}/index`)) - // return 'sync' - // } - // return 'async' - - // Load about page synchronously, all other pages are async. - return filepath.includes('about') ? 'sync' : 'async' - }, - }), - ], -} -``` - -If you are using `async` mode with `react-router`, you will need to wrap your route components with `Suspense`: - -```jsx -function App() { - return ( - Loading...

}> - {useRoutes(routes)} -
- ) -} -``` - -### importPath - -- **Type:** `'absolute' | 'relative'` -- **Default:** `'relative'` - -Import page components from absolute or relative paths. The default behavior is to import from relative paths, but in some special cases, it can be set to `'absolute'` to import from absolute paths. - -For example, if your page components are located in the `app/pages` directory and you have set `base: /app/` in your `vite.config.js`, you should set `importPath` to `'absolute'` in order to correctly import the page components. - -```js -// vite.config.js -export default { - base: '/app/', - plugins: [ - Pages({ - dirs: 'app/pages', - - // It should be set to 'absolute' in this case. - importPath: 'absolute', - }), - ], -} +pnpm i unplugin-convention-routes ``` -See [#492](https://github.com/hannoeru/unplugin-convention-routes/issues/492) for more details. - -### routeBlockLang - -- **Type:** `string` -- **Default:** `'json5'` - -Default SFC route block parser. - -### routeStyle - -- **Type:** `'next' | 'nuxt' | 'remix'` -- **Default:** `next` - -Use file system dynamic routing supporting: - -- [Nextjs Routing](https://nextjs.org/docs/routing/introduction) -- [Nuxtjs Routing](https://nuxtjs.org/docs/2.x/features/file-system-routing) -- [Remix Routing](https://remix.run/docs/en/v1/guides/routing) - -### routeNameSeparator - -- **Type:** `string` -- **Default:** `-` - -Separator for generated route names. - -### resolver - -- **Type:** `'vue' | 'react' | 'solid' | PageResolver` -- **Default:** `'auto detect'` - -Route resolver, support `vue`, `react`, `solid` or custom `PageResolver`. - -### moduleId - -- **Type:** `string` -- **Default:** - - Vue: `'~pages'` - - React: `'~react-pages'` - - Solid: `'~solid-pages'` - -Module id for routes import, useful when you what to use multiple pages plugin in one project. - -### extendRoute - -- **Type:** - `(route: any, parent: any | undefined) => any | void` - -A function that takes a route and optionally returns a modified route. This is -useful for augmenting your routes with extra data (e.g. route metadata). - -```js -// vite.config.js -export default { - // ... - plugins: [ - Pages({ - extendRoute(route, parent) { - if (route.path === '/') { - // Index is unauthenticated. - return route - } - - // Augment the route with meta that indicates that the route requires authentication. - return { - ...route, - meta: { auth: true }, - } - }, - }), - ], -} -``` - -### onRoutesGenerated - -- **Type:** `(routes: any[]) => Awaitable` - -A function that takes a generated routes and optionally returns a modified -generated routes. - -### onClientGenerated - -- **Type:** `(clientCode: string) => Awaitable` - -A function that takes a generated client code and optionally returns a modified -generated client code. - -### SFC custom block for Route Data - -Add route meta to the route by adding a `` block to the SFC. This will be -directly added to the route after it is generated, and will override it. - -You can specific a parser to use using ``, or set a default -parser using `routeBlockLang` option. - -- **Supported parser:** JSON, JSON5, YAML -- **Default:** JSON5 - -JSON/JSON5: - -```html - -{ - name: "name-override", - meta: { - requiresAuth: false - } -} - -``` - -YAML: - -```html - -name: name-override -meta: - requiresAuth: true - -``` - -#### Syntax Highlighting `` - -To enable syntax highlighting `` in VS Code using [Vetur's Custom Code Blocks](https://vuejs.github.io/vetur/highlighting.html#custom-block) add the following snippet to your preferences... - - 1. update setting - - ``` - "vetur.grammar.customBlocks": { - "route": "json" - } -``` - - 2. Run the command in vscode - - `Vetur: Generate grammar from vetur.grammar.customBlocks` - - 3. Restart VS Code to get syntax highlighting for custom blocks. - -### JSX/TSX YAML format comments for Route Data(In Vue) - -Add route meta to the route by adding a comment block starts with `route` to the JSX or TSX file(In Vue). This will be directly added to the route after it is generated, and will override it. - -This feature only support JSX/TSX in vue, and will parse only the first block of comments which should also start with `route`. - -Now only `yaml` parser supported. - -- **Type:** `'vue'` -- **Supported parser:** YAML - -```jsx -/* -route - -name: name-override -meta: - requiresAuth: false - id: 1234 - string: "1234" -*/ -``` - -## File System Routing - -Inspired by the routing from -[NuxtJS](https://nuxtjs.org/guides/features/file-system-routing) ๐Ÿ’š - -Pages automatically generates an array of routes for you to plug-in to your -instance of Vue Router. These routes are determined by the structure of the -files in your pages directory. Simply create `.vue` files in your pages -directory and routes will automatically be created for you, no additional -configuration required! - -For more advanced use cases, you can tailor Pages to fit the needs of your app -through [configuration](#configuration). - -- [Basic Routing](#basic-routing) -- [Index Routes](#index-routes) -- [Dynamic Routes](#dynamic-routes) -- [Nested Routes](#nested-routes) -- [Catch-all Routes](#catch-all-routes) - -### Basic Routing - -Pages will automatically map files from your pages directory to a route with the -same name: - -- `src/pages/users.vue` -> `/users` -- `src/pages/users/profile.vue` -> `/users/profile` -- `src/pages/settings.vue` -> `/settings` - -### Index Routes - -Files with the name `index` are treated as the index page of a route: - -- `src/pages/index.vue` -> `/` -- `src/pages/users/index.vue` -> `/users` - -### Dynamic Routes - -Dynamic routes are denoted using square brackets. Both directories and pages can -be dynamic: - -- `src/pages/users/[id].vue` -> `/users/:id` (`/users/one`) -- `src/pages/[user]/settings.vue` -> `/:user/settings` (`/one/settings`) - -Any dynamic parameters will be passed to the page as props. For example, given -the file `src/pages/users/[id].vue`, the route `/users/abc` will be passed the -following props: - -```json -{ "id": "abc" } -``` - -### Nested Routes - -We can make use of Vue Routers child routes to create nested layouts. The parent -component can be defined by giving it the same name as the directory that -contains your child routes. - -For example, this directory structure: - -``` -src/pages/ - โ”œโ”€โ”€ users/ - โ”‚ โ”œโ”€โ”€ [id].vue - โ”‚ โ””โ”€โ”€ index.vue - โ””โ”€โ”€ users.vue -``` - -will result in this routes configuration: - -```json5 -[ - { - "path": "/users", - "component": "/src/pages/users.vue", - "children": [ - { - "path": "", - "component": "/src/pages/users/index.vue", - "name": "users" - }, - { - "path": ":id", - "component": "/src/pages/users/[id].vue", - "name": "users-id" - } - ] - } -] -``` - -### Catch-all Routes - -Catch-all routes are denoted with square brackets containing an ellipsis: - -- `src/pages/[...all].vue` -> `/*` (`/non-existent-page`) - -The text after the ellipsis will be used both to name the route, and as the name -of the prop in which the route parameters are passed. - -## Sitemap generation - -If you need to generate a sitemap from generated routes, you can use [unplugin-convention-routes-sitemap](https://github.com/jbaubree/unplugin-convention-routes-sitemap). -This plugin allow you to automatically generate sitemap.xml and robots.xml files with customization. - -## License - -MIT License ยฉ 2021-PRESENT [hannoeru](https://github.com/hannoeru) +## ๐Ÿ”จ ็คบไพ‹ diff --git a/virtual-package/react.d.ts b/client-react.d.ts similarity index 100% rename from virtual-package/react.d.ts rename to client-react.d.ts diff --git a/virtual-package/solid.d.ts b/client-solid.d.ts similarity index 100% rename from virtual-package/solid.d.ts rename to client-solid.d.ts diff --git a/virtual-package/vue.d.ts b/client-vue.d.ts similarity index 100% rename from virtual-package/vue.d.ts rename to client-vue.d.ts diff --git a/examples/react-rsbuild/env.d.ts b/examples/react-rsbuild/env.d.ts new file mode 100644 index 0000000..a2c4162 --- /dev/null +++ b/examples/react-rsbuild/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/react/env.d.ts b/examples/react/env.d.ts new file mode 100644 index 0000000..a2c4162 --- /dev/null +++ b/examples/react/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/remix-style/vite.config.ts b/examples/remix-style/vite.config.ts index 1b88597..75790ec 100644 --- a/examples/remix-style/vite.config.ts +++ b/examples/remix-style/vite.config.ts @@ -1,11 +1,11 @@ import react from '@vitejs/plugin-react' import { defineConfig } from 'vite' -import Pages from 'unplugin-convention-routes' +import Pages from 'unplugin-convention-routes/vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ react(), - Pages({ routeStyle: 'remix' }), + Pages({ routeStyle: 'remix', resolver: 'react' }), ], }) diff --git a/examples/vue-rsbuild/.gitignore b/examples/vue-rsbuild/.gitignore new file mode 100644 index 0000000..38d7344 --- /dev/null +++ b/examples/vue-rsbuild/.gitignore @@ -0,0 +1,13 @@ +# Local +.DS_Store +*.local +*.log* + +# Dist +node_modules +dist/ + +# IDE +.vscode/* +!.vscode/extensions.json +.idea diff --git a/examples/vue-rsbuild/package.json b/examples/vue-rsbuild/package.json new file mode 100644 index 0000000..429e266 --- /dev/null +++ b/examples/vue-rsbuild/package.json @@ -0,0 +1,20 @@ +{ + "name": "rsbuild-vue", + "private": true, + "version": "1.0.0", + "scripts": { + "dev": "rsbuild dev", + "build": "rsbuild build", + "preview": "rsbuild preview" + }, + "dependencies": { + "vue": "^3.5.12" + }, + "devDependencies": { + "@rsbuild/core": "^1.0.13", + "@rsbuild/plugin-vue": "^1.0.1", + "unplugin-convention-routes": "workspace:*", + "typescript": "^5.6.3" + }, + "packageManager": "pnpm@9.12.1" +} \ No newline at end of file diff --git a/examples/vue-rsbuild/rsbuild.config.ts b/examples/vue-rsbuild/rsbuild.config.ts new file mode 100644 index 0000000..80bf36b --- /dev/null +++ b/examples/vue-rsbuild/rsbuild.config.ts @@ -0,0 +1,36 @@ +import { defineConfig } from '@rsbuild/core'; +import { pluginVue } from '@rsbuild/plugin-vue'; +import Pages from 'unplugin-convention-routes/rspack' +import {resolve} from 'path' + +export default defineConfig({ + tools:{ + rspack:{ + plugins: [ + Pages({ + resolver: 'vue', + dirs: [ + // issue #68 + { dir: resolve(__dirname, './src/pages'), baseRoute: '' }, + { dir: 'src/features/**/pages', baseRoute: 'features' }, + { dir: 'src/admin/pages', baseRoute: 'admin' }, + ], + extensions: ['vue', 'jsx'], + extendRoute(route: any) { + if (route.name === 'about') + route.props = (route: any) => ({ query: route.query.q }) + + if (route.name === 'components') { + return { + ...route, + beforeEnter: (route: any) => { + console.log(route) + }, + } + } + }, + }),] + } + }, + plugins: [pluginVue()], +}); diff --git a/examples/vue-rsbuild/src/App.vue b/examples/vue-rsbuild/src/App.vue new file mode 100644 index 0000000..98240ae --- /dev/null +++ b/examples/vue-rsbuild/src/App.vue @@ -0,0 +1,3 @@ + diff --git a/examples/vue-rsbuild/src/admin/pages/index.vue b/examples/vue-rsbuild/src/admin/pages/index.vue new file mode 100644 index 0000000..a7c073f --- /dev/null +++ b/examples/vue-rsbuild/src/admin/pages/index.vue @@ -0,0 +1,22 @@ + + + + + + + +{ +meta: { + requiresAuth: false +} +} + diff --git a/examples/vue-rsbuild/src/env.d.ts b/examples/vue-rsbuild/src/env.d.ts new file mode 100644 index 0000000..2e1d25c --- /dev/null +++ b/examples/vue-rsbuild/src/env.d.ts @@ -0,0 +1,10 @@ +/// +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue'; + + // biome-ignore lint/complexity/noBannedTypes: reason + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/examples/vue-rsbuild/src/features/admin/pages/admin.vue b/examples/vue-rsbuild/src/features/admin/pages/admin.vue new file mode 100644 index 0000000..4c7366f --- /dev/null +++ b/examples/vue-rsbuild/src/features/admin/pages/admin.vue @@ -0,0 +1,12 @@ + + + +{ + meta: { + requiresAuth: false + } +} + diff --git a/examples/vue-rsbuild/src/features/dashboard/pages/dashboard.vue b/examples/vue-rsbuild/src/features/dashboard/pages/dashboard.vue new file mode 100644 index 0000000..ad74b81 --- /dev/null +++ b/examples/vue-rsbuild/src/features/dashboard/pages/dashboard.vue @@ -0,0 +1,12 @@ + + + +{ + meta: { + requiresAuth: true + } +} + diff --git a/examples/vue-rsbuild/src/features/dashboard/pages/welcome.vue b/examples/vue-rsbuild/src/features/dashboard/pages/welcome.vue new file mode 100644 index 0000000..438486f --- /dev/null +++ b/examples/vue-rsbuild/src/features/dashboard/pages/welcome.vue @@ -0,0 +1,12 @@ + + + +{ + meta: { + requiresAuth: true + } +} + diff --git a/examples/vue-rsbuild/src/index.css b/examples/vue-rsbuild/src/index.css new file mode 100644 index 0000000..e69de29 diff --git a/examples/vue-rsbuild/src/index.ts b/examples/vue-rsbuild/src/index.ts new file mode 100644 index 0000000..c064816 --- /dev/null +++ b/examples/vue-rsbuild/src/index.ts @@ -0,0 +1,25 @@ +import routes from '~unplugin-convention-routes/vue' +import { createApp } from 'vue' +import { createRouter, createWebHistory } from 'vue-router' +import App from './App.vue' + +// @ts-expect-error no routes type +import reactRoutes from '~admin-pages' +import './index.css' + +// eslint-disable-next-line no-console +console.log('vue:', routes) + +// eslint-disable-next-line no-console +console.log('react:', reactRoutes) + +const router = createRouter({ + history: createWebHistory(), + routes, +}) + +const app = createApp(App) + +app.use(router) + +app.mount('#app') diff --git a/examples/vue-rsbuild/src/pages/[...all].vue b/examples/vue-rsbuild/src/pages/[...all].vue new file mode 100644 index 0000000..361b082 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/[...all].vue @@ -0,0 +1,3 @@ + diff --git a/examples/vue-rsbuild/src/pages/[sensor].vue b/examples/vue-rsbuild/src/pages/[sensor].vue new file mode 100644 index 0000000..61a094b --- /dev/null +++ b/examples/vue-rsbuild/src/pages/[sensor].vue @@ -0,0 +1,4 @@ + diff --git a/examples/vue-rsbuild/src/pages/[sensor]/current.vue b/examples/vue-rsbuild/src/pages/[sensor]/current.vue new file mode 100644 index 0000000..0b0ea7b --- /dev/null +++ b/examples/vue-rsbuild/src/pages/[sensor]/current.vue @@ -0,0 +1,3 @@ + diff --git a/examples/vue-rsbuild/src/pages/__test__/index.vue b/examples/vue-rsbuild/src/pages/__test__/index.vue new file mode 100644 index 0000000..d58ea5d --- /dev/null +++ b/examples/vue-rsbuild/src/pages/__test__/index.vue @@ -0,0 +1,3 @@ + diff --git a/examples/vue-rsbuild/src/pages/about.vue b/examples/vue-rsbuild/src/pages/about.vue new file mode 100644 index 0000000..a499293 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/about.vue @@ -0,0 +1,11 @@ + + + +meta: + lang: yml + diff --git a/examples/vue-rsbuild/src/pages/about/[id].vue b/examples/vue-rsbuild/src/pages/about/[id].vue new file mode 100644 index 0000000..0e786d4 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/about/[id].vue @@ -0,0 +1,26 @@ + + + + + +{ + name: 'about-user-id', + meta: { + requiresAuth: true, + }, +} + diff --git a/examples/vue-rsbuild/src/pages/about/[id]/more.vue b/examples/vue-rsbuild/src/pages/about/[id]/more.vue new file mode 100644 index 0000000..7284d90 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/about/[id]/more.vue @@ -0,0 +1,5 @@ + diff --git a/examples/vue-rsbuild/src/pages/about/[id]/nested.vue b/examples/vue-rsbuild/src/pages/about/[id]/nested.vue new file mode 100644 index 0000000..c1f8011 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/about/[id]/nested.vue @@ -0,0 +1,5 @@ + diff --git a/examples/vue-rsbuild/src/pages/about/index.vue b/examples/vue-rsbuild/src/pages/about/index.vue new file mode 100644 index 0000000..ee31283 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/about/index.vue @@ -0,0 +1,8 @@ + diff --git a/examples/vue-rsbuild/src/pages/blog/[id].vue b/examples/vue-rsbuild/src/pages/blog/[id].vue new file mode 100644 index 0000000..144513d --- /dev/null +++ b/examples/vue-rsbuild/src/pages/blog/[id].vue @@ -0,0 +1,23 @@ + + + + + +{ + name: 'blog-id', + meta: { + requiresAuth: false, + }, +} + diff --git a/examples/vue-rsbuild/src/pages/blog/index.vue b/examples/vue-rsbuild/src/pages/blog/index.vue new file mode 100644 index 0000000..a2602bc --- /dev/null +++ b/examples/vue-rsbuild/src/pages/blog/index.vue @@ -0,0 +1,14 @@ + diff --git a/examples/vue-rsbuild/src/pages/blog/today/[...all].vue b/examples/vue-rsbuild/src/pages/blog/today/[...all].vue new file mode 100644 index 0000000..3667e99 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/blog/today/[...all].vue @@ -0,0 +1,3 @@ + diff --git a/examples/vue-rsbuild/src/pages/blog/today/index.vue b/examples/vue-rsbuild/src/pages/blog/today/index.vue new file mode 100644 index 0000000..8720db9 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/blog/today/index.vue @@ -0,0 +1,5 @@ + diff --git a/examples/vue-rsbuild/src/pages/components.vue b/examples/vue-rsbuild/src/pages/components.vue new file mode 100644 index 0000000..96ffe5b --- /dev/null +++ b/examples/vue-rsbuild/src/pages/components.vue @@ -0,0 +1,8 @@ + + + +meta: + lang: yaml + diff --git a/examples/vue-rsbuild/src/pages/index.vue b/examples/vue-rsbuild/src/pages/index.vue new file mode 100644 index 0000000..d08e861 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/index.vue @@ -0,0 +1,38 @@ + + + +{ + "name": "homepage", + "meta": { + "requiresAuth": false + } +} + diff --git a/examples/vue-rsbuild/src/pages/jsx.jsx b/examples/vue-rsbuild/src/pages/jsx.jsx new file mode 100644 index 0000000..8e1d02a --- /dev/null +++ b/examples/vue-rsbuild/src/pages/jsx.jsx @@ -0,0 +1,18 @@ +/* + + route + +name: blog-id +meta: + requiresAuth: false + id: 1234 +*/ + +import { defineComponent } from 'vue' + +export default defineComponent({ + name: 'TestJSX', + setup() { + return () =>
TestJSX
+ }, +}) diff --git a/examples/vue-rsbuild/src/pages/markdown.md b/examples/vue-rsbuild/src/pages/markdown.md new file mode 100644 index 0000000..f5be0b5 --- /dev/null +++ b/examples/vue-rsbuild/src/pages/markdown.md @@ -0,0 +1 @@ +# hello from markdown file diff --git a/examples/vue-rsbuild/tsconfig.json b/examples/vue-rsbuild/tsconfig.json new file mode 100644 index 0000000..be4dc60 --- /dev/null +++ b/examples/vue-rsbuild/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["DOM", "ES2020"], + "module": "ESNext", + "jsx": "preserve", + "jsxImportSource": "vue", + "noEmit": true, + "strict": true, + "skipLibCheck": true, + "isolatedModules": true, + "resolveJsonModule": true, + "moduleResolution": "bundler", + "useDefineForClassFields": true, + "allowImportingTsExtensions": true + }, + "include": ["src"] +} diff --git a/examples/vue/env.d.ts b/examples/vue/env.d.ts new file mode 100644 index 0000000..de0ba95 --- /dev/null +++ b/examples/vue/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/vue/src/main.ts b/examples/vue/src/main.ts index 072754a..c064816 100644 --- a/examples/vue/src/main.ts +++ b/examples/vue/src/main.ts @@ -1,4 +1,4 @@ -import routes from '~pages' +import routes from '~unplugin-convention-routes/vue' import { createApp } from 'vue' import { createRouter, createWebHistory } from 'vue-router' import App from './App.vue' diff --git a/examples/vue/vite.config.ts b/examples/vue/vite.config.ts index 535da4e..1f1a0df 100644 --- a/examples/vue/vite.config.ts +++ b/examples/vue/vite.config.ts @@ -11,6 +11,7 @@ const config = defineConfig({ include: [/\.vue$/, /\.md$/], }), Pages({ + resolver: 'vue', dirs: [ // issue #68 { dir: resolve(__dirname, './src/pages'), baseRoute: '' }, @@ -33,11 +34,11 @@ const config = defineConfig({ }, }), // test multiple instances - Pages({ - dirs: '../react/src/pages', - resolver: 'react', - moduleId: '~admin-pages', - }), + // Pages({ + // dirs: '../react/src/pages', + // resolver: 'react', + // moduleId: '~admin-pages', + // }), Markdown(), Inspect(), ], diff --git a/package.json b/package.json index 3ae5a1f..2c346ec 100644 --- a/package.json +++ b/package.json @@ -4,14 +4,14 @@ "version": "0.0.1-alpha.1", "packageManager": "pnpm@9.12.3", "description": "File system base router plugin for unplugin", - "author": "hannoeru ", + "author": "liujiayii@foxmail.com", "license": "MIT", - "homepage": "https://github.com/hannoeru/unplugin-convention-routes", + "homepage": "https://github.com/liujiayii/unplugin-convention-routes", "repository": { "type": "git", - "url": "https://github.com/hannoeru/unplugin-convention-routes" + "url": "https://github.com/liujiayii/unplugin-convention-routes" }, - "bugs": "https://github.com/hannoeru/unplugin-convention-routes/issues", + "bugs": "https://github.com/liujiayii/unplugin-convention-routes/issues", "keywords": [ "vite", "vue", @@ -64,10 +64,6 @@ "import": "./dist/types.js", "require": "./dist/types.cjs" }, - "./react": { - "import": "./react.js", - "require": "./react.js" - }, "./*": "./*" }, "main": "dist/index.cjs", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e255728..4ab5e84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -328,6 +328,25 @@ importers: specifier: ^0.23.8 version: 0.23.8(rollup@4.25.0)(vite@5.4.11(@types/node@22.9.0)(terser@5.36.0)) + examples/vue-rsbuild: + dependencies: + vue: + specifier: ^3.5.12 + version: 3.5.12(typescript@5.6.3) + devDependencies: + '@rsbuild/core': + specifier: ^1.0.13 + version: 1.1.0 + '@rsbuild/plugin-vue': + specifier: ^1.0.1 + version: 1.0.4(@rsbuild/core@1.1.0)(@vue/compiler-sfc@3.5.12)(esbuild@0.24.0)(vue@3.5.12(typescript@5.6.3)) + typescript: + specifier: ^5.6.3 + version: 5.6.3 + unplugin-convention-routes: + specifier: workspace:* + version: link:../.. + examples/vue-ssr: dependencies: vue: @@ -1259,6 +1278,11 @@ packages: peerDependencies: '@rsbuild/core': 1.x + '@rsbuild/plugin-vue@1.0.4': + resolution: {integrity: sha512-5Ue3ufWUVllJG1N7GDiTIELbIZZvIzWcDjO62bIYvtcLCG19sL0PPuBge4tXl+mlW2ncJYBoUsVI5aee9qNTWw==} + peerDependencies: + '@rsbuild/core': 1.x + '@rspack/binding-darwin-arm64@1.1.1': resolution: {integrity: sha512-BnvGPWObGZ2ZVnxe4K3NKwAWxYubOJvfwporXWD3NgkzeV5xJqGBFWRDnr/nfsFpgCTI8goxK5db/wb7NVzLqg==} cpu: [arm64] @@ -3979,6 +4003,18 @@ packages: peerDependencies: eslint: '>=6.0.0' + vue-loader@17.4.2: + resolution: {integrity: sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==} + peerDependencies: + '@vue/compiler-sfc': '*' + vue: '*' + webpack: ^4.1.0 || ^5.0.0-0 + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + vue: + optional: true + vue-router@4.2.5: resolution: {integrity: sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==} peerDependencies: @@ -4789,6 +4825,19 @@ snapshots: '@rspack/plugin-react-refresh': 1.0.0(react-refresh@0.14.2) react-refresh: 0.14.2 + '@rsbuild/plugin-vue@1.0.4(@rsbuild/core@1.1.0)(@vue/compiler-sfc@3.5.12)(esbuild@0.24.0)(vue@3.5.12(typescript@5.6.3))': + dependencies: + '@rsbuild/core': 1.1.0 + vue-loader: 17.4.2(@vue/compiler-sfc@3.5.12)(vue@3.5.12(typescript@5.6.3))(webpack@5.96.1(esbuild@0.24.0)) + webpack: 5.96.1(esbuild@0.24.0) + transitivePeerDependencies: + - '@swc/core' + - '@vue/compiler-sfc' + - esbuild + - uglify-js + - vue + - webpack-cli + '@rspack/binding-darwin-arm64@1.1.1': optional: true @@ -7925,6 +7974,16 @@ snapshots: transitivePeerDependencies: - supports-color + vue-loader@17.4.2(@vue/compiler-sfc@3.5.12)(vue@3.5.12(typescript@5.6.3))(webpack@5.96.1(esbuild@0.24.0)): + dependencies: + chalk: 4.1.2 + hash-sum: 2.0.0 + watchpack: 2.4.2 + webpack: 5.96.1(esbuild@0.24.0) + optionalDependencies: + '@vue/compiler-sfc': 3.5.12 + vue: 3.5.12(typescript@5.6.3) + vue-router@4.2.5(vue@3.5.12(typescript@5.6.3)): dependencies: '@vue/devtools-api': 6.6.4 diff --git a/src/core/context.ts b/src/core/context.ts index 701e66d..d89347b 100644 --- a/src/core/context.ts +++ b/src/core/context.ts @@ -99,7 +99,7 @@ export class PageContext { // debug.pages('add', path) for (const p of toArray(path)) { const pageDirPath = slash(resolve(this.root, pageDir.dir)) - const extension = this.options.extensions.find(ext => p.endsWith(`.${ext}`)) + const extension = this.options.extensions!.find(ext => p.endsWith(`.${ext}`)) if (!extension) continue diff --git a/src/core/index.ts b/src/core/index.ts deleted file mode 100644 index 0e736a2..0000000 --- a/src/core/index.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { Plugin } from 'vite' -import type { UserOptions } from './types' -import { MODULE_ID_VIRTUAL, ROUTE_BLOCK_ID_VIRTUAL, routeBlockQueryRE } from './constants' - -import { PageContext } from './context' -import { parsePageRequest } from './utils' - -function pagesPlugin(userOptions: UserOptions = {}): Plugin { - let ctx: PageContext - - return { - name: 'unplugin-convention-routes', - enforce: 'pre', - async configResolved(config) { - // auto set resolver for react project - if ( - !userOptions.resolver - && config.plugins.find(i => i.name.includes('vite:react')) - ) { - userOptions.resolver = 'react' - } - - // auto set resolver for solid project - if ( - !userOptions.resolver - && config.plugins.find(i => i.name.includes('solid')) - ) { - userOptions.resolver = 'solid' - } - - ctx = new PageContext(userOptions, config.root) - ctx.setLogger(config.logger) - await ctx.searchGlob() - }, - api: { - getResolvedRoutes() { - return ctx.options.resolver.getComputedRoutes(ctx) - }, - }, - configureServer(server) { - ctx.setupViteServer(server) - }, - resolveId(id) { - if (ctx.options.moduleIds.includes(id)) - return `${MODULE_ID_VIRTUAL}?id=${id}` - - if (routeBlockQueryRE.test(id)) - return ROUTE_BLOCK_ID_VIRTUAL - - return null - }, - async load(id) { - const { - moduleId, - pageId, - } = parsePageRequest(id) - - if (moduleId === MODULE_ID_VIRTUAL && pageId && ctx.options.moduleIds.includes(pageId)) - return ctx.resolveRoutes() - - if (id === ROUTE_BLOCK_ID_VIRTUAL) { - return { - code: 'export default {};', - map: null, - } - } - - return null - }, - } -} - -export { syncIndexResolver } from './options' -export type { - ReactRoute, - SolidRoute, - VueRoute, -} from './resolvers' - -export { - reactResolver, - solidResolver, - vueResolver, -} from './resolvers' -export * from './types' -export { PageContext } -export default pagesPlugin diff --git a/src/core/resolvers/react.ts b/src/core/resolvers/react.ts index 4c5b9cc..ff0cfe5 100644 --- a/src/core/resolvers/react.ts +++ b/src/core/resolvers/react.ts @@ -43,7 +43,7 @@ function prepareRoutes( } async function computeReactRoutes(ctx: PageContext): Promise { - const { routeStyle, caseSensitive, importPath } = ctx.options + const { routeStyle, caseSensitive = false, importPath } = ctx.options const nuxtStyle = routeStyle === 'nuxt' const pageRoutes = [...ctx.pageRouteMap.values()] diff --git a/src/core/resolvers/solid.ts b/src/core/resolvers/solid.ts index 657a641..64796b4 100644 --- a/src/core/resolvers/solid.ts +++ b/src/core/resolvers/solid.ts @@ -42,7 +42,7 @@ function prepareRoutes( } async function computeSolidRoutes(ctx: PageContext): Promise { - const { routeStyle, caseSensitive, importPath } = ctx.options + const { routeStyle, caseSensitive = false, importPath } = ctx.options const nuxtStyle = routeStyle === 'nuxt' const pageRoutes = [...ctx.pageRouteMap.values()] diff --git a/src/core/resolvers/vue.ts b/src/core/resolvers/vue.ts index 899bd2f..9c4c0ca 100644 --- a/src/core/resolvers/vue.ts +++ b/src/core/resolvers/vue.ts @@ -61,7 +61,7 @@ function prepareRoutes( } async function computeVueRoutes(ctx: PageContext, customBlockMap: Map): Promise { - const { routeStyle, caseSensitive, importPath, routeNameSeparator } = ctx.options + const { routeStyle, caseSensitive = false, importPath, routeNameSeparator } = ctx.options const pageRoutes = [...ctx.pageRouteMap.values()] // sort routes for HMR diff --git a/src/core/types.ts b/src/core/types.ts index ce343ef..a45e8b7 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -66,63 +66,63 @@ export interface Options { * Paths to the directory to search for page components. * @default 'src/pages' */ - dirs: string | (string | PageOptions)[] + dirs?: string | (string | PageOptions)[] /** * Valid file extensions for page components. * @default ['vue', 'js'] */ - extensions: string[] + extensions?: string[] /** * List of path globs to exclude when resolving pages. */ - exclude: string[] + exclude?: string[] /** * Import routes directly or as async components * @default 'root index file => "sync", others => "async"' */ - importMode: ImportMode | ImportModeResolver + importMode?: ImportMode | ImportModeResolver /** * Import page components from absolute or relative paths. * @default 'relative' */ - importPath: 'absolute' | 'relative' + importPath?: 'absolute' | 'relative' /** * Sync load top level index file * @default true * @deprecated use `importMode` instead */ - syncIndex: boolean + syncIndex?: boolean /** * Use Nuxt.js style route naming * @default false * @deprecated use `routeStyle` instead */ - nuxtStyle: boolean + nuxtStyle?: boolean /** * Routing style * @default false */ - routeStyle: 'next' | 'nuxt' | 'remix' + routeStyle?: 'next' | 'nuxt' | 'remix' /** * Separator for generated route names. * @default - */ - routeNameSeparator: string + routeNameSeparator?: string /** * Case for route paths * @default false */ - caseSensitive: boolean + caseSensitive?: boolean /** * Set the default route block parser, or use `` in SFC route block * @default 'json5' */ - routeBlockLang: 'json5' | 'json' | 'yaml' | 'yml' + routeBlockLang?: 'json5' | 'json' | 'yaml' | 'yml' /** * Module id for routes import * @default '~pages' */ - moduleId: string + moduleId?: string /** * Generate React Route * @default 'auto detect' @@ -145,19 +145,19 @@ export interface Options { * Paths to the directory to search for page components. * @deprecated use `dirs` instead */ - pagesDir: string | (string | PageOptions)[] + pagesDir?: string | (string | PageOptions)[] /** * Replace '[]' to '_' in bundle filename * @deprecated issue #122 */ - replaceSquareBrackets: never + replaceSquareBrackets?: never /** * @name watcher */ watcher?: boolean } -export type UserOptions = Partial +export type UserOptions = Options export interface ResolvedOptions extends Omit { /** diff --git a/src/index.ts b/src/index.ts index 1ab380c..231f7f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,9 @@ import process from 'node:process' import { createUnplugin } from 'unplugin' import { PageContext } from './core/context' -export const unpluginFactory: UnpluginFactory = (userOptions: UserOptions = {}) => { +export const unpluginFactory: UnpluginFactory = (userOptions: UserOptions = { + resolver: 'vue', +}) => { const ctx: PageContext = new PageContext(userOptions) // ctx.setLogger(config.logger) return ({ From 66cc87928ff030e6364b44fee02dd133bb1e72bc Mon Sep 17 00:00:00 2001 From: liujiayii Date: Thu, 14 Nov 2024 10:00:45 +0800 Subject: [PATCH 2/2] feat: fix types --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 904ce1d..2016df0 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,4 @@ pnpm i unplugin-convention-routes ``` ## ๐Ÿ”จ ็คบไพ‹ +- https://github.com/liujiayii/unplugin-convention-routes/tree/main/examples