vite-plugin-generoutes 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -135
- package/dist/index.d.ts +18 -2
- package/dist/index.js +62 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,135 +1,135 @@
|
|
|
1
|
-
# vite-plugin-generoutes
|
|
2
|
-
|
|
3
|
-
A Vite plugin that generates routes based on the file structure, supports dynamic routes, and supports custom meta data for each route.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
1. Install the plugin:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install vite-plugin-generoutes
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
2. Add the plugin to your `vite.config.js`:
|
|
14
|
-
|
|
15
|
-
```js
|
|
16
|
-
import { defineConfig } from 'vite'
|
|
17
|
-
import generoutes from 'vite-plugin-generoutes'
|
|
18
|
-
|
|
19
|
-
export default defineConfig({
|
|
20
|
-
plugins: [
|
|
21
|
-
generoutes()
|
|
22
|
-
// ... other plugins
|
|
23
|
-
]
|
|
24
|
-
})
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
3. Create your page files(`index.vue` or `[...all].vue`) in the `src/pages` directory. Each file in this directory will be treated as a route.
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
src/routes/pages
|
|
31
|
-
├── index.vue
|
|
32
|
-
├── [...all].vue
|
|
33
|
-
├── user/
|
|
34
|
-
│ ├── index.vue
|
|
35
|
-
│ ├── [id]
|
|
36
|
-
│ │ └── index.vue
|
|
37
|
-
└── post
|
|
38
|
-
├── index.vue
|
|
39
|
-
└── [...all].vue
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
The above example will generate the routes file `src/router/generoutes/index.js` with the following content:
|
|
43
|
-
```js
|
|
44
|
-
export const routes = [
|
|
45
|
-
{
|
|
46
|
-
name: 'Index',
|
|
47
|
-
path: '/',
|
|
48
|
-
component: () => import('/src/pages/index.vue'),
|
|
49
|
-
meta: {
|
|
50
|
-
title: 'Index',
|
|
51
|
-
show: true,
|
|
52
|
-
keepAlive: false,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: 'User',
|
|
57
|
-
path: '/user',
|
|
58
|
-
component: () => import('/src/pages/user/index.vue'),
|
|
59
|
-
meta: {
|
|
60
|
-
title: 'User',
|
|
61
|
-
show: true,
|
|
62
|
-
keepAlive: false,
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'User_[id]',
|
|
67
|
-
path: '/user/:id',
|
|
68
|
-
component: () => import('/src/pages/user/[id]/index.vue'),
|
|
69
|
-
meta: {
|
|
70
|
-
title: 'User_[id]',
|
|
71
|
-
show: true,
|
|
72
|
-
keepAlive: false,
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'User_Post',
|
|
77
|
-
path: '/user/post',
|
|
78
|
-
component: () => import('/src/pages/user/post/index.vue'),
|
|
79
|
-
meta: {
|
|
80
|
-
title: 'User_Post',
|
|
81
|
-
show: true,
|
|
82
|
-
keepAlive: false,
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: 'Index_[...all]',
|
|
87
|
-
path: '/:pathMatch(.*)*',
|
|
88
|
-
component: () => import('/src/pages/[...all].vue'),
|
|
89
|
-
meta: {
|
|
90
|
-
title: '404 Not Found',
|
|
91
|
-
show: true,
|
|
92
|
-
keepAlive: false,
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: 'User_Post_[...all]',
|
|
97
|
-
path: '/user/post/:pathMatch(.*)*',
|
|
98
|
-
component: () => import('/src/pages/user/post/[...all].vue'),
|
|
99
|
-
meta: {
|
|
100
|
-
title: 'User_Post_[...all]',
|
|
101
|
-
show: true,
|
|
102
|
-
keepAlive: false,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
]
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
4. Import the generated routes and create a router instance:
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
import { createRouter, createWebHashHistory } from 'vue-router'
|
|
112
|
-
import { routes } from './generoutes'
|
|
113
|
-
|
|
114
|
-
const router = createRouter({
|
|
115
|
-
history: createWebHashHistory(),
|
|
116
|
-
routes,
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
export default router
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Features
|
|
123
|
-
|
|
124
|
-
- Generate routes based on the file structure.
|
|
125
|
-
- Support dynamic routes.
|
|
126
|
-
- Support multiple NotFound routes.
|
|
127
|
-
- Support custom meta data for each route.
|
|
128
|
-
- Support ghost paths, For example, the (admin) folder will not be part of the route path, which is very useful for folder grouping.
|
|
129
|
-
- Support immediate update of the routes file when the file structure or defineOptions changes.
|
|
130
|
-
|
|
131
|
-
## Custom route info,including name and meta
|
|
132
|
-
|
|
133
|
-
You can define `name` and `meta` fields in the `defineOptions` of your `.vue` file, which will be used to override the default properties of the generated route. The `name` field will be used as the route name, which is very useful for `KeepAlive`. Any property in `defineOptions.meta` will be used as a property of the route `meta`, which makes the route metadata very flexible.
|
|
134
|
-
|
|
135
|
-
When you make any changes that may affect the route result, the `src/router/generoutes/index.js` file will be updated immediately, and the page will be refreshed without restarting the server.
|
|
1
|
+
# vite-plugin-generoutes
|
|
2
|
+
|
|
3
|
+
A Vite plugin that generates routes based on the file structure, supports dynamic routes, and supports custom meta data for each route.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
1. Install the plugin:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install vite-plugin-generoutes
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. Add the plugin to your `vite.config.js`:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { defineConfig } from 'vite'
|
|
17
|
+
import generoutes from 'vite-plugin-generoutes'
|
|
18
|
+
|
|
19
|
+
export default defineConfig({
|
|
20
|
+
plugins: [
|
|
21
|
+
generoutes()
|
|
22
|
+
// ... other plugins
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
3. Create your page files(`index.vue` or `[...all].vue`) in the `src/pages` directory. Each file in this directory will be treated as a route.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
src/routes/pages
|
|
31
|
+
├── index.vue
|
|
32
|
+
├── [...all].vue
|
|
33
|
+
├── user/
|
|
34
|
+
│ ├── index.vue
|
|
35
|
+
│ ├── [id]
|
|
36
|
+
│ │ └── index.vue
|
|
37
|
+
└── post
|
|
38
|
+
├── index.vue
|
|
39
|
+
└── [...all].vue
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The above example will generate the routes file `src/router/generoutes/index.js` with the following content:
|
|
43
|
+
```js
|
|
44
|
+
export const routes = [
|
|
45
|
+
{
|
|
46
|
+
name: 'Index',
|
|
47
|
+
path: '/',
|
|
48
|
+
component: () => import('/src/pages/index.vue'),
|
|
49
|
+
meta: {
|
|
50
|
+
title: 'Index',
|
|
51
|
+
show: true,
|
|
52
|
+
keepAlive: false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'User',
|
|
57
|
+
path: '/user',
|
|
58
|
+
component: () => import('/src/pages/user/index.vue'),
|
|
59
|
+
meta: {
|
|
60
|
+
title: 'User',
|
|
61
|
+
show: true,
|
|
62
|
+
keepAlive: false,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'User_[id]',
|
|
67
|
+
path: '/user/:id',
|
|
68
|
+
component: () => import('/src/pages/user/[id]/index.vue'),
|
|
69
|
+
meta: {
|
|
70
|
+
title: 'User_[id]',
|
|
71
|
+
show: true,
|
|
72
|
+
keepAlive: false,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'User_Post',
|
|
77
|
+
path: '/user/post',
|
|
78
|
+
component: () => import('/src/pages/user/post/index.vue'),
|
|
79
|
+
meta: {
|
|
80
|
+
title: 'User_Post',
|
|
81
|
+
show: true,
|
|
82
|
+
keepAlive: false,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'Index_[...all]',
|
|
87
|
+
path: '/:pathMatch(.*)*',
|
|
88
|
+
component: () => import('/src/pages/[...all].vue'),
|
|
89
|
+
meta: {
|
|
90
|
+
title: '404 Not Found',
|
|
91
|
+
show: true,
|
|
92
|
+
keepAlive: false,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'User_Post_[...all]',
|
|
97
|
+
path: '/user/post/:pathMatch(.*)*',
|
|
98
|
+
component: () => import('/src/pages/user/post/[...all].vue'),
|
|
99
|
+
meta: {
|
|
100
|
+
title: 'User_Post_[...all]',
|
|
101
|
+
show: true,
|
|
102
|
+
keepAlive: false,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
4. Import the generated routes and create a router instance:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
import { createRouter, createWebHashHistory } from 'vue-router'
|
|
112
|
+
import { routes } from './generoutes'
|
|
113
|
+
|
|
114
|
+
const router = createRouter({
|
|
115
|
+
history: createWebHashHistory(),
|
|
116
|
+
routes,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
export default router
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Features
|
|
123
|
+
|
|
124
|
+
- Generate routes based on the file structure.
|
|
125
|
+
- Support dynamic routes.
|
|
126
|
+
- Support multiple NotFound routes.
|
|
127
|
+
- Support custom meta data for each route.
|
|
128
|
+
- Support ghost paths, For example, the (admin) folder will not be part of the route path, which is very useful for folder grouping.
|
|
129
|
+
- Support immediate update of the routes file when the file structure or defineOptions changes.
|
|
130
|
+
|
|
131
|
+
## Custom route info,including name and meta
|
|
132
|
+
|
|
133
|
+
You can define `name` and `meta` fields in the `defineOptions` of your `.vue` file, which will be used to override the default properties of the generated route. The `name` field will be used as the route name, which is very useful for `KeepAlive`. Any property in `defineOptions.meta` will be used as a property of the route `meta`, which makes the route metadata very flexible.
|
|
134
|
+
|
|
135
|
+
When you make any changes that may affect the route result, the `src/router/generoutes/index.js` file will be updated immediately, and the page will be refreshed without restarting the server.
|
package/dist/index.d.ts
CHANGED
|
@@ -9,9 +9,25 @@ import { Plugin } from 'vite';
|
|
|
9
9
|
**********************************/
|
|
10
10
|
|
|
11
11
|
interface Options {
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* pages 文件夹
|
|
14
|
+
*
|
|
15
|
+
* default: `src/pages`
|
|
16
|
+
*/
|
|
13
17
|
pagesFolder: string;
|
|
18
|
+
/**
|
|
19
|
+
* 路由文件路径
|
|
20
|
+
*
|
|
21
|
+
* default: `${pagesFolder}/generoutes.js`
|
|
22
|
+
*/
|
|
23
|
+
routesPath?: string;
|
|
24
|
+
/**
|
|
25
|
+
* 是否嵌套
|
|
26
|
+
*
|
|
27
|
+
* default: false
|
|
28
|
+
*/
|
|
29
|
+
nested: boolean;
|
|
14
30
|
}
|
|
15
|
-
declare function VitePluginGeneroutes(options?: Options): Plugin<any>;
|
|
31
|
+
declare function VitePluginGeneroutes(options?: Partial<Options>): Plugin<any>;
|
|
16
32
|
|
|
17
33
|
export { VitePluginGeneroutes as default };
|
package/dist/index.js
CHANGED
|
@@ -6,47 +6,64 @@ import { parse } from "@vue/compiler-sfc";
|
|
|
6
6
|
import { debounce, slash } from "@antfu/utils";
|
|
7
7
|
import chokidar from "chokidar";
|
|
8
8
|
import prettier from "prettier";
|
|
9
|
+
|
|
10
|
+
// src/utils.ts
|
|
9
11
|
function toPascalCase(str) {
|
|
10
12
|
return str.toLowerCase().replace(/(?:^|-)([a-z])/g, (_, p1) => {
|
|
11
13
|
return p1.toUpperCase();
|
|
12
14
|
});
|
|
13
15
|
}
|
|
16
|
+
function convertToTree(routes) {
|
|
17
|
+
const nodeMap = {};
|
|
18
|
+
const result = [];
|
|
19
|
+
routes.forEach((route) => {
|
|
20
|
+
const { parent, ...node } = route;
|
|
21
|
+
nodeMap[node.name] = node;
|
|
22
|
+
});
|
|
23
|
+
routes.forEach((route) => {
|
|
24
|
+
if (route.parent) {
|
|
25
|
+
const parentNode = nodeMap[route.parent];
|
|
26
|
+
if (parentNode) {
|
|
27
|
+
if (!parentNode.children) {
|
|
28
|
+
parentNode.children = [];
|
|
29
|
+
}
|
|
30
|
+
parentNode.children.push(nodeMap[route.name]);
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
result.push(nodeMap[route.name]);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/index.ts
|
|
14
40
|
var defaultOptions = {
|
|
15
|
-
|
|
16
|
-
|
|
41
|
+
pagesFolder: "src/pages",
|
|
42
|
+
nested: false
|
|
17
43
|
};
|
|
18
|
-
function VitePluginGeneroutes(options =
|
|
19
|
-
|
|
44
|
+
function VitePluginGeneroutes(options = {}) {
|
|
45
|
+
if (options.routesPath && ![".ts", ".js"].includes(path.extname(options.routesPath)))
|
|
46
|
+
throw new Error("routesPath must be a js or ts file path, such as src/router/generoutes.js");
|
|
20
47
|
let rootDir;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// ! \u6B64\u6587\u4EF6\u7531 vite-plugin-generoutes \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u4FEE\u6539\uFF0C\u8BF7\u52FF\u4FEE\u6539\uFF0C\u8BF7\u52FF\u4FEE\u6539!!!
|
|
26
|
-
|
|
27
|
-
export const routes = ${JSON.stringify(routes, null, 2)}
|
|
28
|
-
`;
|
|
29
|
-
routesStr = routesStr.replace(/"##(.*)##"/g, (_, p1) => `() => import('${p1}')`);
|
|
30
|
-
routesStr = await prettier.format(routesStr, { parser: "babel", semi: false, singleQuote: true });
|
|
31
|
-
fs.writeFileSync(`${routesFolder}/index.js`, routesStr);
|
|
32
|
-
}
|
|
33
|
-
const debounceWriter = debounce(500, writerRoutesFile);
|
|
48
|
+
const pagesFolder = options.pagesFolder || defaultOptions.pagesFolder;
|
|
49
|
+
const routesPath = options.routesPath || path.join(pagesFolder, "generoutes.js");
|
|
50
|
+
const nested = options.nested || defaultOptions.nested;
|
|
51
|
+
const defineOptionsCache = /* @__PURE__ */ new Map();
|
|
34
52
|
function generateMenusAndRoutes() {
|
|
35
|
-
const pages = globSync(`${
|
|
53
|
+
const pages = globSync(`${pagesFolder}/**/index.vue`).concat(globSync(`${pagesFolder}/**/\\[...all\\].vue`));
|
|
36
54
|
const routes = pages.map((filePath) => {
|
|
37
55
|
filePath = slash(filePath);
|
|
38
|
-
const defineOptions = parseDefineOptions(filePath);
|
|
56
|
+
const defineOptions = parseDefineOptions(filePath) || {};
|
|
39
57
|
defineOptionsCache.set(filePath, JSON.stringify(defineOptions));
|
|
40
58
|
const meta = defineOptions?.meta || {};
|
|
41
59
|
if (meta.enabled === false)
|
|
42
60
|
return null;
|
|
43
61
|
const fileName = path.basename(filePath);
|
|
44
|
-
const
|
|
45
|
-
const pathSegments = filePath.replace(`${options.pagesFolder}/`, "").replace(fileName, "").split("/").filter((item) => !!item && !/^\(.*\)$/.test(item));
|
|
62
|
+
const pathSegments = path.dirname(filePath).replace(`${pagesFolder}`, "").split("/").filter((item) => !!item && !/^\(.*\)$/.test(item));
|
|
46
63
|
let name = defineOptions?.name || pathSegments.map((item) => toPascalCase(item)).join("_") || "Index";
|
|
47
64
|
const component = `##/${filePath}##`;
|
|
48
65
|
let routePath = `/${pathSegments.map((item) => item.replace(/\[(.*?)\]/g, (_, p1) => p1.split(",").map((i) => `:${i}`).join("/"))).join("/")}`;
|
|
49
|
-
if (
|
|
66
|
+
if (fileName === "[...all].vue") {
|
|
50
67
|
name = `${name}_[...all]`;
|
|
51
68
|
routePath = routePath === "/" ? "/:pathMatch(.*)*" : `${routePath}/:pathMatch(.*)*`;
|
|
52
69
|
}
|
|
@@ -59,15 +76,31 @@ function VitePluginGeneroutes(options = defaultOptions) {
|
|
|
59
76
|
name,
|
|
60
77
|
path: routePath,
|
|
61
78
|
component,
|
|
62
|
-
meta
|
|
79
|
+
meta,
|
|
80
|
+
parent: defineOptions?.parent
|
|
63
81
|
};
|
|
64
82
|
}).filter(Boolean);
|
|
65
83
|
return {
|
|
66
|
-
routes
|
|
84
|
+
routes: nested ? convertToTree(routes) : routes
|
|
67
85
|
};
|
|
68
86
|
}
|
|
87
|
+
async function writerRoutesFile() {
|
|
88
|
+
const { routes } = generateMenusAndRoutes();
|
|
89
|
+
let routesStr = `
|
|
90
|
+
// ! \u6B64\u6587\u4EF6\u7531 vite-plugin-generoutes \u81EA\u52A8\u751F\u6210\uFF0C\u5EFA\u8BAE\u6DFB\u52A0\u5230 .gitignore\uFF0C\u8BF7\u52FF\u76F4\u63A5\u5728\u6B64\u6587\u4EF6\u4FEE\u6539!!!
|
|
91
|
+
// ! This file is generated by vite-plugin-generoutes, it is recommended to add it to .gitignore, do not modify it directly in this file!!!
|
|
92
|
+
|
|
93
|
+
export const routes = ${JSON.stringify(routes, null, 2)}
|
|
94
|
+
`;
|
|
95
|
+
routesStr = routesStr.replace(/"##(.*)##"/g, (_, p1) => `() => import('${p1}')`);
|
|
96
|
+
routesStr = await prettier.format(routesStr, { parser: "babel", semi: false, singleQuote: true });
|
|
97
|
+
const filePath = path.resolve(rootDir, routesPath);
|
|
98
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
99
|
+
fs.writeFileSync(filePath, routesStr);
|
|
100
|
+
}
|
|
101
|
+
const debounceWriter = debounce(500, writerRoutesFile);
|
|
69
102
|
function createWatcher() {
|
|
70
|
-
const watcher = chokidar.watch([
|
|
103
|
+
const watcher = chokidar.watch([`${pagesFolder}/**/index.vue`, `${pagesFolder}/**/[...all].vue`], { ignoreInitial: true });
|
|
71
104
|
return watcher.on("all", async (event, path2) => {
|
|
72
105
|
if ((path2.endsWith("index.vue") || path2.endsWith("[...all].vue")) && (event === "add" || event === "unlink")) {
|
|
73
106
|
debounceWriter();
|
|
@@ -80,13 +113,11 @@ function VitePluginGeneroutes(options = defaultOptions) {
|
|
|
80
113
|
name: "vite-plugin-generoutes",
|
|
81
114
|
async configResolved(config) {
|
|
82
115
|
rootDir = config.root;
|
|
83
|
-
routesFolder = slash(path.resolve(rootDir, routesFolder));
|
|
84
|
-
await fs.ensureDir(routesFolder);
|
|
85
116
|
await writerRoutesFile();
|
|
86
117
|
config.command !== "build" && createWatcher();
|
|
87
118
|
},
|
|
88
119
|
async handleHotUpdate({ file, read }) {
|
|
89
|
-
if (file.includes(
|
|
120
|
+
if (file.includes(pagesFolder) && (file.endsWith("index.vue") || file.endsWith("[...all].vue"))) {
|
|
90
121
|
const prevDefineOptions = defineOptionsCache.get(slash(path.relative(rootDir, file)));
|
|
91
122
|
if (!prevDefineOptions) {
|
|
92
123
|
debounceWriter();
|
|
@@ -105,16 +136,16 @@ function parseDefineOptions(filePath, content) {
|
|
|
105
136
|
const { descriptor } = parse(content);
|
|
106
137
|
const setupScript = descriptor.scriptSetup?.content;
|
|
107
138
|
if (setupScript) {
|
|
108
|
-
const defineOptionsMatch = setupScript.match(/defineOptions\(([
|
|
139
|
+
const defineOptionsMatch = setupScript.match(/defineOptions\s*\(\s*(\{[\s\S]*?\})\s*\)/);
|
|
109
140
|
if (defineOptionsMatch) {
|
|
110
141
|
try {
|
|
111
142
|
return new Function(`return ${defineOptionsMatch[1]}`)();
|
|
112
143
|
} catch (e) {
|
|
113
|
-
|
|
144
|
+
throw new Error(`Failed to parse defineOptions in ${filePath}: ${e}`);
|
|
114
145
|
}
|
|
115
146
|
}
|
|
116
147
|
}
|
|
117
|
-
return
|
|
148
|
+
return {};
|
|
118
149
|
}
|
|
119
150
|
var src_default = VitePluginGeneroutes;
|
|
120
151
|
export {
|