sv-router 0.0.4 → 0.0.5
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/package.json +2 -2
- package/src/RecursiveComponentTree.svelte +2 -2
- package/src/Router.svelte +1 -1
- package/src/cli/index.js +17 -3
- package/src/create-router.svelte.js +59 -16
- package/src/gen/config.js +5 -1
- package/src/gen/generate-router-code.js +30 -6
- package/src/helpers/match-route.js +27 -23
- package/src/helpers/preload-on-hover.js +28 -0
- package/src/index.d.ts +65 -6
- package/src/vite-plugin/index.d.ts +6 -0
- package/src/vite-plugin/plugin.js +2 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sv-router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Modern Svelte routing",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"svelte": "^5"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
|
-
"ex:
|
|
60
|
+
"ex:config-based": "pnpm --filter config-based-example",
|
|
61
61
|
"ex:file-based": "pnpm --filter file-based-example",
|
|
62
62
|
"test": "vitest",
|
|
63
63
|
"check": "tsc --noEmit && pnpm -r check",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import {
|
|
2
|
+
import { params } from './create-router.svelte.js';
|
|
3
3
|
import RecursiveComponentTree from './RecursiveComponentTree.svelte';
|
|
4
4
|
|
|
5
5
|
/** @type {{ tree: import('svelte').Component[] }} */
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
const restTree = $derived(tree.slice(1));
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
|
-
{#key restTree.length > 0 || Object.values(
|
|
12
|
+
{#key restTree.length > 0 || Object.values(params.value)}
|
|
13
13
|
<FirstComponent>
|
|
14
14
|
{#if restTree.length > 0}
|
|
15
15
|
<RecursiveComponentTree tree={restTree}></RecursiveComponentTree>
|
package/src/Router.svelte
CHANGED
package/src/cli/index.js
CHANGED
|
@@ -5,9 +5,23 @@ import { writeRouterCode } from '../gen/write-router-code.js';
|
|
|
5
5
|
|
|
6
6
|
const args = process.argv.slice(2).flatMap((arg) => arg.split('='));
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* @param {keyof import('../vite-plugin/index.d.ts').RouterOptions} option
|
|
10
|
+
* @returns
|
|
11
|
+
*/
|
|
12
|
+
function arg(option) {
|
|
13
|
+
const pathArgIndex = args.indexOf('--' + option);
|
|
14
|
+
if (pathArgIndex === -1) return;
|
|
15
|
+
if (pathArgIndex + 1 < args.length) {
|
|
16
|
+
return args[pathArgIndex + 1];
|
|
17
|
+
}
|
|
18
|
+
return args[pathArgIndex];
|
|
11
19
|
}
|
|
12
20
|
|
|
21
|
+
const pathArg = arg('path');
|
|
22
|
+
if (pathArg) genConfig.routesPath = pathArg;
|
|
23
|
+
|
|
24
|
+
const jsArg = arg('js');
|
|
25
|
+
if (jsArg) genConfig.routesInJs = true;
|
|
26
|
+
|
|
13
27
|
writeRouterCode();
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { BROWSER, DEV } from 'esm-env';
|
|
2
2
|
import { matchRoute } from './helpers/match-route.js';
|
|
3
|
+
import { preloadOnHover } from './helpers/preload-on-hover.js';
|
|
3
4
|
import { constructPath, resolveRouteComponents } from './helpers/utils.js';
|
|
4
5
|
|
|
5
6
|
/** @type {import('./index.d.ts').Routes} */
|
|
6
7
|
export let routes;
|
|
7
8
|
|
|
8
|
-
/** @type {import('svelte').Component[]} */
|
|
9
|
-
export
|
|
9
|
+
/** @type {{ value: import('svelte').Component[] }} */
|
|
10
|
+
export let componentTree = $state({ value: [] });
|
|
10
11
|
|
|
11
|
-
/** @type {Record<string, string>} */
|
|
12
|
-
export
|
|
12
|
+
/** @type {{ value: Record<string, string> }} */
|
|
13
|
+
export let params = $state({ value: {} });
|
|
14
|
+
|
|
15
|
+
let location = $state(updatedLocation());
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* @template {import('./index.d.ts').Routes} T
|
|
16
19
|
* @param {T} r
|
|
17
|
-
* @returns {import('./index.d.ts').
|
|
20
|
+
* @returns {import('./index.d.ts').RouterApi<T>}
|
|
18
21
|
*/
|
|
19
22
|
export function createRouter(r) {
|
|
20
23
|
routes = r;
|
|
@@ -25,16 +28,44 @@ export function createRouter(r) {
|
|
|
25
28
|
});
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
preloadOnHover(routes);
|
|
32
|
+
|
|
28
33
|
return {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
p: constructPath,
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} path
|
|
37
|
+
* @param {import('./index.d.ts').NavigateOptions & { params?: Record<string, string> }} options
|
|
38
|
+
*/
|
|
39
|
+
navigate(path, options = {}) {
|
|
40
|
+
if (options.params) {
|
|
41
|
+
path = constructPath(path, options.params);
|
|
42
|
+
}
|
|
43
|
+
if (options.search) {
|
|
44
|
+
path += options.search;
|
|
45
|
+
}
|
|
46
|
+
if (options.hash) {
|
|
47
|
+
path += options.hash;
|
|
48
|
+
}
|
|
49
|
+
const historyMethod = options.replace ? 'replaceState' : 'pushState';
|
|
50
|
+
globalThis.history[historyMethod](options.state || {}, '', path);
|
|
33
51
|
onNavigate();
|
|
34
52
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
route: {
|
|
54
|
+
get params() {
|
|
55
|
+
return params.value;
|
|
56
|
+
},
|
|
57
|
+
get pathname() {
|
|
58
|
+
return location.pathname;
|
|
59
|
+
},
|
|
60
|
+
get search() {
|
|
61
|
+
return location.search;
|
|
62
|
+
},
|
|
63
|
+
get state() {
|
|
64
|
+
return location.state;
|
|
65
|
+
},
|
|
66
|
+
get hash() {
|
|
67
|
+
return location.hash;
|
|
68
|
+
},
|
|
38
69
|
},
|
|
39
70
|
};
|
|
40
71
|
}
|
|
@@ -43,11 +74,12 @@ export function onNavigate() {
|
|
|
43
74
|
if (!routes) {
|
|
44
75
|
throw new Error('Router not initialized: `createRouter` was not called.');
|
|
45
76
|
}
|
|
46
|
-
|
|
77
|
+
location = updatedLocation();
|
|
78
|
+
const { match, layouts, params: newParams } = matchRoute(globalThis.location.pathname, routes);
|
|
79
|
+
params.value = newParams || {};
|
|
47
80
|
resolveRouteComponents(match ? [...layouts, match] : layouts).then((components) => {
|
|
48
|
-
|
|
81
|
+
componentTree.value = components;
|
|
49
82
|
});
|
|
50
|
-
Object.assign(paramsStore, params);
|
|
51
83
|
}
|
|
52
84
|
|
|
53
85
|
/** @param {Event} event */
|
|
@@ -62,6 +94,17 @@ export function onGlobalClick(event) {
|
|
|
62
94
|
if (url.origin !== currentOrigin) return;
|
|
63
95
|
|
|
64
96
|
event.preventDefault();
|
|
65
|
-
|
|
97
|
+
const { replace, state } = anchor.dataset;
|
|
98
|
+
const historyMethod = replace === undefined || replace === 'false' ? 'pushState' : 'replaceState';
|
|
99
|
+
globalThis.history[historyMethod](state || {}, '', anchor.href);
|
|
66
100
|
onNavigate();
|
|
67
101
|
}
|
|
102
|
+
|
|
103
|
+
function updatedLocation() {
|
|
104
|
+
return {
|
|
105
|
+
pathname: globalThis.location.pathname,
|
|
106
|
+
search: globalThis.location.search,
|
|
107
|
+
state: globalThis.history.state,
|
|
108
|
+
hash: globalThis.location.hash,
|
|
109
|
+
};
|
|
110
|
+
}
|
package/src/gen/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @type {{
|
|
3
3
|
* routesPath: string;
|
|
4
|
+
* routesInJs: boolean;
|
|
4
5
|
* readonly genCodeDirPath: string;
|
|
5
6
|
* readonly routerPath: string;
|
|
6
7
|
* readonly tsconfigPath: string;
|
|
@@ -9,8 +10,11 @@
|
|
|
9
10
|
*/
|
|
10
11
|
export const genConfig = {
|
|
11
12
|
routesPath: 'src/routes',
|
|
13
|
+
routesInJs: false,
|
|
12
14
|
genCodeDirPath: '.router',
|
|
13
|
-
routerPath
|
|
15
|
+
get routerPath() {
|
|
16
|
+
return '.router/router.' + (this.routesInJs ? 'js' : 'ts');
|
|
17
|
+
},
|
|
14
18
|
tsconfigPath: '.router/tsconfig.json',
|
|
15
19
|
genCodeAlias: 'sv-router/generated',
|
|
16
20
|
};
|
|
@@ -28,16 +28,40 @@ export function generateRouterCode(routesPath) {
|
|
|
28
28
|
export function buildFileTree(routesPath) {
|
|
29
29
|
const entries = fs.readdirSync(routesPath);
|
|
30
30
|
/** @type {FileTree} */
|
|
31
|
-
const
|
|
31
|
+
const tree = [];
|
|
32
32
|
for (const entry of entries) {
|
|
33
33
|
const stat = fs.lstatSync(path.join(routesPath, entry));
|
|
34
34
|
if (stat.isDirectory()) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
result.push(entry);
|
|
35
|
+
tree.push({ name: entry, tree: buildFileTree(path.join(routesPath, entry)) });
|
|
36
|
+
continue;
|
|
38
37
|
}
|
|
38
|
+
if (!entry.endsWith('.svelte')) continue;
|
|
39
|
+
handleFlatFilename(tree, entry);
|
|
39
40
|
}
|
|
40
|
-
return
|
|
41
|
+
return tree;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {FileTree} tree
|
|
46
|
+
* @param {string} path
|
|
47
|
+
*/
|
|
48
|
+
function handleFlatFilename(tree, path) {
|
|
49
|
+
const splited = path.split('.');
|
|
50
|
+
if (splited.length === 2) {
|
|
51
|
+
tree.push(path);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const first = /** @type {string} */ (splited.shift());
|
|
55
|
+
for (const item of tree) {
|
|
56
|
+
if (typeof item === 'object' && item.name === first) {
|
|
57
|
+
handleFlatFilename(item.tree, splited.join('.'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** @type {FileTree} */
|
|
62
|
+
const branch = [];
|
|
63
|
+
handleFlatFilename(branch, splited.join('.'));
|
|
64
|
+
tree.push({ name: first, tree: branch });
|
|
41
65
|
}
|
|
42
66
|
|
|
43
67
|
/**
|
|
@@ -97,6 +121,6 @@ export function createRouterCode(routes, routesPath) {
|
|
|
97
121
|
return [
|
|
98
122
|
'import { createRouter } from "sv-router";',
|
|
99
123
|
'\n\n',
|
|
100
|
-
`export const {
|
|
124
|
+
`export const { p, navigate, route } = createRouter(${withImports});`,
|
|
101
125
|
].join('');
|
|
102
126
|
}
|
|
@@ -21,8 +21,8 @@ export function matchRoute(pathname, routes) {
|
|
|
21
21
|
if (pathname.length > 1 && pathname.endsWith('/')) {
|
|
22
22
|
pathname = pathname.slice(0, -1);
|
|
23
23
|
}
|
|
24
|
-
const pathParts = pathname.split('/');
|
|
25
|
-
const
|
|
24
|
+
const pathParts = pathname.split('/').slice(1);
|
|
25
|
+
const allRoutes = sortRoutes(Object.keys(routes));
|
|
26
26
|
|
|
27
27
|
/** @type {RouteComponent | undefined} */
|
|
28
28
|
let match;
|
|
@@ -35,8 +35,11 @@ export function matchRoute(pathname, routes) {
|
|
|
35
35
|
|
|
36
36
|
let breakFromLayouts = false;
|
|
37
37
|
|
|
38
|
-
outer: for (const
|
|
39
|
-
|
|
38
|
+
outer: for (const route of allRoutes) {
|
|
39
|
+
const routeParts = route.split('/');
|
|
40
|
+
if (routeParts[0] === '') routeParts.shift();
|
|
41
|
+
|
|
42
|
+
for (let [index, routePart] of routeParts.entries()) {
|
|
40
43
|
breakFromLayouts = routePart.startsWith('(') && routePart.endsWith(')');
|
|
41
44
|
if (breakFromLayouts) {
|
|
42
45
|
routePart = routePart.slice(1, -1);
|
|
@@ -46,9 +49,10 @@ export function matchRoute(pathname, routes) {
|
|
|
46
49
|
if (routePart.startsWith(':')) {
|
|
47
50
|
params[routePart.slice(1)] = pathPart;
|
|
48
51
|
} else if (routePart === '*') {
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
const resolvedPath = /** @type {keyof Routes} */ (
|
|
53
|
+
(index ? '/' : '') + routeParts.join('/')
|
|
51
54
|
);
|
|
55
|
+
match = /** @type {RouteComponent} */ (routes[resolvedPath]);
|
|
52
56
|
break outer;
|
|
53
57
|
} else if (routePart !== pathPart) {
|
|
54
58
|
break;
|
|
@@ -58,31 +62,31 @@ export function matchRoute(pathname, routes) {
|
|
|
58
62
|
continue;
|
|
59
63
|
}
|
|
60
64
|
|
|
65
|
+
const routeMatch = /** @type {RouteComponent} */ (
|
|
66
|
+
routes[/** @type {keyof Routes} */ ('/' + routeParts.join('/'))]
|
|
67
|
+
);
|
|
68
|
+
|
|
61
69
|
if (!breakFromLayouts && 'layout' in routes && routes.layout) {
|
|
62
70
|
layouts.push(routes.layout);
|
|
63
71
|
}
|
|
64
72
|
|
|
65
|
-
const routeMatch = /** @type {RouteComponent} */ (
|
|
66
|
-
routes[/** @type {keyof Routes} */ (routeParts.join('/'))]
|
|
67
|
-
);
|
|
68
|
-
|
|
69
73
|
if (typeof routeMatch === 'function') {
|
|
70
74
|
if (routeParts.length === pathParts.length) {
|
|
71
75
|
match = routeMatch;
|
|
72
|
-
|
|
73
|
-
continue;
|
|
76
|
+
break outer;
|
|
74
77
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const nestedPathname = '/' + pathParts.slice(index + 1).join('/');
|
|
82
|
+
const result = matchRoute(nestedPathname, routeMatch);
|
|
83
|
+
if (result) {
|
|
84
|
+
match = result.match;
|
|
85
|
+
params = { ...params, ...result.params };
|
|
86
|
+
if (result.breakFromLayouts) {
|
|
87
|
+
layouts = [];
|
|
88
|
+
} else {
|
|
89
|
+
layouts.push(...result.layouts);
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
break outer;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { matchRoute } from './match-route.js';
|
|
2
|
+
import { resolveRouteComponents } from './utils.js';
|
|
3
|
+
|
|
4
|
+
const linkSet = new Set();
|
|
5
|
+
|
|
6
|
+
/** @param {import('../index.d.ts').Routes} routes */
|
|
7
|
+
export function preloadOnHover(routes) {
|
|
8
|
+
const observer = new MutationObserver(() => {
|
|
9
|
+
const links = document.querySelectorAll('a[data-preload]');
|
|
10
|
+
for (const link of links) {
|
|
11
|
+
if (linkSet.has(link)) continue;
|
|
12
|
+
linkSet.add(link);
|
|
13
|
+
|
|
14
|
+
link.addEventListener('mouseenter', function callback() {
|
|
15
|
+
link.removeEventListener('mouseenter', callback);
|
|
16
|
+
const href = link.getAttribute('href');
|
|
17
|
+
if (!href) return;
|
|
18
|
+
const { match, layouts } = matchRoute(href, routes);
|
|
19
|
+
resolveRouteComponents(match ? [...layouts, match] : layouts);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
observer.observe(document.body, {
|
|
25
|
+
subtree: true,
|
|
26
|
+
childList: true,
|
|
27
|
+
});
|
|
28
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -4,14 +4,14 @@ import type { Component, Snippet } from 'svelte';
|
|
|
4
4
|
* Setup a new router instance with the given routes.
|
|
5
5
|
*
|
|
6
6
|
* ```js
|
|
7
|
-
* export const {
|
|
7
|
+
* export const { p, navigate, route } = createRouter({
|
|
8
8
|
* '/': Home,
|
|
9
9
|
* '/about': About,
|
|
10
10
|
* ...
|
|
11
11
|
* });
|
|
12
12
|
* ```
|
|
13
13
|
*/
|
|
14
|
-
export function createRouter<T extends Routes>(r: T):
|
|
14
|
+
export function createRouter<T extends Routes>(r: T): RouterApi<T>;
|
|
15
15
|
export const Router: Component;
|
|
16
16
|
|
|
17
17
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
@@ -32,10 +32,55 @@ export type Routes = {
|
|
|
32
32
|
layout?: LayoutComponent;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
export type
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
export type RouterApi<T extends Routes> = {
|
|
36
|
+
/**
|
|
37
|
+
* Construct a path while ensuring type safety.
|
|
38
|
+
*
|
|
39
|
+
* ```js
|
|
40
|
+
* p('/users');
|
|
41
|
+
* // With parameters
|
|
42
|
+
* p('/users/:id', { id: 1 });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param route The route to navigate to.
|
|
46
|
+
* @param params The parameters to replace in the route.
|
|
47
|
+
*/
|
|
48
|
+
p<U extends Path<T>>(...args: ConstructPathArgs<U>): string;
|
|
49
|
+
/**
|
|
50
|
+
* Navigate programatically to a route.
|
|
51
|
+
*
|
|
52
|
+
* ```js
|
|
53
|
+
* navigate('/users');
|
|
54
|
+
* // With parameters
|
|
55
|
+
* navigate('/users/:id', {
|
|
56
|
+
* params: {
|
|
57
|
+
* id: 1,
|
|
58
|
+
* },
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @param route The route to navigate to.
|
|
63
|
+
* @param options The navigation options.
|
|
64
|
+
*/
|
|
65
|
+
navigate<U extends Path<T>>(...args: NavigateArgs<U>): void;
|
|
66
|
+
route: {
|
|
67
|
+
/**
|
|
68
|
+
* An object containing the parameters of the current route.
|
|
69
|
+
*
|
|
70
|
+
* For example, given the route `/posts/:slug/comments/:commentId` and the URL
|
|
71
|
+
* `http://localhost:5173/posts/hello-world/comments/123`, the `params` object would be `{ slug:
|
|
72
|
+
* 'hello-world', commentId: '123' }`.
|
|
73
|
+
*/
|
|
74
|
+
params: AllParams<T>;
|
|
75
|
+
/** The reactive pathname of the URL. */
|
|
76
|
+
pathname: string;
|
|
77
|
+
/** The reactive query string part of the URL. */
|
|
78
|
+
search: string;
|
|
79
|
+
/** The reactive history state that can be passed to the `navigate` function. */
|
|
80
|
+
state: unknown;
|
|
81
|
+
/** The reactive hash part of the URL. */
|
|
82
|
+
hash: string;
|
|
83
|
+
};
|
|
39
84
|
};
|
|
40
85
|
|
|
41
86
|
export type Path<T extends Routes> = RemoveParenthesis<
|
|
@@ -50,6 +95,20 @@ export type PathParams<T extends string> =
|
|
|
50
95
|
|
|
51
96
|
export type AllParams<T extends Routes> = Partial<Record<ExtractParams<RecursiveKeys<T>>, string>>;
|
|
52
97
|
|
|
98
|
+
export type NavigateOptions =
|
|
99
|
+
| {
|
|
100
|
+
replace?: boolean;
|
|
101
|
+
search?: string;
|
|
102
|
+
state?: string;
|
|
103
|
+
hash?: `#${string}`;
|
|
104
|
+
}
|
|
105
|
+
| undefined;
|
|
106
|
+
|
|
107
|
+
export type NavigateArgs<T extends string> =
|
|
108
|
+
PathParams<T> extends never
|
|
109
|
+
? [T, NavigateOptions]
|
|
110
|
+
: [T, NavigateOptions & { params: PathParams<T> }];
|
|
111
|
+
|
|
53
112
|
type StripNonRoutes<T extends Routes> = {
|
|
54
113
|
[K in keyof T as K extends '*' ? never : K extends 'layout' ? never : K]: T[K] extends Routes
|
|
55
114
|
? StripNonRoutes<T[K]>
|
|
@@ -7,6 +7,12 @@ export type RouterOptions = {
|
|
|
7
7
|
* @default 'src/routes'
|
|
8
8
|
*/
|
|
9
9
|
path?: string;
|
|
10
|
+
/**
|
|
11
|
+
* If true, generates the routes in a .js file instead of a .ts file.
|
|
12
|
+
*
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
js?: boolean;
|
|
10
16
|
};
|
|
11
17
|
|
|
12
18
|
export const router: (options?: RouterOptions) => Plugin;
|
|
@@ -7,9 +7,8 @@ import { writeRouterCode } from '../gen/write-router-code.js';
|
|
|
7
7
|
* @returns {import('vite').Plugin}
|
|
8
8
|
*/
|
|
9
9
|
export function router(options) {
|
|
10
|
-
if (options?.path)
|
|
11
|
-
|
|
12
|
-
}
|
|
10
|
+
if (options?.path) genConfig.routesPath = options.path;
|
|
11
|
+
if (options?.js) genConfig.routesInJs = options.js;
|
|
13
12
|
|
|
14
13
|
return {
|
|
15
14
|
name: 'sv-router',
|