vite-plugin-vanjs 0.1.6 → 0.1.7
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/client/index.mjs +2 -1
- package/package.json +1 -1
- package/plugin/index.mjs +2 -1
- package/router/a.mjs +1 -1
- package/router/extractParams.mjs +27 -0
- package/router/helpers.mjs +1 -79
- package/router/index.mjs +3 -0
- package/router/lazy.mjs +2 -1
- package/router/matchRoute.mjs +49 -0
- package/router/route.mjs +36 -0
- package/router/router.mjs +3 -2
- package/router/routes.mjs +0 -81
- package/router/state.mjs +13 -1
- package/router/unwrap.mjs +33 -0
- package/server/index.mjs +1 -4
- package/server/types.d.ts +3 -3
package/client/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { unwrap } from "../router/
|
|
1
|
+
import { unwrap } from "../router/unwrap.mjs";
|
|
2
2
|
import { getTagKey } from "../meta/helpers.mjs";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -118,6 +118,7 @@ function createHydrationContext() {
|
|
|
118
118
|
|
|
119
119
|
/** @type {(oldDom: HTMLElement, newDom: HTMLElement | HTMLElement[]) => HTMLElement} */
|
|
120
120
|
function diffAndHydrate(oldDom, newDom) {
|
|
121
|
+
if (!oldDom || !newDom) return;
|
|
121
122
|
// SPA mode
|
|
122
123
|
// istanbul ignore else
|
|
123
124
|
if (!oldDom.children.length && !elementsMatch(oldDom, newDom)) {
|
package/package.json
CHANGED
package/plugin/index.mjs
CHANGED
|
@@ -203,7 +203,8 @@ export default function VitePluginVanJS(options = {}) {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
const routesScript = `
|
|
206
|
-
import { Route
|
|
206
|
+
import { Route } from "@vanjs/router/route.mjs";
|
|
207
|
+
import { routes } from "@vanjs/router/routes.mjs";
|
|
207
208
|
import { lazy } from "@vanjs/router/lazy.mjs";
|
|
208
209
|
|
|
209
210
|
// Reset current routes
|
package/router/a.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// router/a.mjs
|
|
2
2
|
import van from "vanjs-core";
|
|
3
|
-
import { matchRoute } from "./
|
|
3
|
+
import { matchRoute } from "./matchRoute.mjs";
|
|
4
4
|
import { executeLifecycle, isCurrentPage, navigate } from "./helpers.mjs";
|
|
5
5
|
|
|
6
6
|
/** @typedef {typeof import("./types").A} A */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract route params
|
|
3
|
+
* @param {string} pattern
|
|
4
|
+
* @param {string} path
|
|
5
|
+
* @returns {Record<string, string> | null}
|
|
6
|
+
*/
|
|
7
|
+
export const extractParams = (pattern, path) => {
|
|
8
|
+
/** @type {Record<string, string>} */
|
|
9
|
+
const params = {};
|
|
10
|
+
const patternParts = pattern.split("/");
|
|
11
|
+
const pathParts = path.split("/");
|
|
12
|
+
|
|
13
|
+
if (patternParts.length !== pathParts.length) return null;
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
16
|
+
const patternPart = patternParts[i];
|
|
17
|
+
const pathPart = pathParts[i];
|
|
18
|
+
|
|
19
|
+
if (patternPart.startsWith(":")) {
|
|
20
|
+
params[patternPart.slice(1)] = pathPart;
|
|
21
|
+
} else if (patternPart !== pathPart) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return params;
|
|
27
|
+
};
|
package/router/helpers.mjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
// import van from "vanjs-core";
|
|
2
1
|
import isServer from "../setup/isServer.mjs";
|
|
3
2
|
import { routerState, setRouterState } from "./state.mjs";
|
|
4
|
-
import { matchRoute } from "./
|
|
3
|
+
import { matchRoute } from "./matchRoute.mjs";
|
|
5
4
|
|
|
6
|
-
/** @typedef {typeof import("./types.d.ts").unwrap} Unwrap */
|
|
7
5
|
/** @typedef {typeof import("./types.d.ts").navigate} Navigate */
|
|
8
6
|
/** @typedef {import("./types.d.ts").Route} Route */
|
|
9
7
|
/** @typedef {import("./types.d.ts").VanNode} VanNode */
|
|
@@ -21,41 +19,6 @@ export const isCurrentPage = (pageName) => {
|
|
|
21
19
|
return routerState.pathname.val === pageName;
|
|
22
20
|
};
|
|
23
21
|
|
|
24
|
-
/**
|
|
25
|
-
* Merge the children of an Element or an array of elements with an optional array of children
|
|
26
|
-
* into the childen prperty of a simple object.
|
|
27
|
-
* @type {Unwrap}
|
|
28
|
-
*/
|
|
29
|
-
export const unwrap = (source, ...children) => {
|
|
30
|
-
const layout = () => {
|
|
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];
|
|
44
|
-
|
|
45
|
-
// return van.tags.fragment(
|
|
46
|
-
// ...(children || /* istanbul ignore next */ []),
|
|
47
|
-
// ...pageChildren,
|
|
48
|
-
// );
|
|
49
|
-
return {
|
|
50
|
-
children: [
|
|
51
|
-
...(children || /* istanbul ignore next */ []),
|
|
52
|
-
...pageChildren,
|
|
53
|
-
],
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
return layout();
|
|
57
|
-
};
|
|
58
|
-
|
|
59
22
|
/**
|
|
60
23
|
* Check if component is a lazy component
|
|
61
24
|
* @param {ComponentFn | (() => LazyComponent)} component
|
|
@@ -121,47 +84,6 @@ export const navigate = (path, options = {}) => {
|
|
|
121
84
|
}
|
|
122
85
|
};
|
|
123
86
|
|
|
124
|
-
/**
|
|
125
|
-
* Extract route params
|
|
126
|
-
* @param {string} pattern
|
|
127
|
-
* @param {string} path
|
|
128
|
-
* @returns {Record<string, string> | null}
|
|
129
|
-
*/
|
|
130
|
-
export const extractParams = (pattern, path) => {
|
|
131
|
-
/** @type {Record<string, string>} */
|
|
132
|
-
const params = {};
|
|
133
|
-
const patternParts = pattern.split("/");
|
|
134
|
-
const pathParts = path.split("/");
|
|
135
|
-
|
|
136
|
-
if (patternParts.length !== pathParts.length) return null;
|
|
137
|
-
|
|
138
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
139
|
-
const patternPart = patternParts[i];
|
|
140
|
-
const pathPart = pathParts[i];
|
|
141
|
-
|
|
142
|
-
if (patternPart.startsWith(":")) {
|
|
143
|
-
params[patternPart.slice(1)] = pathPart;
|
|
144
|
-
} else if (patternPart !== pathPart) {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return params;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Fix the URL of a route
|
|
154
|
-
* @param {string=} url
|
|
155
|
-
* @returns
|
|
156
|
-
*/
|
|
157
|
-
export const fixRouteUrl = (url) => {
|
|
158
|
-
if (!url) return "/";
|
|
159
|
-
if (url.startsWith("/")) {
|
|
160
|
-
return url;
|
|
161
|
-
}
|
|
162
|
-
return `/${url}`;
|
|
163
|
-
};
|
|
164
|
-
|
|
165
87
|
/**
|
|
166
88
|
* Client only reload utility
|
|
167
89
|
* WORK IN PROGRESS
|
package/router/index.mjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export * from "./router.mjs";
|
|
2
2
|
export * from "./routes.mjs";
|
|
3
|
+
export * from "./route.mjs";
|
|
3
4
|
export * from "./a.mjs";
|
|
4
5
|
export * from "./state.mjs";
|
|
5
6
|
export * from "./lazy.mjs";
|
|
6
7
|
export * from "./helpers.mjs";
|
|
7
8
|
export * from "./cache.mjs";
|
|
9
|
+
export * from "./unwrap.mjs";
|
|
10
|
+
export * from "./extractParams.mjs";
|
package/router/lazy.mjs
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// router/matchRoute.mjs
|
|
2
|
+
import { extractParams } from "./extractParams.mjs";
|
|
3
|
+
import { routes } from "./routes.mjs";
|
|
4
|
+
|
|
5
|
+
/** @typedef {import("./types.d.ts").RouteEntry} RouteEntry */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Find a registered route that matches the given path
|
|
9
|
+
* @param {string} initialPath
|
|
10
|
+
* @returns {RouteEntry | null}
|
|
11
|
+
*/
|
|
12
|
+
export const matchRoute = (initialPath) => {
|
|
13
|
+
const path = initialPath !== "/" && initialPath.endsWith("/")
|
|
14
|
+
? initialPath.slice(0, -1)
|
|
15
|
+
: initialPath;
|
|
16
|
+
|
|
17
|
+
// First try exact match (excluding wildcards)
|
|
18
|
+
let foundMatch = routes.find((r) => r.path === path && !r.path.includes("*"));
|
|
19
|
+
|
|
20
|
+
// Then try nested wildcard match if no exact match found
|
|
21
|
+
if (!foundMatch) {
|
|
22
|
+
// Build the path for potential nested wildcard, e.g. /admin/* for /admin/articles
|
|
23
|
+
const nestedPath = path.split("/").slice(0, -1).join("/") + "/*";
|
|
24
|
+
foundMatch = routes.find((r) => r.path === nestedPath);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If we found either an exact or nested wildcard match, return it with params
|
|
28
|
+
if (foundMatch) {
|
|
29
|
+
return {
|
|
30
|
+
...foundMatch,
|
|
31
|
+
params: extractParams(foundMatch.path, path) ?? /* istanbul ignore next */
|
|
32
|
+
undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Try parameterized routes (like /users/:id)
|
|
37
|
+
for (const route of routes) {
|
|
38
|
+
// Skip the global catch-all
|
|
39
|
+
if (route.path === "*") continue;
|
|
40
|
+
const params = extractParams(route.path, path);
|
|
41
|
+
|
|
42
|
+
if (params) {
|
|
43
|
+
return { ...route, params };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Finally, fallback to global catch-all route
|
|
48
|
+
return routes.find((r) => r.path === "*") || null;
|
|
49
|
+
};
|
package/router/route.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// router/routes.mjs
|
|
2
|
+
import { isLazyComponent } from "./helpers.mjs";
|
|
3
|
+
import { routes } from "./routes.mjs";
|
|
4
|
+
import { lazy } from "./lazy.mjs";
|
|
5
|
+
/** @typedef {import("./types.d.ts").RouteProps} RouteProps */
|
|
6
|
+
/** @typedef {import("./types.d.ts").ComponentModule} ComponentModule */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {RouteProps} routeProps
|
|
10
|
+
*/
|
|
11
|
+
export const Route = (routeProps) => {
|
|
12
|
+
const { path, component, preload, load, ...rest } = routeProps;
|
|
13
|
+
|
|
14
|
+
// istanbul ignore next - no point testing this error
|
|
15
|
+
if (routes.some((r) => r.path === path)) {
|
|
16
|
+
console.error(`🍦 @vanjs/router: duplicated route for "${path}".`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// If component isn't lazy, make it lazy
|
|
21
|
+
if (!isLazyComponent(component)) {
|
|
22
|
+
/** @type {() => Promise<ComponentModule>} */
|
|
23
|
+
const wrappedComponent = lazy(() =>
|
|
24
|
+
Promise.resolve({
|
|
25
|
+
Page: component,
|
|
26
|
+
route: { preload, load },
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
routes.push({ ...rest, path, component: wrappedComponent });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Otherwise keep original component
|
|
34
|
+
// @ts-expect-error - RouteProps and RouteEntry are now equivalent
|
|
35
|
+
routes.push(routeProps);
|
|
36
|
+
};
|
package/router/router.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import van from "vanjs-core";
|
|
2
2
|
import isServer from "../setup/isServer.mjs";
|
|
3
3
|
import { routerState, setRouterState } from "./state.mjs";
|
|
4
|
-
import { matchRoute } from "./
|
|
5
|
-
import { executeLifecycle
|
|
4
|
+
import { matchRoute } from "./matchRoute.mjs";
|
|
5
|
+
import { executeLifecycle } from "./helpers.mjs";
|
|
6
|
+
import { unwrap } from "./unwrap.mjs";
|
|
6
7
|
import { hydrate } from "../client/index.mjs";
|
|
7
8
|
import { Head, initializeHeadTags } from "../meta/index.mjs";
|
|
8
9
|
|
package/router/routes.mjs
CHANGED
|
@@ -1,85 +1,4 @@
|
|
|
1
1
|
// router/routes.mjs
|
|
2
|
-
import { extractParams, isLazyComponent } from "./helpers.mjs";
|
|
3
|
-
import { lazy } from "./lazy.mjs";
|
|
4
|
-
|
|
5
2
|
/** @typedef {import("./types.d.ts").RouteEntry} RouteEntry */
|
|
6
|
-
/** @typedef {import("./types.d.ts").RouteProps} RouteProps */
|
|
7
|
-
/** @typedef {import("./types.d.ts").DynamicModule} DynamicModule */
|
|
8
|
-
/** @typedef {import("./types.d.ts").ComponentModule} ComponentModule */
|
|
9
|
-
|
|
10
3
|
/** @type {RouteEntry[]} */
|
|
11
4
|
export const routes = [];
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @param {RouteProps} routeProps
|
|
15
|
-
*/
|
|
16
|
-
export const Route = (routeProps) => {
|
|
17
|
-
const { path, component, preload, load, ...rest } = routeProps;
|
|
18
|
-
|
|
19
|
-
// istanbul ignore next - no point testing this error
|
|
20
|
-
if (routes.some((r) => r.path === path)) {
|
|
21
|
-
console.error(`🍦 @vanjs/router: duplicated route for "${path}".`);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// If component isn't lazy, make it lazy
|
|
26
|
-
if (!isLazyComponent(component)) {
|
|
27
|
-
/** @type {() => Promise<ComponentModule>} */
|
|
28
|
-
const wrappedComponent = lazy(() =>
|
|
29
|
-
Promise.resolve({
|
|
30
|
-
Page: component,
|
|
31
|
-
route: { preload, load },
|
|
32
|
-
})
|
|
33
|
-
);
|
|
34
|
-
routes.push({ ...rest, path, component: wrappedComponent });
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Otherwise keep original component
|
|
39
|
-
// @ts-expect-error - RouteProps and RouteEntry are now equivalent
|
|
40
|
-
routes.push(routeProps);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Find a registered route that matches the given path
|
|
45
|
-
* @param {string} initialPath
|
|
46
|
-
* @returns {RouteEntry | null}
|
|
47
|
-
*/
|
|
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
|
-
}
|
|
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)
|
|
73
|
-
for (const route of routes) {
|
|
74
|
-
// Skip the global catch-all
|
|
75
|
-
if (route.path === "*") continue;
|
|
76
|
-
const params = extractParams(route.path, path);
|
|
77
|
-
|
|
78
|
-
if (params) {
|
|
79
|
-
return { ...route, params };
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Finally, fallback to global catch-all route
|
|
84
|
-
return routes.find((r) => r.path === "*") || null;
|
|
85
|
-
};
|
package/router/state.mjs
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
// router/state.js
|
|
2
2
|
import van from "vanjs-core";
|
|
3
3
|
import isServer from "../setup/isServer.mjs";
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fix the URL of a route
|
|
7
|
+
* @param {string=} url
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export const fixRouteUrl = (url) => {
|
|
11
|
+
if (!url) return "/";
|
|
12
|
+
if (url.startsWith("/")) {
|
|
13
|
+
return url;
|
|
14
|
+
}
|
|
15
|
+
return `/${url}`;
|
|
16
|
+
};
|
|
5
17
|
|
|
6
18
|
const initialPath = !isServer ? globalThis.location.pathname : "/";
|
|
7
19
|
const initialSearch = !isServer ? globalThis.location.search : "";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** @typedef {typeof import("./types.d.ts").unwrap} Unwrap */
|
|
2
|
+
/** @typedef {import("./types.d.ts").VanNode} VanNode */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Merge the children of an Element or an array of elements with an optional array of children
|
|
6
|
+
* into the childen prperty of a simple object.
|
|
7
|
+
* @type {Unwrap}
|
|
8
|
+
*/
|
|
9
|
+
export const unwrap = (source, ...children) => {
|
|
10
|
+
const layout = () => {
|
|
11
|
+
/** @type {VanNode[]} */
|
|
12
|
+
const pageChildren =
|
|
13
|
+
source && typeof source === "object" && "children" in source &&
|
|
14
|
+
Array.isArray(source?.children)
|
|
15
|
+
? source.children
|
|
16
|
+
: typeof source === "function"
|
|
17
|
+
// @ts-expect-error - this case is specific to VanJS components in SSR
|
|
18
|
+
? [...source()?.children || source()]
|
|
19
|
+
: typeof HTMLElement !== "undefined" && source instanceof HTMLElement
|
|
20
|
+
? [...source.children]
|
|
21
|
+
: /* istanbul ignore next */ Array.isArray(source)
|
|
22
|
+
? source
|
|
23
|
+
: [source];
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
children: [
|
|
27
|
+
...(children || /* istanbul ignore next */ []),
|
|
28
|
+
...pageChildren,
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
return layout();
|
|
33
|
+
};
|
package/server/index.mjs
CHANGED
|
@@ -67,9 +67,6 @@ function renderPreloadLink(file) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
* @type {typeof import("./types.d.ts").renderPreloadLinks}
|
|
72
|
-
*/
|
|
73
70
|
/**
|
|
74
71
|
* @type {typeof import("./types.d.ts").renderPreloadLinks}
|
|
75
72
|
*/
|
|
@@ -82,7 +79,7 @@ export function renderPreloadLinks(modules, manifest) {
|
|
|
82
79
|
Object.entries(manifest).forEach(([id, files]) => {
|
|
83
80
|
// istanbul ignore else - don't pre-render routes, layouts and JSX stuff
|
|
84
81
|
if (
|
|
85
|
-
["src/pages", "src/routes", "vite-plugin-vanjs/
|
|
82
|
+
["src/pages", "src/routes", "vite-plugin-vanjs/"].some((l) =>
|
|
86
83
|
id.includes(l)
|
|
87
84
|
)
|
|
88
85
|
) {
|
package/server/types.d.ts
CHANGED
|
@@ -21,11 +21,11 @@ type ValidVanNode =
|
|
|
21
21
|
| VanElement
|
|
22
22
|
| TagFunc;
|
|
23
23
|
|
|
24
|
-
type
|
|
24
|
+
type VComponent = () => HTMLElement | ValidVanNode | ValidVanNode[];
|
|
25
25
|
export type Source =
|
|
26
26
|
| Promise<ValidVanNode>
|
|
27
|
-
|
|
|
28
|
-
| (() =>
|
|
27
|
+
| VComponent
|
|
28
|
+
| (() => VComponent)
|
|
29
29
|
| ValidVanNode
|
|
30
30
|
| ValidVanNode[]
|
|
31
31
|
| undefined;
|