sv-router 0.0.2 → 0.0.4
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 +69 -74
- package/{dist → src}/RecursiveComponentTree.svelte +4 -4
- package/{dist → src}/Router.svelte +2 -2
- package/src/cli/index.js +13 -0
- package/src/create-router.svelte.js +67 -0
- package/src/gen/config.js +16 -0
- package/src/gen/generate-router-code.js +102 -0
- package/src/gen/write-router-code.js +52 -0
- package/src/helpers/match-route.js +112 -0
- package/src/helpers/utils.js +46 -0
- package/src/helpers/validate-routes.js +46 -0
- package/src/index.d.ts +77 -0
- package/src/index.js +2 -0
- package/src/vite-plugin/index.d.ts +12 -0
- package/src/vite-plugin/index.js +1 -0
- package/src/vite-plugin/plugin.js +42 -0
- package/dist/RecursiveComponentTree.svelte.d.ts +0 -8
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -3
- package/dist/common.d.ts +0 -5
- package/dist/common.js +0 -6
- package/dist/gen/generate-router-code.d.ts +0 -12
- package/dist/gen/generate-router-code.js +0 -67
- package/dist/gen/generate-router-code.test.d.ts +0 -1
- package/dist/gen/generate-router-code.test.js +0 -103
- package/dist/gen/write-router-code.d.ts +0 -1
- package/dist/gen/write-router-code.js +0 -36
- package/dist/helpers/match-route.d.ts +0 -7
- package/dist/helpers/match-route.js +0 -66
- package/dist/helpers/match-route.test.d.ts +0 -1
- package/dist/helpers/match-route.test.js +0 -146
- package/dist/helpers/utils.d.ts +0 -8
- package/dist/helpers/utils.js +0 -28
- package/dist/helpers/validate-routes.d.ts +0 -3
- package/dist/helpers/validate-routes.js +0 -34
- package/dist/helpers/validate-routes.test.d.ts +0 -1
- package/dist/helpers/validate-routes.test.js +0 -67
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/router.svelte.d.ts +0 -13
- package/dist/router.svelte.js +0 -49
- package/dist/types/test-types.d.ts +0 -1
- package/dist/types/test-types.js +0 -1
- package/dist/types/types.d.ts +0 -26
- package/dist/types/types.js +0 -1
- package/dist/vite-plugin/index.d.ts +0 -1
- package/dist/vite-plugin/index.js +0 -1
- package/dist/vite-plugin/plugin.d.ts +0 -2
- package/dist/vite-plugin/plugin.js +0 -31
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Component, Snippet } from 'svelte';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Setup a new router instance with the given routes.
|
|
5
|
+
*
|
|
6
|
+
* ```js
|
|
7
|
+
* export const { path, goto, params } = createRouter({
|
|
8
|
+
* '/': Home,
|
|
9
|
+
* '/about': About,
|
|
10
|
+
* ...
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function createRouter<T extends Routes>(r: T): RouterMethods<T>;
|
|
15
|
+
export const Router: Component;
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
18
|
+
type BaseProps = {};
|
|
19
|
+
|
|
20
|
+
export type LazyRouteComponent<Props extends BaseProps = BaseProps> = () => Promise<{
|
|
21
|
+
default: Component<Props>;
|
|
22
|
+
}>;
|
|
23
|
+
|
|
24
|
+
export type RouteComponent<Props extends BaseProps = any> =
|
|
25
|
+
| Component<Props>
|
|
26
|
+
| LazyRouteComponent<Props>;
|
|
27
|
+
export type LayoutComponent = RouteComponent<{ children: Snippet }>;
|
|
28
|
+
|
|
29
|
+
export type Routes = {
|
|
30
|
+
[_: `/${string}`]: RouteComponent | Routes;
|
|
31
|
+
'*'?: RouteComponent;
|
|
32
|
+
layout?: LayoutComponent;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type RouterMethods<T extends Routes> = {
|
|
36
|
+
path<U extends Path<T>>(...args: ConstructPathArgs<U>): string;
|
|
37
|
+
goto<U extends Path<T>>(...args: ConstructPathArgs<U>): void;
|
|
38
|
+
params(): AllParams<T>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type Path<T extends Routes> = RemoveParenthesis<
|
|
42
|
+
RemoveLastSlash<RecursiveKeys<StripNonRoutes<T>>>
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
export type ConstructPathArgs<T extends string> =
|
|
46
|
+
PathParams<T> extends never ? [T] : [T, PathParams<T>];
|
|
47
|
+
|
|
48
|
+
export type PathParams<T extends string> =
|
|
49
|
+
ExtractParams<T> extends never ? never : Record<ExtractParams<T>, string>;
|
|
50
|
+
|
|
51
|
+
export type AllParams<T extends Routes> = Partial<Record<ExtractParams<RecursiveKeys<T>>, string>>;
|
|
52
|
+
|
|
53
|
+
type StripNonRoutes<T extends Routes> = {
|
|
54
|
+
[K in keyof T as K extends '*' ? never : K extends 'layout' ? never : K]: T[K] extends Routes
|
|
55
|
+
? StripNonRoutes<T[K]>
|
|
56
|
+
: T[K];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type RecursiveKeys<T extends Routes, Prefix extends string = ''> = {
|
|
60
|
+
[K in keyof T]: K extends string
|
|
61
|
+
? T[K] extends Routes
|
|
62
|
+
? RecursiveKeys<T[K], `${Prefix}${K}`>
|
|
63
|
+
: `${Prefix}${K}`
|
|
64
|
+
: never;
|
|
65
|
+
}[keyof T];
|
|
66
|
+
|
|
67
|
+
type RemoveLastSlash<T extends string> = T extends '/' ? T : T extends `${infer R}/` ? R : T;
|
|
68
|
+
|
|
69
|
+
type RemoveParenthesis<T extends string> = T extends `${infer A}(${infer B})${infer C}`
|
|
70
|
+
? RemoveParenthesis<`${A}${B}${C}`>
|
|
71
|
+
: T;
|
|
72
|
+
|
|
73
|
+
type ExtractParams<T extends string> = T extends `${string}:${infer Param}/${infer Rest}`
|
|
74
|
+
? Param | ExtractParams<`/${Rest}`>
|
|
75
|
+
: T extends `${string}:${infer Param}`
|
|
76
|
+
? Param
|
|
77
|
+
: never;
|
package/src/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { router } from './plugin.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { genConfig } from '../gen/config.js';
|
|
3
|
+
import { writeRouterCode } from '../gen/write-router-code.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('./index.d.ts').RouterOptions | undefined} options
|
|
7
|
+
* @returns {import('vite').Plugin}
|
|
8
|
+
*/
|
|
9
|
+
export function router(options) {
|
|
10
|
+
if (options?.path) {
|
|
11
|
+
genConfig.routesPath = options.path;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
name: 'sv-router',
|
|
16
|
+
config(config) {
|
|
17
|
+
if (!config.resolve) {
|
|
18
|
+
config.resolve = {};
|
|
19
|
+
}
|
|
20
|
+
if (!config.resolve.alias) {
|
|
21
|
+
config.resolve.alias = {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const replacement = path.resolve(process.cwd(), genConfig.routerPath);
|
|
25
|
+
|
|
26
|
+
if (Array.isArray(config.resolve.alias)) {
|
|
27
|
+
config.resolve.alias.push({ find: genConfig.genCodeAlias, replacement });
|
|
28
|
+
} else {
|
|
29
|
+
/** @type {Record<string, string>} */ (config.resolve.alias)[genConfig.genCodeAlias] =
|
|
30
|
+
replacement;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
buildStart() {
|
|
34
|
+
writeRouterCode();
|
|
35
|
+
},
|
|
36
|
+
watchChange(file) {
|
|
37
|
+
if (file.includes(genConfig.routesPath)) {
|
|
38
|
+
writeRouterCode();
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Component } from 'svelte';
|
|
2
|
-
import RecursiveComponentTree from './RecursiveComponentTree.svelte';
|
|
3
|
-
type $$ComponentProps = {
|
|
4
|
-
tree: Component[];
|
|
5
|
-
};
|
|
6
|
-
declare const RecursiveComponentTree: Component<$$ComponentProps, {}, "">;
|
|
7
|
-
type RecursiveComponentTree = ReturnType<typeof RecursiveComponentTree>;
|
|
8
|
-
export default RecursiveComponentTree;
|
package/dist/cli/index.d.ts
DELETED
package/dist/cli/index.js
DELETED
package/dist/common.d.ts
DELETED
package/dist/common.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
export const ROUTES_PATH = 'src/routes';
|
|
3
|
-
export const GEN_CODE_DIR_PATH = '.router';
|
|
4
|
-
export const ROUTER_PATH = path.join(GEN_CODE_DIR_PATH, '/router.ts');
|
|
5
|
-
export const TSCONFIG_PATH = path.join(GEN_CODE_DIR_PATH, '/tsconfig.json');
|
|
6
|
-
export const GEN_CODE_ALIAS = 'sv-router/generated';
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
type FileTree = (string | {
|
|
2
|
-
name: string;
|
|
3
|
-
tree: FileTree;
|
|
4
|
-
})[];
|
|
5
|
-
type GeneratedRoutes = {
|
|
6
|
-
[key: string]: string | GeneratedRoutes;
|
|
7
|
-
};
|
|
8
|
-
export declare function generateRouterCode(routesPath: string): string;
|
|
9
|
-
export declare function buildFileTree(routesPath: string): FileTree;
|
|
10
|
-
export declare function createRouteMap(fileTree: FileTree, prefix?: string): GeneratedRoutes;
|
|
11
|
-
export declare function createRouterCode(routes: GeneratedRoutes, routesPath: string): string;
|
|
12
|
-
export {};
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
const PARAM_FILENAME_REGEX = /\[(.*)\].svelte/g;
|
|
4
|
-
export function generateRouterCode(routesPath) {
|
|
5
|
-
const fileTree = buildFileTree(path.join(process.cwd(), routesPath));
|
|
6
|
-
const routeMap = createRouteMap(fileTree);
|
|
7
|
-
return createRouterCode(routeMap, path.join('..', routesPath));
|
|
8
|
-
}
|
|
9
|
-
export function buildFileTree(routesPath) {
|
|
10
|
-
const entries = fs.readdirSync(routesPath);
|
|
11
|
-
const result = [];
|
|
12
|
-
for (const entry of entries) {
|
|
13
|
-
const stat = fs.lstatSync(path.join(routesPath, entry));
|
|
14
|
-
if (stat.isDirectory()) {
|
|
15
|
-
result.push({ name: entry, tree: buildFileTree(path.join(routesPath, entry)) });
|
|
16
|
-
}
|
|
17
|
-
else if (entry.endsWith('.svelte')) {
|
|
18
|
-
result.push(entry);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return result;
|
|
22
|
-
}
|
|
23
|
-
export function createRouteMap(fileTree, prefix = '') {
|
|
24
|
-
const result = {};
|
|
25
|
-
for (const entry of fileTree) {
|
|
26
|
-
if (typeof entry === 'string') {
|
|
27
|
-
switch (entry) {
|
|
28
|
-
case 'index.svelte': {
|
|
29
|
-
result['/'] = prefix + entry;
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
case '*.svelte': {
|
|
33
|
-
result['*'] = prefix + entry;
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
case '_layout.svelte': {
|
|
37
|
-
result['layout'] = prefix + entry;
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
default: {
|
|
41
|
-
if (PARAM_FILENAME_REGEX.test(entry)) {
|
|
42
|
-
result['/' + entry.replaceAll(PARAM_FILENAME_REGEX, ':$1')] = prefix + entry;
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
result['/' + entry.replace('.svelte', '')] = prefix + entry;
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
result['/' + entry.name] = createRouteMap(entry.tree, prefix + entry.name + '/');
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return result;
|
|
55
|
-
}
|
|
56
|
-
export function createRouterCode(routes, routesPath) {
|
|
57
|
-
if (!routesPath.endsWith('/')) {
|
|
58
|
-
routesPath += '/';
|
|
59
|
-
}
|
|
60
|
-
const jsonRoutes = JSON.stringify(routes, undefined, 2);
|
|
61
|
-
const withImports = jsonRoutes.replaceAll(/"(.*)": "(.*)",?/g, `"$1": () => import("${routesPath}$2"),`);
|
|
62
|
-
return [
|
|
63
|
-
'import { createRouter } from "sv-router";',
|
|
64
|
-
'\n\n',
|
|
65
|
-
`export const { path, goto, params } = createRouter(${withImports});`,
|
|
66
|
-
].join('');
|
|
67
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { buildFileTree, createRouteMap, createRouterCode, generateRouterCode, } from "./generate-router-code.js";
|
|
2
|
-
vi.mock('node:fs', () => ({
|
|
3
|
-
default: {
|
|
4
|
-
readdirSync: (dir) => {
|
|
5
|
-
if (dir.toString().endsWith('posts')) {
|
|
6
|
-
return [
|
|
7
|
-
'[id].svelte',
|
|
8
|
-
'_layout.svelte',
|
|
9
|
-
'index.svelte',
|
|
10
|
-
'static.svelte',
|
|
11
|
-
'text.txt',
|
|
12
|
-
'noextension',
|
|
13
|
-
];
|
|
14
|
-
}
|
|
15
|
-
return ['*.svelte', 'about.svelte', 'index.svelte', 'posts'];
|
|
16
|
-
},
|
|
17
|
-
lstatSync: (dir) => ({
|
|
18
|
-
isDirectory: () => dir.toString().endsWith('posts'),
|
|
19
|
-
}),
|
|
20
|
-
},
|
|
21
|
-
}));
|
|
22
|
-
describe('generateRouterCode', () => {
|
|
23
|
-
it('should generate the router code', () => {
|
|
24
|
-
const result = generateRouterCode('./a/fake/path');
|
|
25
|
-
expect(result).toBe(`import { createRouter } from "sv-router";
|
|
26
|
-
|
|
27
|
-
export const { path, goto, params } = createRouter({
|
|
28
|
-
"*": () => import("../a/fake/path/*.svelte"),
|
|
29
|
-
"/about": () => import("../a/fake/path/about.svelte"),
|
|
30
|
-
"/": () => import("../a/fake/path/index.svelte"),
|
|
31
|
-
"/posts": {
|
|
32
|
-
"/:id": () => import("../a/fake/path/posts/[id].svelte"),
|
|
33
|
-
"layout": () => import("../a/fake/path/posts/_layout.svelte"),
|
|
34
|
-
"/": () => import("../a/fake/path/posts/index.svelte"),
|
|
35
|
-
"/static": () => import("../a/fake/path/posts/static.svelte"),
|
|
36
|
-
}
|
|
37
|
-
});`);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
describe('buildFileTree', () => {
|
|
41
|
-
it('should get the file tree', () => {
|
|
42
|
-
const result = buildFileTree('a/fake/path');
|
|
43
|
-
expect(result).toEqual([
|
|
44
|
-
'*.svelte',
|
|
45
|
-
'about.svelte',
|
|
46
|
-
'index.svelte',
|
|
47
|
-
{
|
|
48
|
-
name: 'posts',
|
|
49
|
-
tree: ['[id].svelte', '_layout.svelte', 'index.svelte', 'static.svelte'],
|
|
50
|
-
},
|
|
51
|
-
]);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
describe('createRouteMap', () => {
|
|
55
|
-
it('should generate routes', () => {
|
|
56
|
-
const result = createRouteMap([
|
|
57
|
-
'index.svelte',
|
|
58
|
-
'about.svelte',
|
|
59
|
-
{
|
|
60
|
-
name: 'posts',
|
|
61
|
-
tree: ['index.svelte', 'static.svelte', '[id].svelte', '_layout.svelte'],
|
|
62
|
-
},
|
|
63
|
-
'*.svelte',
|
|
64
|
-
]);
|
|
65
|
-
expect(result).toEqual({
|
|
66
|
-
'/': 'index.svelte',
|
|
67
|
-
'/about': 'about.svelte',
|
|
68
|
-
'/posts': {
|
|
69
|
-
'/': 'posts/index.svelte',
|
|
70
|
-
'/static': 'posts/static.svelte',
|
|
71
|
-
'/:id': 'posts/[id].svelte',
|
|
72
|
-
layout: 'posts/_layout.svelte',
|
|
73
|
-
},
|
|
74
|
-
'*': '*.svelte',
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
describe('createRouterCode', () => {
|
|
79
|
-
it('should generate the router', () => {
|
|
80
|
-
const result = createRouterCode({
|
|
81
|
-
'/': 'index.svelte',
|
|
82
|
-
'/about': 'about.svelte',
|
|
83
|
-
'/posts': {
|
|
84
|
-
'/': 'posts/index.svelte',
|
|
85
|
-
'/static': 'posts/static.svelte',
|
|
86
|
-
'/:id': 'posts/:id.svelte',
|
|
87
|
-
},
|
|
88
|
-
'*': '*.svelte',
|
|
89
|
-
}, './routes');
|
|
90
|
-
expect(result).toBe(`import { createRouter } from "sv-router";
|
|
91
|
-
|
|
92
|
-
export const { path, goto, params } = createRouter({
|
|
93
|
-
"/": () => import("./routes/index.svelte"),
|
|
94
|
-
"/about": () => import("./routes/about.svelte"),
|
|
95
|
-
"/posts": {
|
|
96
|
-
"/": () => import("./routes/posts/index.svelte"),
|
|
97
|
-
"/static": () => import("./routes/posts/static.svelte"),
|
|
98
|
-
"/:id": () => import("./routes/posts/:id.svelte"),
|
|
99
|
-
},
|
|
100
|
-
"*": () => import("./routes/*.svelte"),
|
|
101
|
-
});`);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function writeRouterCode(): void;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { GEN_CODE_ALIAS, GEN_CODE_DIR_PATH, ROUTER_PATH, ROUTES_PATH, TSCONFIG_PATH, } from "../common.js";
|
|
5
|
-
import { generateRouterCode } from "./generate-router-code.js";
|
|
6
|
-
export function writeRouterCode() {
|
|
7
|
-
try {
|
|
8
|
-
if (!fs.existsSync(GEN_CODE_DIR_PATH)) {
|
|
9
|
-
fs.mkdirSync(GEN_CODE_DIR_PATH);
|
|
10
|
-
}
|
|
11
|
-
// Write `.router/router.ts` file
|
|
12
|
-
const routerCode = generateRouterCode(ROUTES_PATH);
|
|
13
|
-
writeFileIfDifferent(ROUTER_PATH, routerCode);
|
|
14
|
-
// Write `.router/tsconfig.json` file
|
|
15
|
-
const tsConfig = {
|
|
16
|
-
compilerOptions: {
|
|
17
|
-
module: 'Preserve',
|
|
18
|
-
moduleResolution: 'Bundler',
|
|
19
|
-
paths: {
|
|
20
|
-
[GEN_CODE_ALIAS]: [path.join('..', ROUTER_PATH)],
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
include: ['./router.ts'],
|
|
24
|
-
};
|
|
25
|
-
writeFileIfDifferent(TSCONFIG_PATH, JSON.stringify(tsConfig, undefined, 2));
|
|
26
|
-
console.log('✅️ Routes generated');
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
console.error('Error during routes generation:', error);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function writeFileIfDifferent(filePath, content) {
|
|
33
|
-
if (!fs.existsSync(filePath) || fs.readFileSync(filePath, 'utf8') !== content) {
|
|
34
|
-
fs.writeFileSync(filePath, content);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { LayoutComponent, RouteComponent, Routes } from '../types/types.ts';
|
|
2
|
-
export declare function matchRoute(pathname: string, routes: Routes): {
|
|
3
|
-
match: RouteComponent | undefined;
|
|
4
|
-
layouts: LayoutComponent[];
|
|
5
|
-
params: Record<string, string>;
|
|
6
|
-
};
|
|
7
|
-
export declare function sortRoutes(routes: string[]): string[];
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
export function matchRoute(pathname, routes) {
|
|
2
|
-
// Remove trailing slash
|
|
3
|
-
if (pathname.length > 1 && pathname.endsWith('/')) {
|
|
4
|
-
pathname = pathname.slice(0, -1);
|
|
5
|
-
}
|
|
6
|
-
const pathParts = pathname.split('/');
|
|
7
|
-
const allRouteParts = sortRoutes(Object.keys(routes)).map((route) => route.split('/'));
|
|
8
|
-
let match;
|
|
9
|
-
const layouts = [];
|
|
10
|
-
let params = {};
|
|
11
|
-
outer: for (const routeParts of allRouteParts) {
|
|
12
|
-
for (const [index, routePart] of sortRoutes(routeParts).entries()) {
|
|
13
|
-
const pathPart = pathParts[index];
|
|
14
|
-
if (routePart.startsWith(':')) {
|
|
15
|
-
params[routePart.slice(1)] = pathPart;
|
|
16
|
-
}
|
|
17
|
-
else if (routePart === '*') {
|
|
18
|
-
match = routes[routeParts.join('/')];
|
|
19
|
-
break outer;
|
|
20
|
-
}
|
|
21
|
-
else if (routePart !== pathPart) {
|
|
22
|
-
break;
|
|
23
|
-
}
|
|
24
|
-
if (index !== routeParts.length - 1) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const routeMatch = routes[routeParts.join('/')];
|
|
28
|
-
if (typeof routeMatch !== 'function' &&
|
|
29
|
-
routeMatch?.layout &&
|
|
30
|
-
!layouts.includes(routeMatch.layout)) {
|
|
31
|
-
layouts.push(routeMatch.layout);
|
|
32
|
-
}
|
|
33
|
-
if (typeof routeMatch === 'function') {
|
|
34
|
-
if (routeParts.length === pathParts.length) {
|
|
35
|
-
match = routeMatch;
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
else if (routeMatch) {
|
|
42
|
-
const nestedPathname = '/' + pathParts.slice(index + 1).join('/');
|
|
43
|
-
const result = matchRoute(nestedPathname, routeMatch);
|
|
44
|
-
if (result) {
|
|
45
|
-
match = result.match;
|
|
46
|
-
params = { ...params, ...result.params };
|
|
47
|
-
layouts.push(...result.layouts);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
break outer;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return { match, layouts, params };
|
|
54
|
-
}
|
|
55
|
-
export function sortRoutes(routes) {
|
|
56
|
-
return routes.toSorted((a, b) => getRoutePriority(a) - getRoutePriority(b));
|
|
57
|
-
}
|
|
58
|
-
function getRoutePriority(route) {
|
|
59
|
-
if (route === '' || route === '/')
|
|
60
|
-
return 1;
|
|
61
|
-
if (route === '*')
|
|
62
|
-
return 4;
|
|
63
|
-
if (route.includes(':'))
|
|
64
|
-
return 3;
|
|
65
|
-
return 2;
|
|
66
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { matchRoute, sortRoutes } from "./match-route.js";
|
|
2
|
-
const Home = (() => 'Home');
|
|
3
|
-
const Posts = (() => 'Posts');
|
|
4
|
-
const StaticPost = (() => 'StaticPost');
|
|
5
|
-
const DynamicPost = (() => 'DynamicPost');
|
|
6
|
-
const DynamicPostComment = (() => 'DynamicPostComment');
|
|
7
|
-
const UserNotFound = (() => 'UserNotFound');
|
|
8
|
-
const PageNotFound = (() => 'PageNotFound');
|
|
9
|
-
const Layout1 = (() => 'Layout1');
|
|
10
|
-
const Layout2 = (() => 'Layout2');
|
|
11
|
-
describe('matchRoute', () => {
|
|
12
|
-
describe.each([
|
|
13
|
-
{
|
|
14
|
-
mode: 'flat',
|
|
15
|
-
routes: {
|
|
16
|
-
'/': Home,
|
|
17
|
-
'/posts': Posts,
|
|
18
|
-
'/posts/static': StaticPost,
|
|
19
|
-
'/posts/:id': DynamicPost,
|
|
20
|
-
'/posts/:id/:commentId': DynamicPostComment,
|
|
21
|
-
'/users/*': UserNotFound,
|
|
22
|
-
'*': PageNotFound,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
mode: 'flat unordered',
|
|
27
|
-
routes: {
|
|
28
|
-
'/posts/:id/:commentId': DynamicPostComment,
|
|
29
|
-
'/posts': Posts,
|
|
30
|
-
'/users/*': UserNotFound,
|
|
31
|
-
'/posts/static': StaticPost,
|
|
32
|
-
'*': PageNotFound,
|
|
33
|
-
'/posts/:id': DynamicPost,
|
|
34
|
-
'/': Home,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
mode: 'tree',
|
|
39
|
-
routes: {
|
|
40
|
-
'/': Home,
|
|
41
|
-
'/posts': {
|
|
42
|
-
'/': Posts,
|
|
43
|
-
'/static': StaticPost,
|
|
44
|
-
'/:id': {
|
|
45
|
-
'/': DynamicPost,
|
|
46
|
-
'/:commentId': DynamicPostComment,
|
|
47
|
-
layout: Layout2,
|
|
48
|
-
},
|
|
49
|
-
layout: Layout1,
|
|
50
|
-
},
|
|
51
|
-
'/users': {
|
|
52
|
-
'*': UserNotFound,
|
|
53
|
-
layout: Layout1,
|
|
54
|
-
},
|
|
55
|
-
'*': PageNotFound,
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
mode: 'tree unordered',
|
|
60
|
-
routes: {
|
|
61
|
-
'*': PageNotFound,
|
|
62
|
-
'/posts': {
|
|
63
|
-
'/:id': {
|
|
64
|
-
'/:commentId': DynamicPostComment,
|
|
65
|
-
'/': DynamicPost,
|
|
66
|
-
layout: Layout2,
|
|
67
|
-
},
|
|
68
|
-
'/': Posts,
|
|
69
|
-
'/static': StaticPost,
|
|
70
|
-
layout: Layout1,
|
|
71
|
-
},
|
|
72
|
-
'/users': {
|
|
73
|
-
'*': UserNotFound,
|
|
74
|
-
layout: Layout1,
|
|
75
|
-
},
|
|
76
|
-
'/': Home,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
])('$mode paths', ({ mode, routes: r }) => {
|
|
80
|
-
const routes = r;
|
|
81
|
-
const treeMode = mode.startsWith('tree');
|
|
82
|
-
it('should match the root route', () => {
|
|
83
|
-
const { match } = matchRoute('/', routes);
|
|
84
|
-
expect(match).toEqual(Home);
|
|
85
|
-
});
|
|
86
|
-
it('should match a simple route', () => {
|
|
87
|
-
const { match } = matchRoute('/posts', routes);
|
|
88
|
-
expect(match).toEqual(Posts);
|
|
89
|
-
});
|
|
90
|
-
it('should match a simple route with a trailing slash', () => {
|
|
91
|
-
const { match } = matchRoute('/posts', routes);
|
|
92
|
-
expect(match).toEqual(Posts);
|
|
93
|
-
});
|
|
94
|
-
it('should match a nested route', () => {
|
|
95
|
-
const { match } = matchRoute('/posts/static', routes);
|
|
96
|
-
expect(match).toEqual(StaticPost);
|
|
97
|
-
});
|
|
98
|
-
it('should match a dynamic route and return a param', () => {
|
|
99
|
-
const { match, params } = matchRoute('/posts/bar', routes);
|
|
100
|
-
expect(match).toEqual(DynamicPost);
|
|
101
|
-
expect(params).toEqual({ id: 'bar' });
|
|
102
|
-
});
|
|
103
|
-
it('should match multiple dynamic nested routes and return params', () => {
|
|
104
|
-
const { match, params } = matchRoute('/posts/bar/baz', routes);
|
|
105
|
-
expect(match).toEqual(DynamicPostComment);
|
|
106
|
-
expect(params).toEqual({ id: 'bar', commentId: 'baz' });
|
|
107
|
-
});
|
|
108
|
-
if (treeMode) {
|
|
109
|
-
it('should match routes with layout', () => {
|
|
110
|
-
const { layouts: layouts1 } = matchRoute('/', routes);
|
|
111
|
-
const { layouts: layouts2 } = matchRoute('/posts', routes);
|
|
112
|
-
const { layouts: layouts3 } = matchRoute('/posts/static', routes);
|
|
113
|
-
const { layouts: layouts4 } = matchRoute('/posts/bar/baz', routes);
|
|
114
|
-
expect(layouts1).toEqual([]);
|
|
115
|
-
expect(layouts2).toEqual([Layout1]);
|
|
116
|
-
expect(layouts3).toEqual([Layout1]);
|
|
117
|
-
expect(layouts4).toEqual([Layout1, Layout2]);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
it('should match wildcard route', () => {
|
|
121
|
-
const { match, layouts } = matchRoute('/notfound', routes);
|
|
122
|
-
expect(match).toEqual(PageNotFound);
|
|
123
|
-
if (treeMode) {
|
|
124
|
-
expect(layouts).toEqual([]);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
it('should match wildcard nested route', () => {
|
|
128
|
-
const { match, layouts } = matchRoute('/users/notfound', routes);
|
|
129
|
-
expect(match).toEqual(UserNotFound);
|
|
130
|
-
if (treeMode) {
|
|
131
|
-
expect(layouts).toEqual([Layout1]);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
it('should not match any route', () => {
|
|
135
|
-
delete routes['*'];
|
|
136
|
-
const { match } = matchRoute('/notfound', routes);
|
|
137
|
-
expect(match).toBeUndefined();
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
describe('sortRoutes', () => {
|
|
142
|
-
it('should sort routes', () => {
|
|
143
|
-
const result = sortRoutes(['/:id', '*', '/foo', '', '/']);
|
|
144
|
-
expect(result).toEqual(['', '/', '/foo', '/:id', '*']);
|
|
145
|
-
});
|
|
146
|
-
});
|
package/dist/helpers/utils.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Component } from 'svelte';
|
|
2
|
-
import type { PathParams } from '../types/types.ts';
|
|
3
|
-
import type { LazyRouteComponent, RouteComponent } from '../types/types.ts';
|
|
4
|
-
export type ConstructPathArgs<T extends string> = PathParams<T> extends never ? [T] : [T, PathParams<T>];
|
|
5
|
-
export declare function constructPath<T extends string>(...args: ConstructPathArgs<T>): string;
|
|
6
|
-
export declare function resolveRouteComponents(input: RouteComponent<any>[]): Promise<Component[]>;
|
|
7
|
-
export declare function resolveRouteComponent(input: RouteComponent): Promise<Component>;
|
|
8
|
-
export declare function isLazyImport(input: unknown): input is LazyRouteComponent;
|
package/dist/helpers/utils.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export function constructPath(...args) {
|
|
2
|
-
const [path, params] = args;
|
|
3
|
-
if (!params)
|
|
4
|
-
return path;
|
|
5
|
-
let result = path;
|
|
6
|
-
for (const key in params) {
|
|
7
|
-
result = result.replace(`:${key}`, params[key]);
|
|
8
|
-
}
|
|
9
|
-
return result;
|
|
10
|
-
}
|
|
11
|
-
export function resolveRouteComponents(input) {
|
|
12
|
-
return Promise.all(input.map((c) => resolveRouteComponent(c)));
|
|
13
|
-
}
|
|
14
|
-
export function resolveRouteComponent(input) {
|
|
15
|
-
return new Promise((resolve) => {
|
|
16
|
-
if (isLazyImport(input)) {
|
|
17
|
-
Promise.resolve(input()).then((module) => {
|
|
18
|
-
resolve(module.default);
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
resolve(input);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
export function isLazyImport(input) {
|
|
27
|
-
return typeof input === 'function' && !!/\(\)\s?=>\s?import\(.*\)/g.test(String(input));
|
|
28
|
-
}
|