vite-plugin-vanjs 0.0.10 → 0.1.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 +19 -221
- package/client/global.d.ts +3 -3
- package/client/index.mjs +141 -7
- package/client/types.d.ts +3 -3
- package/jsx/jsx.d.ts +2 -0
- package/jsx/jsx.mjs +4 -4
- package/meta/Head.mjs +3 -3
- package/meta/helpers.mjs +3 -5
- package/package.json +19 -19
- package/plugin/helpers.mjs +271 -0
- package/plugin/index.mjs +206 -0
- package/plugin/types.d.ts +23 -0
- package/router/a.mjs +18 -19
- package/router/cache.mjs +4 -3
- package/router/global.d.ts +74 -28
- package/router/helpers.mjs +32 -27
- package/router/lazy.mjs +22 -11
- package/router/router.mjs +28 -21
- package/router/routes.mjs +38 -9
- package/router/state.mjs +3 -3
- package/router/types.d.ts +103 -29
- package/server/index.mjs +32 -9
- package/server/types.d.ts +56 -0
- package/setup/global.d.ts +41 -39
- package/setup/helpers.mjs +7 -0
- package/setup/index-debug.mjs +8 -20
- package/setup/index-ssr.mjs +5 -0
- package/setup/index.mjs +4 -20
- package/setup/isServer.mjs +2 -0
- package/setup/package.json +16 -0
- package/setup/van-debug.mjs +2 -2
- package/setup/van-ssr..d.ts +1 -0
- package/setup/van-ssr.mjs +51 -0
- package/setup/van.mjs +44 -2
- package/setup/vanX-ssr.d.ts +1 -0
- package/setup/vanX-ssr.mjs +12 -0
- package/setup/vanX.mjs +23 -4
- package/tsconfig.json +1 -1
- package/src/index.mjs +0 -79
- package/src/types.d.ts +0 -28
package/router/global.d.ts
CHANGED
|
@@ -14,20 +14,30 @@ declare module "@vanjs/router" {
|
|
|
14
14
|
} from "vanjs-core";
|
|
15
15
|
|
|
16
16
|
// type VanElement = VElement | Exclude<Primitive, boolean | undefined>;
|
|
17
|
-
type DOMElement = globalThis.Element;
|
|
17
|
+
// type DOMElement = globalThis.Element;
|
|
18
18
|
type PrimitiveChild = Primitive | State<Primitive | undefined | null>;
|
|
19
19
|
|
|
20
|
-
type
|
|
20
|
+
type DOMElement<T extends keyof HTMLElementTagNameMap = "div"> =
|
|
21
21
|
| SVGElement
|
|
22
22
|
| HTMLElement
|
|
23
|
-
| DOMElement
|
|
24
23
|
| Node
|
|
24
|
+
| HTMLElementTagNameMap[T];
|
|
25
|
+
|
|
26
|
+
export type VanElement =
|
|
27
|
+
| DOMElement
|
|
25
28
|
| VElement
|
|
26
29
|
| PrimitiveChild;
|
|
30
|
+
// type VanElement =
|
|
31
|
+
// | SVGElement
|
|
32
|
+
// | HTMLElement
|
|
33
|
+
// | DOMElement
|
|
34
|
+
// | Node
|
|
35
|
+
// | VElement
|
|
36
|
+
// | PrimitiveChild;
|
|
27
37
|
export type VanNode =
|
|
28
38
|
| VanElement
|
|
29
|
-
|
|
|
30
|
-
| (() =>
|
|
39
|
+
| VanElement[]
|
|
40
|
+
| (() => VanElement | VanElement[])
|
|
31
41
|
| null
|
|
32
42
|
| undefined;
|
|
33
43
|
|
|
@@ -62,14 +72,12 @@ declare module "@vanjs/router" {
|
|
|
62
72
|
* return Router(); // or <Router /> for JSX
|
|
63
73
|
* }
|
|
64
74
|
*/
|
|
65
|
-
// export const Router: JSX.Component<HTMLElement> & VanComponent<HTMLElement>;
|
|
66
|
-
// export function Router(props?: ComponentProps<HTMLElement>): HTMLElement;
|
|
67
|
-
// export function Router(
|
|
68
|
-
// props?: JSX.IntrinsicElements["main"] & { children: null },
|
|
69
|
-
// ): HTMLElement;
|
|
70
75
|
export const Router:
|
|
71
76
|
& VanComponent<"main">
|
|
72
77
|
& JSX.Component<"main">;
|
|
78
|
+
export const FileSystemRouter:
|
|
79
|
+
& VanComponent<"main">
|
|
80
|
+
& JSX.Component<"main">;
|
|
73
81
|
|
|
74
82
|
// a.mjs
|
|
75
83
|
/**
|
|
@@ -123,8 +131,13 @@ declare module "@vanjs/router" {
|
|
|
123
131
|
export type RouteEntry = {
|
|
124
132
|
path: string;
|
|
125
133
|
component: Promise<ComponentModule>;
|
|
126
|
-
|
|
127
|
-
|
|
134
|
+
params?: Record<string, string>;
|
|
135
|
+
preload?: (
|
|
136
|
+
params?: Record<string, string>,
|
|
137
|
+
) => boolean | void | Promise<boolean | void>;
|
|
138
|
+
load?: (
|
|
139
|
+
params?: Record<string, string>,
|
|
140
|
+
) => boolean | void | Promise<boolean | void>;
|
|
128
141
|
};
|
|
129
142
|
|
|
130
143
|
export type RouteProps = {
|
|
@@ -134,9 +147,16 @@ declare module "@vanjs/router" {
|
|
|
134
147
|
| HTMLElementTagNameMap[unknown]
|
|
135
148
|
| HTMLElementTagNameMap[unknown][])
|
|
136
149
|
| VanComponent
|
|
137
|
-
|
|
|
138
|
-
|
|
139
|
-
|
|
150
|
+
| ComponentFn
|
|
151
|
+
| (() => Promise<DynamicModule>);
|
|
152
|
+
// | ComponentModule["component"];
|
|
153
|
+
// component: ComponentFn | (() => Promise<ComponentModule>);
|
|
154
|
+
preload?: (
|
|
155
|
+
params?: Record<string, string>,
|
|
156
|
+
) => boolean | void | Promise<boolean | void>;
|
|
157
|
+
load?: (
|
|
158
|
+
params?: Record<string, string>,
|
|
159
|
+
) => boolean | void | Promise<boolean | void>;
|
|
140
160
|
};
|
|
141
161
|
export const routes: RouteEntry[];
|
|
142
162
|
|
|
@@ -185,42 +205,68 @@ declare module "@vanjs/router" {
|
|
|
185
205
|
|
|
186
206
|
/**
|
|
187
207
|
* Merge the children of an Element or an array of elements with an optional array of children
|
|
188
|
-
* into the childen of a
|
|
189
|
-
* @param
|
|
190
|
-
* @param
|
|
208
|
+
* into the childen prperty of a simple object.
|
|
209
|
+
* @param source
|
|
210
|
+
* @param children
|
|
191
211
|
*/
|
|
192
212
|
export const unwrap: (
|
|
193
213
|
source:
|
|
194
214
|
| VanNode
|
|
195
|
-
| IsoTagFunc
|
|
196
215
|
| VanNode[]
|
|
197
|
-
|
|
|
198
|
-
|
|
|
199
|
-
|
|
200
|
-
|
|
216
|
+
| VanComponent
|
|
217
|
+
| { children: VanNode[] }
|
|
218
|
+
| ComponentFn
|
|
219
|
+
| ReturnType<IsoTagFunc>
|
|
220
|
+
| ReturnType<IsoTagFunc>[]
|
|
221
|
+
| (() =>
|
|
222
|
+
| ReturnType<IsoTagFunc>
|
|
223
|
+
| ReturnType<IsoTagFunc>[]
|
|
224
|
+
| VanNode
|
|
225
|
+
| VanNode[]),
|
|
226
|
+
...children: ChildDom[]
|
|
227
|
+
) => { children: ChildDom[] };
|
|
228
|
+
|
|
229
|
+
export type JSXComponentFn = () => JSX.Element;
|
|
230
|
+
export type FragmentFn = () => ChildDom | ChildDom[];
|
|
231
|
+
export type ComponentFn = FragmentFn | VanComponent | JSXComponentFn;
|
|
201
232
|
|
|
202
233
|
export type ComponentModule = {
|
|
203
|
-
component:
|
|
204
|
-
route
|
|
234
|
+
component: ComponentFn;
|
|
235
|
+
route?: Pick<RouteEntry, "load" | "preload">;
|
|
205
236
|
};
|
|
206
237
|
|
|
207
238
|
export type DynamicModule = {
|
|
208
|
-
Page:
|
|
209
|
-
default?:
|
|
239
|
+
Page: ComponentFn;
|
|
240
|
+
default?: ComponentFn;
|
|
210
241
|
route?: Pick<RouteEntry, "load" | "preload">;
|
|
211
242
|
};
|
|
212
243
|
|
|
213
244
|
export type LazyComponent = Promise<DynamicModule>;
|
|
214
245
|
|
|
246
|
+
export type ImportFn = () => LazyComponent;
|
|
247
|
+
|
|
215
248
|
/**
|
|
216
249
|
* Registers a lazy component.
|
|
217
250
|
* @param importFn
|
|
218
251
|
*/
|
|
219
|
-
export const lazy: (importFn:
|
|
252
|
+
export const lazy: (importFn: ImportFn) => () => Promise<ComponentModule>;
|
|
220
253
|
|
|
221
254
|
/**
|
|
222
255
|
* Fixes the URL of a route.
|
|
223
256
|
* @param url
|
|
224
257
|
*/
|
|
225
258
|
export const fixRouteUrl: (url: string) => string;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Execute lifecycle methods preload and / or load
|
|
262
|
+
*/
|
|
263
|
+
export const executeLifecycle: (
|
|
264
|
+
{ route }: ComponentModule,
|
|
265
|
+
params?: Record<string, string> | undefined,
|
|
266
|
+
) => Promise<boolean | void>;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Find a registered route that matches the given path
|
|
270
|
+
*/
|
|
271
|
+
export const matchRoute: (path: string) => RouteEntry | null;
|
|
226
272
|
}
|
package/router/helpers.mjs
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// import van from "vanjs-core";
|
|
2
|
-
import
|
|
2
|
+
import isServer from "../setup/isServer.mjs";
|
|
3
3
|
import { routerState, setRouterState } from "./state.mjs";
|
|
4
4
|
import { matchRoute } from "./routes.mjs";
|
|
5
5
|
|
|
6
|
+
/** @typedef {typeof import("./types.d.ts").unwrap} Unwrap */
|
|
7
|
+
/** @typedef {typeof import("./types.d.ts").navigate} Navigate */
|
|
6
8
|
/** @typedef {import("./types.d.ts").Route} Route */
|
|
7
9
|
/** @typedef {import("./types.d.ts").VanNode} VanNode */
|
|
8
10
|
/** @typedef {import("./types.d.ts").ComponentModule} ComponentModule */
|
|
9
|
-
/** @typedef {import(
|
|
11
|
+
/** @typedef {import("./types.d.ts").DynamicModule} DynamicModule */
|
|
12
|
+
/** @typedef {import("./types.d.ts").ComponentFn} ComponentFn */
|
|
13
|
+
/** @typedef {import("./types.d.ts").LazyComponent} LazyComponent */
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* Check if selected page is the current page;
|
|
@@ -19,22 +23,24 @@ export const isCurrentPage = (pageName) => {
|
|
|
19
23
|
|
|
20
24
|
/**
|
|
21
25
|
* Merge the children of an Element or an array of elements with an optional array of children
|
|
22
|
-
* into the childen of a
|
|
23
|
-
* @
|
|
24
|
-
* @param {...Element[]} children
|
|
25
|
-
* @returns {TagFunc<HTMLFragmentElement> | HTMLElement}
|
|
26
|
+
* into the childen prperty of a simple object.
|
|
27
|
+
* @type {Unwrap}
|
|
26
28
|
*/
|
|
27
29
|
export const unwrap = (source, ...children) => {
|
|
28
30
|
const layout = () => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
/** @type {VanNode[]} */
|
|
32
|
+
const pageChildren =
|
|
33
|
+
source && typeof source === "object" && "children" in source &&
|
|
34
|
+
Array.isArray(source?.children)
|
|
35
|
+
? source.children
|
|
36
|
+
: typeof source === "function"
|
|
37
|
+
// @ts-expect-error - this case is specific to VanJS components in SSR
|
|
38
|
+
? [...source()?.children || source()]
|
|
39
|
+
: typeof HTMLElement !== "undefined" && source instanceof HTMLElement
|
|
40
|
+
? [...source.children]
|
|
41
|
+
: /* istanbul ignore next */ Array.isArray(source)
|
|
42
|
+
? source
|
|
43
|
+
: [source];
|
|
38
44
|
|
|
39
45
|
// return van.tags.fragment(
|
|
40
46
|
// ...(children || /* istanbul ignore next */ []),
|
|
@@ -52,16 +58,17 @@ export const unwrap = (source, ...children) => {
|
|
|
52
58
|
|
|
53
59
|
/**
|
|
54
60
|
* Check if component is a lazy component
|
|
55
|
-
* @param {
|
|
56
|
-
* @returns {component is (() =>
|
|
61
|
+
* @param {ComponentFn | (() => LazyComponent)} component
|
|
62
|
+
* @returns {component is (() => LazyComponent)}
|
|
57
63
|
*/
|
|
58
64
|
export const isLazyComponent = (component) => {
|
|
59
65
|
// Server: Check if it's an async function
|
|
60
|
-
if (
|
|
66
|
+
if (isServer && typeof component === "function") {
|
|
61
67
|
return component.constructor.name.includes("AsyncFunction");
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
// Client: Check if it's designated as lazy
|
|
71
|
+
// @ts-expect-error - this property is optional and on purpose
|
|
65
72
|
return component?.isLazy === true;
|
|
66
73
|
};
|
|
67
74
|
|
|
@@ -69,7 +76,7 @@ export const isLazyComponent = (component) => {
|
|
|
69
76
|
* Execute lifecycle methods preload and / or load
|
|
70
77
|
* @param {ComponentModule} param0
|
|
71
78
|
* @param {Record<string, string> | undefined} params
|
|
72
|
-
* @returns
|
|
79
|
+
* @returns {Promise<boolean>}
|
|
73
80
|
*/
|
|
74
81
|
export const executeLifecycle = async ({ route }, params) => {
|
|
75
82
|
// istanbul ignore next
|
|
@@ -88,16 +95,13 @@ export const executeLifecycle = async ({ route }, params) => {
|
|
|
88
95
|
|
|
89
96
|
/**
|
|
90
97
|
* Client only navigation utility.
|
|
91
|
-
* @
|
|
92
|
-
* @param {{ replace: boolean } | undefined} options - Navigation options
|
|
93
|
-
* @param {boolean} options.replace - Whether to replace current history entry
|
|
94
|
-
* @returns {void}
|
|
98
|
+
* @type {Navigate}
|
|
95
99
|
*/
|
|
96
100
|
export const navigate = (path, options = {}) => {
|
|
97
101
|
const { replace = false } = options;
|
|
98
102
|
|
|
99
103
|
// istanbul ignore else
|
|
100
|
-
if (!
|
|
104
|
+
if (!isServer) {
|
|
101
105
|
// Client-side navigation
|
|
102
106
|
const url = new URL(path, globalThis.location.origin);
|
|
103
107
|
const route = matchRoute(url.pathname);
|
|
@@ -121,9 +125,10 @@ export const navigate = (path, options = {}) => {
|
|
|
121
125
|
* Extract route params
|
|
122
126
|
* @param {string} pattern
|
|
123
127
|
* @param {string} path
|
|
124
|
-
* @returns {Record<string, string>}
|
|
128
|
+
* @returns {Record<string, string> | null}
|
|
125
129
|
*/
|
|
126
130
|
export const extractParams = (pattern, path) => {
|
|
131
|
+
/** @type {Record<string, string>} */
|
|
127
132
|
const params = {};
|
|
128
133
|
const patternParts = pattern.split("/");
|
|
129
134
|
const pathParts = path.split("/");
|
|
@@ -164,7 +169,7 @@ export const fixRouteUrl = (url) => {
|
|
|
164
169
|
* @returns {void}
|
|
165
170
|
*/
|
|
166
171
|
// export const reload = (forceFetch = false) => {
|
|
167
|
-
// if (!
|
|
172
|
+
// if (!isServer) {
|
|
168
173
|
// // Client-side reload
|
|
169
174
|
// if (forceFetch) {
|
|
170
175
|
// window.location.reload();
|
|
@@ -191,7 +196,7 @@ export const fixRouteUrl = (url) => {
|
|
|
191
196
|
// export const redirect = (path, options = {}) => {
|
|
192
197
|
// const { status = 302, replace = true } = options;
|
|
193
198
|
|
|
194
|
-
// if (!
|
|
199
|
+
// if (!isServer) {
|
|
195
200
|
// // Client-side redirect
|
|
196
201
|
// navigate(path, { replace });
|
|
197
202
|
// } else {
|
package/router/lazy.mjs
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import isServer from "../setup/isServer.mjs";
|
|
2
2
|
import van from "vanjs-core";
|
|
3
3
|
import { cache, getCached } from "./cache.mjs";
|
|
4
4
|
|
|
5
5
|
/** @typedef {import('./types').VanNode} VanNode */
|
|
6
|
+
/** @typedef {import('./types').DynamicModule} DynamicModule */
|
|
6
7
|
/** @typedef {import('./types').ComponentModule} ComponentModule */
|
|
8
|
+
/** @typedef {import('./types').ComponentFn} ComponentFn */
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Registers a lazy component.
|
|
10
|
-
* @
|
|
11
|
-
* @returns {ComponentModule | Promise<ComponentModule>}
|
|
12
|
+
* @type {typeof import("./types").lazy}
|
|
12
13
|
*/
|
|
13
14
|
export const lazy = (importFn) => {
|
|
14
|
-
if (
|
|
15
|
+
if (isServer) {
|
|
15
16
|
return async () => {
|
|
16
17
|
const cached = getCached(importFn);
|
|
17
18
|
/* istanbul ignore next */
|
|
18
19
|
if (cached) {
|
|
19
20
|
return cached;
|
|
20
21
|
}
|
|
22
|
+
|
|
21
23
|
const module = await importFn();
|
|
22
|
-
|
|
24
|
+
/** @type {ComponentFn} */
|
|
25
|
+
const component = module?.default || module.Page;
|
|
26
|
+
/** @type {ComponentModule} */
|
|
23
27
|
const result = { component, route: module.route };
|
|
24
28
|
|
|
25
29
|
cache(importFn, result);
|
|
@@ -28,7 +32,9 @@ export const lazy = (importFn) => {
|
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
let initialized = false;
|
|
35
|
+
/** @type {import("vanjs-core").State<(ComponentModule["component"] | (() => string))>} */
|
|
31
36
|
const component = van.state(() => "Loading..");
|
|
37
|
+
/** @type {import("vanjs-core").State<ComponentModule["route"]>} */
|
|
32
38
|
const route = van.state({});
|
|
33
39
|
|
|
34
40
|
const load = () => {
|
|
@@ -43,12 +49,16 @@ export const lazy = (importFn) => {
|
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
initialized = true;
|
|
46
|
-
importFn().then(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
importFn().then(
|
|
53
|
+
/** @param {DynamicModule} module */
|
|
54
|
+
(module) => {
|
|
55
|
+
/** @type {ComponentModule["component"]} */
|
|
56
|
+
const pageComponent = module?.default || module.Page;
|
|
57
|
+
cache(importFn, { component: pageComponent, route: module.route });
|
|
58
|
+
component.val = pageComponent;
|
|
59
|
+
route.val = module.route;
|
|
60
|
+
},
|
|
61
|
+
);
|
|
52
62
|
};
|
|
53
63
|
|
|
54
64
|
const lazyComponent = () => {
|
|
@@ -56,5 +66,6 @@ export const lazy = (importFn) => {
|
|
|
56
66
|
return { component: component.val(), route: route.val };
|
|
57
67
|
};
|
|
58
68
|
lazyComponent.isLazy = true;
|
|
69
|
+
// @ts-expect-error - typescript cannot handle this isomorphically
|
|
59
70
|
return lazyComponent;
|
|
60
71
|
};
|
package/router/router.mjs
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
import van from "vanjs-core";
|
|
2
|
-
import
|
|
2
|
+
import isServer from "../setup/isServer.mjs";
|
|
3
3
|
import { routerState, setRouterState } from "./state.mjs";
|
|
4
4
|
import { matchRoute } from "./routes.mjs";
|
|
5
5
|
import { executeLifecycle, unwrap } from "./helpers.mjs";
|
|
6
6
|
import { hydrate } from "../client/index.mjs";
|
|
7
|
-
import { Head, initializeHeadTags } from "
|
|
7
|
+
import { Head, initializeHeadTags } from "../meta/index.mjs";
|
|
8
|
+
|
|
9
|
+
import "virtual:@vanjs/routes";
|
|
8
10
|
|
|
9
11
|
let isConnected = false;
|
|
10
|
-
/** @param {Event & {target: globalThis}} e */
|
|
11
|
-
// istanbul ignore next - cannot test
|
|
12
|
-
const popHandler = (e) => {
|
|
13
|
-
const location = e.target.location;
|
|
14
|
-
const oldPath = routerState.pathname._oldVal;
|
|
15
|
-
// istanbul ignore next - cannot test
|
|
16
|
-
if (location.pathname !== oldPath) {
|
|
17
|
-
setRouterState(location.pathname, location.search);
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
12
|
|
|
21
13
|
export const Router = (initialProps = /* istanbul ignore next */ {}) => {
|
|
22
14
|
const { div, main } = van.tags;
|
|
@@ -29,11 +21,11 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
|
|
|
29
21
|
const mainLayout = () => {
|
|
30
22
|
const route = matchRoute(routerState.pathname.val);
|
|
31
23
|
/* istanbul ignore else */
|
|
32
|
-
if (!route) return van.add(wrapper, div("
|
|
24
|
+
if (!route) return van.add(wrapper, div("No Route Found"));
|
|
33
25
|
|
|
34
26
|
routerState.params.val = route.params || {};
|
|
35
27
|
// Server-side or async component: use renderComponent
|
|
36
|
-
if (
|
|
28
|
+
if (isServer) {
|
|
37
29
|
const renderComponent = async () => {
|
|
38
30
|
try {
|
|
39
31
|
const module = await route.component();
|
|
@@ -56,19 +48,30 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
|
|
|
56
48
|
|
|
57
49
|
const root = document.querySelector("[data-root]");
|
|
58
50
|
// istanbul ignore else - cannot test unmount
|
|
59
|
-
if (isConnected || !root) {
|
|
51
|
+
if (!isConnected || !root) {
|
|
60
52
|
initializeHeadTags();
|
|
61
|
-
globalThis.addEventListener(
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
globalThis.addEventListener(
|
|
54
|
+
"popstate",
|
|
55
|
+
/** @param {Event & {target: globalThis}} e */
|
|
56
|
+
// istanbul ignore next - cannot test
|
|
57
|
+
(e) => {
|
|
58
|
+
const location = e.target.location;
|
|
59
|
+
const oldPath = routerState.pathname._oldVal;
|
|
60
|
+
// istanbul ignore next - cannot test
|
|
61
|
+
if (location.pathname !== oldPath) {
|
|
62
|
+
setRouterState(location.pathname, location.search);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
);
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
// Client-side lazy component, lifeCycle is already executed on the server
|
|
67
69
|
// or when A component has been clicked in the client
|
|
68
70
|
if (root) {
|
|
69
71
|
// this case is when root is server side rendered
|
|
70
|
-
const module = route.component();
|
|
71
72
|
const children = () => {
|
|
73
|
+
const module = route.component();
|
|
74
|
+
executeLifecycle(module, route.params);
|
|
72
75
|
// istanbul ignore next - cannot test
|
|
73
76
|
const cp = (Array.isArray(module) || module instanceof Element)
|
|
74
77
|
? module
|
|
@@ -79,9 +82,9 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
|
|
|
79
82
|
const kids = () => cp ? Array.from(unwrap(cp).children) : [];
|
|
80
83
|
const kudos = kids();
|
|
81
84
|
|
|
85
|
+
isConnected = true;
|
|
82
86
|
// istanbul ignore else
|
|
83
87
|
if (document.head) {
|
|
84
|
-
isConnected = true;
|
|
85
88
|
van.hydrate(document.head, (head) => hydrate(head, Head()));
|
|
86
89
|
}
|
|
87
90
|
|
|
@@ -97,7 +100,11 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
|
|
|
97
100
|
});
|
|
98
101
|
|
|
99
102
|
const children = van.derive(() => {
|
|
100
|
-
const
|
|
103
|
+
const route = csrRoute.val;
|
|
104
|
+
// istanbul ignore if - can only be tested in client
|
|
105
|
+
if (!route) return [div("No Route Found")];
|
|
106
|
+
const md = route.component();
|
|
107
|
+
executeLifecycle(md, route.params);
|
|
101
108
|
// istanbul ignore next - cannot test all cases
|
|
102
109
|
const cp = (Array.isArray(md) || md instanceof Element)
|
|
103
110
|
? md
|
package/router/routes.mjs
CHANGED
|
@@ -4,6 +4,8 @@ import { lazy } from "./lazy.mjs";
|
|
|
4
4
|
|
|
5
5
|
/** @typedef {import("./types.d.ts").RouteEntry} RouteEntry */
|
|
6
6
|
/** @typedef {import("./types.d.ts").RouteProps} RouteProps */
|
|
7
|
+
/** @typedef {import("./types.d.ts").DynamicModule} DynamicModule */
|
|
8
|
+
/** @typedef {import("./types.d.ts").ComponentModule} ComponentModule */
|
|
7
9
|
|
|
8
10
|
/** @type {RouteEntry[]} */
|
|
9
11
|
export const routes = [];
|
|
@@ -15,15 +17,17 @@ export const Route = (routeProps) => {
|
|
|
15
17
|
const { path, component, preload, load, ...rest } = routeProps;
|
|
16
18
|
|
|
17
19
|
// istanbul ignore next - no point testing this error
|
|
18
|
-
if (path ===
|
|
19
|
-
|
|
20
|
+
if (routes.some((r) => r.path === path)) {
|
|
21
|
+
console.error(`🍦 @vanjs/router: duplicated route for "${path}".`);
|
|
22
|
+
return;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
// If component
|
|
23
|
-
if (!isLazyComponent(component)
|
|
25
|
+
// If component isn't lazy, make it lazy
|
|
26
|
+
if (!isLazyComponent(component)) {
|
|
27
|
+
/** @type {() => Promise<ComponentModule>} */
|
|
24
28
|
const wrappedComponent = lazy(() =>
|
|
25
29
|
Promise.resolve({
|
|
26
|
-
|
|
30
|
+
Page: component,
|
|
27
31
|
route: { preload, load },
|
|
28
32
|
})
|
|
29
33
|
);
|
|
@@ -32,25 +36,50 @@ export const Route = (routeProps) => {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
// Otherwise keep original component
|
|
39
|
+
// @ts-expect-error - RouteProps and RouteEntry are now equivalent
|
|
35
40
|
routes.push(routeProps);
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
44
|
* Find a registered route that matches the given path
|
|
40
|
-
* @param {string}
|
|
45
|
+
* @param {string} initialPath
|
|
41
46
|
* @returns {RouteEntry | null}
|
|
42
47
|
*/
|
|
43
|
-
export const matchRoute = (
|
|
44
|
-
const
|
|
45
|
-
|
|
48
|
+
export const matchRoute = (initialPath) => {
|
|
49
|
+
const path = initialPath !== "/" && initialPath.endsWith("/")
|
|
50
|
+
? initialPath.slice(0, -1)
|
|
51
|
+
: initialPath;
|
|
52
|
+
|
|
53
|
+
// First try exact match (excluding wildcards)
|
|
54
|
+
let foundMatch = routes.find((r) => r.path === path && !r.path.includes("*"));
|
|
55
|
+
|
|
56
|
+
// Then try nested wildcard match if no exact match found
|
|
57
|
+
if (!foundMatch) {
|
|
58
|
+
// Build the path for potential nested wildcard, e.g. /admin/* for /admin/articles
|
|
59
|
+
const nestedPath = path.split("/").slice(0, -1).join("/") + "/*";
|
|
60
|
+
foundMatch = routes.find((r) => r.path === nestedPath);
|
|
61
|
+
}
|
|
46
62
|
|
|
63
|
+
// If we found either an exact or nested wildcard match, return it with params
|
|
64
|
+
if (foundMatch) {
|
|
65
|
+
return {
|
|
66
|
+
...foundMatch,
|
|
67
|
+
params: extractParams(foundMatch.path, path) ?? /* istanbul ignore next */
|
|
68
|
+
undefined,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Try parameterized routes (like /users/:id)
|
|
47
73
|
for (const route of routes) {
|
|
74
|
+
// Skip the global catch-all
|
|
48
75
|
if (route.path === "*") continue;
|
|
49
76
|
const params = extractParams(route.path, path);
|
|
77
|
+
|
|
50
78
|
if (params) {
|
|
51
79
|
return { ...route, params };
|
|
52
80
|
}
|
|
53
81
|
}
|
|
54
82
|
|
|
83
|
+
// Finally, fallback to global catch-all route
|
|
55
84
|
return routes.find((r) => r.path === "*") || null;
|
|
56
85
|
};
|
package/router/state.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// router/state.js
|
|
2
2
|
import van from "vanjs-core";
|
|
3
|
-
import
|
|
3
|
+
import isServer from "../setup/isServer.mjs";
|
|
4
4
|
import { fixRouteUrl } from "./helpers.mjs";
|
|
5
5
|
|
|
6
|
-
const initialPath = !
|
|
7
|
-
const initialSearch = !
|
|
6
|
+
const initialPath = !isServer ? globalThis.location.pathname : "/";
|
|
7
|
+
const initialSearch = !isServer ? globalThis.location.search : "";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @type {typeof import("./types.d.ts").routerState}
|