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 CHANGED
@@ -7,8 +7,8 @@
7
7
  [![typescript version](https://img.shields.io/badge/typescript-5.6.2-brightgreen)](https://www.typescriptlang.org/)
8
8
  [![vanjs-core version](https://img.shields.io/badge/vanjs--core-1.5.3-brightgreen)](https://github.com/vanjs-org/van)
9
9
  [![mini-van-plate version](https://img.shields.io/badge/mini--van--plate-0.6.3-brightgreen)](https://github.com/vanjs-org/mini-van-plate)
10
- [![vitest version](https://img.shields.io/badge/vitest-3.0.9-brightgreen)](https://www.vitest.dev/)
11
- [![vite version](https://img.shields.io/badge/vite-6.2.2-brightgreen)](https://vite.dev)
10
+ [![vitest version](https://img.shields.io/badge/vitest-3.1.1-brightgreen)](https://www.vitest.dev/)
11
+ [![vite version](https://img.shields.io/badge/vite-6.2.5-brightgreen)](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 install vite-plugin-vanjs
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
- export default Route();
133
+ return Router();
134
134
  }
135
135
 
136
- // render or hydrate the app
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 call the `initializeHeadTags` which allows the module to store the current tags that come from either a Vite starter template `index.html` or the server (SSR); if we don't call this function, the existing tags will be removed and some of them are very important (especially `charset` and `viewport`);
183
- * then 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;
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, Meta, initializeHeadTags } from '@vanjs/meta'
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.9",
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.13.10",
91
- "@vitest/browser": "^3.0.9",
92
- "@vitest/coverage-istanbul": "^3.0.9",
93
- "@vitest/ui": "^3.0.9",
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.2",
97
- "vitest": "^3.0.9"
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,
@@ -129,7 +129,12 @@ declare module "@vanjs/router" {
129
129
 
130
130
  export type RouteProps = {
131
131
  path: string;
132
- component: VanComponent | (() => ComponentModule);
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
@@ -28,7 +28,7 @@ export const lazy = (importFn) => {
28
28
  }
29
29
 
30
30
  let initialized = false;
31
- const component = van.state(() => "");
31
+ const component = van.state(() => "Loading..");
32
32
  const route = van.state({});
33
33
 
34
34
  const load = () => {
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
- const module = route.component();
43
- const children = module.component
44
- ? Array.from(unwrap(module.component).children)
45
- : [];
46
- return children.length ? van.add(wrapper, ...children) : wrapper;
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: VanComponent | (() => ComponentModule);
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
  };