vite-plugin-vanjs 0.0.9 → 0.0.10
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 +13 -17
- package/package.json +8 -7
- package/router/a.mjs +1 -1
- package/router/global.d.ts +6 -1
- package/router/lazy.mjs +1 -1
- package/router/router.mjs +95 -9
- package/router/routes.mjs +8 -3
- package/router/types.d.ts +4 -1
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
[](https://www.typescriptlang.org/)
|
|
8
8
|
[](https://github.com/vanjs-org/van)
|
|
9
9
|
[](https://github.com/vanjs-org/mini-van-plate)
|
|
10
|
-
[](https://www.vitest.dev/)
|
|
11
|
+
[](https://vite.dev)
|
|
12
12
|
|
|
13
13
|
A mini meta-framework for [VanJS](https://vanjs.org/) developed around the awesome [Vite](https://vite.dev). The plugin comes with a set of modules to simplify your workflow:
|
|
14
14
|
* ***@vanjs/router*** - one of the most important part of an application which allows you to split code and lazy load page like components with ease, handles both Client Side Rendering (SSR) and Server Side Rendering (CSR) and makes it really easy to work with;
|
|
@@ -34,19 +34,19 @@ Also in _development_ mode, the plugin will load the `van.debug.js` module to he
|
|
|
34
34
|
1) Install the plugin:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
npm install vite-plugin-vanjs
|
|
37
|
+
npm install vite-plugin-vanjs@latest
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
pnpm add vite-plugin-vanjs
|
|
41
|
+
pnpm add vite-plugin-vanjs@latest
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
deno add npm:vite-plugin-vanjs
|
|
45
|
+
deno add npm:vite-plugin-vanjs@latest
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
bun
|
|
49
|
+
bun add vite-plugin-vanjs@latest
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
|
|
@@ -130,12 +130,14 @@ Route({ path: '*', component: () => van.tags.div('404 - Page Not Found') });
|
|
|
130
130
|
|
|
131
131
|
function App() {
|
|
132
132
|
// the Router is only an outlet
|
|
133
|
-
|
|
133
|
+
return Router();
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
// render
|
|
136
|
+
// render the app
|
|
137
137
|
van.add(document.body, App());
|
|
138
138
|
```
|
|
139
|
+
**IMPORTANT** - using lazy components for the main route (`"/"`) is not allowed. This is to prevent hydration waterfalls, especially for SSR apps.
|
|
140
|
+
|
|
139
141
|
|
|
140
142
|
Here's how a page should look like, pay attention to the comments:
|
|
141
143
|
```ts
|
|
@@ -179,23 +181,17 @@ The **vite-plugin-vanjs** plugin provides metadata management via the exported `
|
|
|
179
181
|
|
|
180
182
|
Depending on the type of application, it's generally very easy to setup the system to work properly, we only need to make sure that we execute functions in a specific order:
|
|
181
183
|
|
|
182
|
-
* first we
|
|
183
|
-
*
|
|
184
|
-
* lastly, on other pages, we define tags that override both the existing and the default tags.
|
|
184
|
+
* first we define a set of default tags to be used by both client and server (if using an SSR template) on all pages that don't come with any metadata tags;
|
|
185
|
+
* next, on other pages, we define tags that override both the existing and the default tags.
|
|
185
186
|
|
|
186
187
|
Here's a quick example, first let's start again with the `app.ts`:
|
|
187
188
|
```ts
|
|
188
189
|
import van from 'vanjs-core'
|
|
189
|
-
import { Style, Title,
|
|
190
|
+
import { Style, Title, Link, Meta } from '@vanjs/meta'
|
|
190
191
|
|
|
191
192
|
function App() {
|
|
192
193
|
const { div, h1, p } van.tags;
|
|
193
194
|
|
|
194
|
-
// when app is first time executed
|
|
195
|
-
// we need to register tags coming
|
|
196
|
-
// from the index.html template
|
|
197
|
-
initializeHeadTags();
|
|
198
|
-
|
|
199
195
|
// a good practice is to define some default tags
|
|
200
196
|
// they are used on pages where no tags are set
|
|
201
197
|
Title("VanJS + Vite App");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-vanjs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"author": "thednp",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A mini meta-framework for VanJS powered by Vite",
|
|
@@ -87,15 +87,16 @@
|
|
|
87
87
|
"vanjs-ext": "^0.6.2"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
|
-
"@types/node": "^22.
|
|
91
|
-
"@vitest/browser": "^3.
|
|
92
|
-
"@vitest/coverage-istanbul": "^3.
|
|
93
|
-
"@vitest/ui": "^3.
|
|
90
|
+
"@types/node": "^22.14.0",
|
|
91
|
+
"@vitest/browser": "^3.1.1",
|
|
92
|
+
"@vitest/coverage-istanbul": "^3.1.1",
|
|
93
|
+
"@vitest/ui": "^3.1.1",
|
|
94
94
|
"happy-dom": "^16.8.1",
|
|
95
95
|
"typescript": "5.6.2",
|
|
96
|
-
"vite": "^6.2.
|
|
97
|
-
"vitest": "^3.
|
|
96
|
+
"vite": "^6.2.5",
|
|
97
|
+
"vitest": "^3.1.1"
|
|
98
98
|
},
|
|
99
|
+
"packageManager": "pnpm@8.6.12",
|
|
99
100
|
"engines": {
|
|
100
101
|
"node": ">=20",
|
|
101
102
|
"pnpm": ">=8.6.0"
|
package/router/a.mjs
CHANGED
|
@@ -15,7 +15,7 @@ export const A = (
|
|
|
15
15
|
) => {
|
|
16
16
|
/* istanbul ignore next - try again later */
|
|
17
17
|
const props = Object.fromEntries(
|
|
18
|
-
Object.entries(rest || {}).filter(([_, val]) => val),
|
|
18
|
+
Object.entries(rest || {}).filter(([_, val]) => val !== undefined),
|
|
19
19
|
);
|
|
20
20
|
const newProps = {
|
|
21
21
|
href,
|
package/router/global.d.ts
CHANGED
|
@@ -129,7 +129,12 @@ declare module "@vanjs/router" {
|
|
|
129
129
|
|
|
130
130
|
export type RouteProps = {
|
|
131
131
|
path: string;
|
|
132
|
-
component:
|
|
132
|
+
component:
|
|
133
|
+
| (() =>
|
|
134
|
+
| HTMLElementTagNameMap[unknown]
|
|
135
|
+
| HTMLElementTagNameMap[unknown][])
|
|
136
|
+
| VanComponent
|
|
137
|
+
| (() => ComponentModule);
|
|
133
138
|
preload?: (params?: Record<string, string>) => void;
|
|
134
139
|
load?: (params?: Record<string, string>) => void;
|
|
135
140
|
};
|
package/router/lazy.mjs
CHANGED
package/router/router.mjs
CHANGED
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
import van from "vanjs-core";
|
|
2
2
|
import setup from "../setup/index.mjs";
|
|
3
|
-
import { routerState } from "./state.mjs";
|
|
3
|
+
import { routerState, setRouterState } from "./state.mjs";
|
|
4
4
|
import { matchRoute } from "./routes.mjs";
|
|
5
5
|
import { executeLifecycle, unwrap } from "./helpers.mjs";
|
|
6
|
+
import { hydrate } from "../client/index.mjs";
|
|
7
|
+
import { Head, initializeHeadTags } from "@vanjs/meta";
|
|
8
|
+
|
|
9
|
+
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
|
+
};
|
|
6
20
|
|
|
7
21
|
export const Router = (initialProps = /* istanbul ignore next */ {}) => {
|
|
8
22
|
const { div, main } = van.tags;
|
|
23
|
+
|
|
9
24
|
/* istanbul ignore next - try again later */
|
|
10
25
|
const props = Object.fromEntries(
|
|
11
|
-
Object.entries(initialProps).filter(([_, val]) => val),
|
|
26
|
+
Object.entries(initialProps).filter(([_, val]) => val !== undefined),
|
|
12
27
|
);
|
|
13
|
-
const wrapper = main(props);
|
|
28
|
+
const wrapper = main({ ...props, "data-root": true });
|
|
14
29
|
const mainLayout = () => {
|
|
15
30
|
const route = matchRoute(routerState.pathname.val);
|
|
16
31
|
/* istanbul ignore else */
|
|
@@ -22,7 +37,9 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
|
|
|
22
37
|
const renderComponent = async () => {
|
|
23
38
|
try {
|
|
24
39
|
const module = await route.component();
|
|
25
|
-
const component = module.component
|
|
40
|
+
const component = typeof module.component === "function"
|
|
41
|
+
? module.component()
|
|
42
|
+
: /* istanbul ignore next */ module.component;
|
|
26
43
|
|
|
27
44
|
await executeLifecycle(module, route.params);
|
|
28
45
|
return van.add(wrapper, unwrap(component).children);
|
|
@@ -37,13 +54,82 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
|
|
|
37
54
|
return renderComponent();
|
|
38
55
|
}
|
|
39
56
|
|
|
57
|
+
const root = document.querySelector("[data-root]");
|
|
58
|
+
// istanbul ignore else - cannot test unmount
|
|
59
|
+
if (isConnected || !root) {
|
|
60
|
+
initializeHeadTags();
|
|
61
|
+
globalThis.addEventListener("popstate", popHandler);
|
|
62
|
+
} else {
|
|
63
|
+
globalThis.removeEventListener("popstate", popHandler);
|
|
64
|
+
}
|
|
65
|
+
|
|
40
66
|
// Client-side lazy component, lifeCycle is already executed on the server
|
|
41
67
|
// or when A component has been clicked in the client
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
68
|
+
if (root) {
|
|
69
|
+
// this case is when root is server side rendered
|
|
70
|
+
const module = route.component();
|
|
71
|
+
const children = () => {
|
|
72
|
+
// istanbul ignore next - cannot test
|
|
73
|
+
const cp = (Array.isArray(module) || module instanceof Element)
|
|
74
|
+
? module
|
|
75
|
+
: typeof module.component === "function"
|
|
76
|
+
? module.component()
|
|
77
|
+
: module.component;
|
|
78
|
+
// istanbul ignore next - cannot test
|
|
79
|
+
const kids = () => cp ? Array.from(unwrap(cp).children) : [];
|
|
80
|
+
const kudos = kids();
|
|
81
|
+
|
|
82
|
+
// istanbul ignore else
|
|
83
|
+
if (document.head) {
|
|
84
|
+
isConnected = true;
|
|
85
|
+
van.hydrate(document.head, (head) => hydrate(head, Head()));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return kudos;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return van.add(wrapper, ...children());
|
|
92
|
+
}
|
|
93
|
+
// this case is when root is for SPA apps
|
|
94
|
+
const csrRoute = van.derive(() => {
|
|
95
|
+
const p = routerState.pathname.val;
|
|
96
|
+
return matchRoute(p);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const children = van.derive(() => {
|
|
100
|
+
const md = csrRoute.val.component();
|
|
101
|
+
// istanbul ignore next - cannot test all cases
|
|
102
|
+
const cp = (Array.isArray(md) || md instanceof Element)
|
|
103
|
+
? md
|
|
104
|
+
: typeof md.component === "function"
|
|
105
|
+
? md.component()
|
|
106
|
+
: md.component;
|
|
107
|
+
return cp
|
|
108
|
+
? Array.from(unwrap(cp).children)
|
|
109
|
+
: /* istanbul ignore next */ [];
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const component = () => {
|
|
113
|
+
const kids = () => children.val;
|
|
114
|
+
const result = () => {
|
|
115
|
+
return van.derive(() =>
|
|
116
|
+
van.hydrate(wrapper, (el) => {
|
|
117
|
+
const kudos = kids();
|
|
118
|
+
isConnected = true;
|
|
119
|
+
// istanbul ignore else
|
|
120
|
+
if (document.head) {
|
|
121
|
+
van.hydrate(document.head, (head) => hydrate(head, Head()));
|
|
122
|
+
}
|
|
123
|
+
return hydrate(el, kudos);
|
|
124
|
+
})
|
|
125
|
+
).val;
|
|
126
|
+
};
|
|
127
|
+
return result();
|
|
128
|
+
};
|
|
129
|
+
const finalResult = component();
|
|
130
|
+
return finalResult
|
|
131
|
+
? /* istanbul ignore next*/ van.add(wrapper, finalResult)
|
|
132
|
+
: wrapper;
|
|
47
133
|
};
|
|
48
134
|
|
|
49
135
|
return mainLayout();
|
package/router/routes.mjs
CHANGED
|
@@ -12,17 +12,22 @@ export const routes = [];
|
|
|
12
12
|
* @param {RouteProps} routeProps
|
|
13
13
|
*/
|
|
14
14
|
export const Route = (routeProps) => {
|
|
15
|
-
const { component, preload, load, ...rest } = routeProps;
|
|
15
|
+
const { path, component, preload, load, ...rest } = routeProps;
|
|
16
|
+
|
|
17
|
+
// istanbul ignore next - no point testing this error
|
|
18
|
+
if (path === "/" && isLazyComponent(component)) {
|
|
19
|
+
throw new Error("Home component must not be a lazy component");
|
|
20
|
+
}
|
|
16
21
|
|
|
17
22
|
// If component has lifecycle methods but isn't lazy, make it lazy
|
|
18
|
-
if (!isLazyComponent(component)) {
|
|
23
|
+
if (!isLazyComponent(component) && path !== "/") {
|
|
19
24
|
const wrappedComponent = lazy(() =>
|
|
20
25
|
Promise.resolve({
|
|
21
26
|
default: component,
|
|
22
27
|
route: { preload, load },
|
|
23
28
|
})
|
|
24
29
|
);
|
|
25
|
-
routes.push({ ...rest, component: wrappedComponent });
|
|
30
|
+
routes.push({ ...rest, path, component: wrappedComponent });
|
|
26
31
|
return;
|
|
27
32
|
}
|
|
28
33
|
|
package/router/types.d.ts
CHANGED
|
@@ -129,7 +129,10 @@ export type RouteEntry = {
|
|
|
129
129
|
|
|
130
130
|
export type RouteProps = {
|
|
131
131
|
path: string;
|
|
132
|
-
component:
|
|
132
|
+
component:
|
|
133
|
+
| (() => HTMLElementTagNameMap[unknown] | HTMLElementTagNameMap[unknown][])
|
|
134
|
+
| VanComponent
|
|
135
|
+
| (() => ComponentModule);
|
|
133
136
|
preload?: (params?: Record<string, string>) => void;
|
|
134
137
|
load?: (params?: Record<string, string>) => void;
|
|
135
138
|
};
|