sv-router 0.0.2 → 0.0.3

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.
Files changed (49) hide show
  1. package/package.json +69 -74
  2. package/{dist → src}/RecursiveComponentTree.svelte +4 -4
  3. package/{dist → src}/Router.svelte +2 -2
  4. package/src/cli/index.js +5 -0
  5. package/src/create-router.svelte.js +67 -0
  6. package/src/gen/config.js +16 -0
  7. package/src/gen/generate-router-code.js +102 -0
  8. package/src/gen/write-router-code.js +52 -0
  9. package/src/helpers/match-route.js +112 -0
  10. package/src/helpers/utils.js +46 -0
  11. package/src/helpers/validate-routes.js +46 -0
  12. package/src/index.d.ts +77 -0
  13. package/src/index.js +2 -0
  14. package/src/vite-plugin/index.d.ts +12 -0
  15. package/src/vite-plugin/index.js +1 -0
  16. package/src/vite-plugin/plugin.js +42 -0
  17. package/dist/RecursiveComponentTree.svelte.d.ts +0 -8
  18. package/dist/cli/index.d.ts +0 -2
  19. package/dist/cli/index.js +0 -3
  20. package/dist/common.d.ts +0 -5
  21. package/dist/common.js +0 -6
  22. package/dist/gen/generate-router-code.d.ts +0 -12
  23. package/dist/gen/generate-router-code.js +0 -67
  24. package/dist/gen/generate-router-code.test.d.ts +0 -1
  25. package/dist/gen/generate-router-code.test.js +0 -103
  26. package/dist/gen/write-router-code.d.ts +0 -1
  27. package/dist/gen/write-router-code.js +0 -36
  28. package/dist/helpers/match-route.d.ts +0 -7
  29. package/dist/helpers/match-route.js +0 -66
  30. package/dist/helpers/match-route.test.d.ts +0 -1
  31. package/dist/helpers/match-route.test.js +0 -146
  32. package/dist/helpers/utils.d.ts +0 -8
  33. package/dist/helpers/utils.js +0 -28
  34. package/dist/helpers/validate-routes.d.ts +0 -3
  35. package/dist/helpers/validate-routes.js +0 -34
  36. package/dist/helpers/validate-routes.test.d.ts +0 -1
  37. package/dist/helpers/validate-routes.test.js +0 -67
  38. package/dist/index.d.ts +0 -2
  39. package/dist/index.js +0 -2
  40. package/dist/router.svelte.d.ts +0 -13
  41. package/dist/router.svelte.js +0 -49
  42. package/dist/types/test-types.d.ts +0 -1
  43. package/dist/types/test-types.js +0 -1
  44. package/dist/types/types.d.ts +0 -26
  45. package/dist/types/types.js +0 -1
  46. package/dist/vite-plugin/index.d.ts +0 -1
  47. package/dist/vite-plugin/index.js +0 -1
  48. package/dist/vite-plugin/plugin.d.ts +0 -2
  49. 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,2 @@
1
+ export { createRouter } from './create-router.svelte.js';
2
+ export { default as Router } from './Router.svelte';
@@ -0,0 +1,12 @@
1
+ import type { Plugin } from 'vite';
2
+
3
+ export type RouterOptions = {
4
+ /**
5
+ * The path to the routes folder.
6
+ *
7
+ * @default 'src/routes'
8
+ */
9
+ path?: string;
10
+ };
11
+
12
+ export const router: (options?: RouterOptions) => Plugin;
@@ -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;
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/dist/cli/index.js DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import { writeRouterCode } from "../gen/write-router-code.js";
3
- writeRouterCode();
package/dist/common.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export declare const ROUTES_PATH = "src/routes";
2
- export declare const GEN_CODE_DIR_PATH = ".router";
3
- export declare const ROUTER_PATH: string;
4
- export declare const TSCONFIG_PATH: string;
5
- export declare const GEN_CODE_ALIAS = "sv-router/generated";
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
- });
@@ -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;
@@ -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
- }
@@ -1,3 +0,0 @@
1
- import type { Routes } from '../types/types.ts';
2
- export declare function validateRoutes(routes: Routes): void;
3
- export declare function getRoutePaths(routes: Routes): string[];