vite-plugin-vanjs 0.1.22 → 0.1.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-vanjs",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "author": "thednp",
5
5
  "license": "MIT",
6
6
  "description": "An async first mini meta-framework for VanJS powered by Vite",
@@ -0,0 +1,8 @@
1
+ const ENV = import.meta.env;
2
+ const SSR = ENV.SSR;
3
+ const DEV = ENV.DEV;
4
+ const PROD = ENV.PROD;
5
+ const MODE = ENV.MODE;
6
+ const BASE_URL = ENV.BASE_URL;
7
+
8
+ export { BASE_URL, DEV, MODE, PROD, SSR };
@@ -218,28 +218,28 @@ export const generateRouteProloaders = (route) => {
218
218
  const layoutName = "Module";
219
219
 
220
220
  return `{
221
- preload: async (params) => {
222
- ${
221
+ preload: async () => {
222
+ ${
223
223
  route.layouts.map((layout) =>
224
224
  `if (${layout.id + layoutName}?.route?.preload) await ${
225
225
  layout.id + layoutName
226
- }?.route?.preload(params);`
227
- ).join("\n ")
226
+ }?.route?.preload();`
227
+ ).join("\n ")
228
228
  }
229
- if (${moduleName}?.route?.preload) await ${moduleName}?.route?.preload(params);
230
- },
231
- load: async (params) => {
232
- let _data;
233
- ${
229
+ if (${moduleName}?.route?.preload) await ${moduleName}?.route?.preload();
230
+ },
231
+ load: async () => {
232
+ let _data;
233
+ ${
234
234
  route.layouts.map((layout) =>
235
235
  `if (${layout.id + layoutName}?.route?.load) await ${
236
236
  layout.id + layoutName
237
- }?.route?.load(params);`
238
- ).join("\n ")
239
- }
240
- if (${moduleName}?.route?.load) _data = await ${moduleName}?.route?.load(params);
241
- return _data;
237
+ }?.route?.load();`
238
+ ).join("\n ")
242
239
  }
240
+ if (${moduleName}?.route?.load) _data = await ${moduleName}?.route?.load();
241
+ return _data;
242
+ }
243
243
  }`;
244
244
  };
245
245
 
@@ -139,12 +139,8 @@ declare module "@vanjs/router" {
139
139
  path: string;
140
140
  component: () => Promise<ComponentModule>;
141
141
  params?: Record<string, string>;
142
- preload?: (
143
- params?: Record<string, string>,
144
- ) => boolean | void | Promise<boolean | void>;
145
- load?: (
146
- params?: Record<string, string>,
147
- ) => boolean | void | Promise<boolean | void>;
142
+ preload?: () => boolean | void | Promise<boolean | void>;
143
+ load?: () => boolean | void | Promise<boolean | void>;
148
144
  };
149
145
 
150
146
  export type ImportFn = () => LazyComponent;
@@ -157,12 +153,8 @@ declare module "@vanjs/router" {
157
153
  | VanComponent
158
154
  | ComponentFn
159
155
  | (() => Promise<ComponentModule>);
160
- preload?: (
161
- params?: Record<string, string>,
162
- ) => boolean | void | Promise<boolean | void>;
163
- load?: (
164
- params?: Record<string, string>,
165
- ) => boolean | void | Promise<boolean | void>;
156
+ preload?: () => boolean | void | Promise<boolean | void>;
157
+ load?: () => boolean | void | Promise<boolean | void>;
166
158
  };
167
159
 
168
160
  export type RouteConfig = {
@@ -191,7 +183,7 @@ declare module "@vanjs/router" {
191
183
  // state.mjs
192
184
  export type RouterState = {
193
185
  pathname: string;
194
- searchParams: URLSearchParams;
186
+ searchParams: string;
195
187
  params: Record<string, string>;
196
188
  status: "idle" | "pending" | "success" | "error";
197
189
  };
@@ -293,10 +285,9 @@ declare module "@vanjs/router" {
293
285
  */
294
286
  export const executeLifecycle: (
295
287
  route: RouteEntry | {
296
- preload?: (params?: Record<string, string>) => unknown;
297
- load?: (params?: Record<string, string>) => unknown;
288
+ preload?: () => unknown;
289
+ load?: () => unknown;
298
290
  } | null,
299
- params: Record<string, string> | undefined,
300
291
  ) => Promise<boolean>;
301
292
 
302
293
  /**
@@ -304,8 +295,7 @@ declare module "@vanjs/router" {
304
295
  */
305
296
  export const matchRoute: (path: string) => RouteEntry | null;
306
297
 
307
- /**
308
- * Convenience hook to get the current route's cached data.
298
+ /** * Convenience hook to get the current route's cached data.
309
299
  */
310
300
  export const useRouteData: <T>() => T | undefined;
311
301
 
@@ -1,15 +1,32 @@
1
1
  import isServer from "../setup/isServer.mjs";
2
2
  import { routerState, setRouterState } from "./state.mjs";
3
3
  import { matchRoute } from "./matchRoute.mjs";
4
+ import { unwrap } from "./unwrap.mjs";
4
5
  import * as dataCache from "./dataCache.mjs";
5
6
 
6
7
  /** @typedef {typeof import("./types.d.ts").navigate} Navigate */
8
+ /** @typedef {import("./types.d.ts").SearchParamDef} SearchParamDef */
7
9
  /** @typedef {import("./types.d.ts").Route} Route */
8
10
  /** @typedef {import("./types.d.ts").VanNode} VanNode */
9
11
  /** @typedef {import("./types.d.ts").ComponentModule} ComponentModule */
10
12
  /** @typedef {import("./types.d.ts").ComponentFn} ComponentFn */
11
13
  /** @typedef {import("./types.d.ts").LazyComponent} LazyComponent */
12
14
 
15
+ /**
16
+ * Resolve component children from a module
17
+ * @param {ComponentModule | Element | Element[] | any} module
18
+ * @returns {VanNode[]}
19
+ */
20
+ export const resolveChildren = (module) => {
21
+ const isElement = typeof Element !== "undefined" && module instanceof Element;
22
+ const cp = (Array.isArray(module) || isElement)
23
+ ? module
24
+ : typeof module.component === "function"
25
+ ? module.component()
26
+ : module.component;
27
+ return cp ? Array.from(unwrap(cp).children) : /* istanbul ignore next */ [];
28
+ };
29
+
13
30
  /**
14
31
  * Returns the HREF string value
15
32
  * @param {unknown} v
@@ -19,13 +36,25 @@ export const getValue = (v) => {
19
36
  return typeof v === "function" ? v() : v.rawVal ? v.val : v;
20
37
  };
21
38
 
39
+ export const getCacheKey = () => {
40
+ const params = routerState.params;
41
+ const search = routerState.searchParams;
42
+ return Object.keys(params).length === 0
43
+ ? search || ""
44
+ : JSON.stringify(params) + (search ? `&${search}` : "");
45
+ };
46
+
22
47
  /**
23
48
  * Check if selected page is the current page;
24
49
  * @param {string} pageName
25
50
  * @returns {boolean}
26
51
  */
27
52
  export const isCurrentPage = (pageName) => {
28
- return routerState.pathname === getValue(pageName);
53
+ const href = getValue(pageName);
54
+ if (isServer) return routerState.pathname === href;
55
+ const url = new URL(href, globalThis.location.origin);
56
+ return routerState.pathname === url.pathname &&
57
+ routerState.searchParams === url.searchParams.toString();
29
58
  };
30
59
 
31
60
  /**
@@ -53,10 +82,9 @@ export const isLazyComponent = (component) => {
53
82
  /**
54
83
  * Execute lifecycle methods preload and / or load
55
84
  * @param {import("./types.d.ts").RouteEntry} route
56
- * @param {Record<string, string> | undefined} params
57
85
  * @returns {Promise<boolean>}
58
86
  */
59
- export const executeLifecycle = async (route, params) => {
87
+ export const executeLifecycle = async (route) => {
60
88
  // istanbul ignore next
61
89
  try {
62
90
  if (!route) return true;
@@ -66,13 +94,11 @@ export const executeLifecycle = async (route, params) => {
66
94
  const load = route.load;
67
95
 
68
96
  const pathname = routerState.pathname;
69
- const cacheKey = Object.keys(params || {}).length === 0
70
- ? ""
71
- : JSON.stringify(params);
97
+ const cacheKey = getCacheKey();
72
98
 
73
- if (preload) await preload(params);
99
+ if (preload) await preload();
74
100
  if (!data && load && !dataCache.has(pathname, cacheKey)) {
75
- data = await load(params);
101
+ data = await load();
76
102
  } else if (load && dataCache.has(pathname, cacheKey)) {
77
103
  dataCache.touch(pathname);
78
104
  }
@@ -102,8 +128,7 @@ export const executeLifecycle = async (route, params) => {
102
128
  * @returns {any | undefined}
103
129
  */
104
130
  export const useRouteData = () => {
105
- const params = routerState.params;
106
- const key = Object.keys(params).length === 0 ? "" : JSON.stringify(params);
131
+ const key = getCacheKey();
107
132
  return dataCache.get(routerState.pathname, key)?.data;
108
133
  };
109
134
 
package/router/router.mjs CHANGED
@@ -1,33 +1,20 @@
1
1
  import van from "vanjs-core";
2
2
  import isServer from "../setup/isServer.mjs";
3
+ import { MODE } from "../plugin/const.mjs";
3
4
  import { routerState, setRouterState } from "./state.mjs";
4
5
  import { matchRoute } from "./matchRoute.mjs";
5
- import { executeLifecycle } from "./helpers.mjs";
6
- import { unwrap } from "./unwrap.mjs";
6
+ import { executeLifecycle, resolveChildren } from "./helpers.mjs";
7
7
  import { hydrate } from "../client/index.mjs";
8
8
  import { Head, initializeHeadTags } from "../meta/index.mjs";
9
9
  import * as dataCache from "./dataCache.mjs";
10
10
  import "virtual:@vanjs/routes";
11
11
 
12
+ const isDev = MODE === "development";
13
+
12
14
  /** @typedef {import("./types.d.ts").ComponentModule} ComponentModule */
13
15
  /** @typedef {import("./types.d.ts").RouteEntry} RouteEntry */
14
16
  /** @typedef {import("./types.d.ts").VanNode} VanNode */
15
17
 
16
- /**
17
- * Resolve component children from a module
18
- * @param {ComponentModule | Element | Element[] | any} module
19
- * @returns {VanNode[]}
20
- */
21
- const resolveChildren = (module) => {
22
- const isElement = typeof Element !== "undefined" && module instanceof Element;
23
- const cp = (Array.isArray(module) || isElement)
24
- ? module
25
- : typeof module.component === "function"
26
- ? module.component()
27
- : module.component;
28
- return cp ? Array.from(unwrap(cp).children) : /* istanbul ignore next */ [];
29
- };
30
-
31
18
  /**
32
19
  * Update head tags
33
20
  */
@@ -38,6 +25,26 @@ const updateHead = () => {
38
25
  }
39
26
  };
40
27
 
28
+ /**
29
+ * @param {RouteEntry} route
30
+ * @param {HTMLElement} wrapper
31
+ * @param {boolean} ssr
32
+ * @returns
33
+ */
34
+ const executeModule = async (route, wrapper, ssr) => {
35
+ // 1. Resolve the module first (to get route lifecycle hooks)
36
+ const module = await route.component();
37
+ // 2. Execute lifecycle
38
+ await executeLifecycle(module.route);
39
+ // 3. Resolve children
40
+ const children = resolveChildren(module);
41
+ // 4. Update <head> in the client
42
+ if (!isServer) updateHead();
43
+ // 5. Update / replace children in wrapper
44
+ if (ssr) return van.add(wrapper, ...children);
45
+ else wrapper.replaceChildren(...children);
46
+ };
47
+
41
48
  /**
42
49
  * Initialize client-side router (Head + popstate listener)
43
50
  */
@@ -53,8 +60,10 @@ const initClient = () => {
53
60
  (e) => {
54
61
  const location = e.target.location;
55
62
  const oldPath = routerState.pathname;
63
+ const oldSearch = routerState.searchParams;
64
+ const newSearch = new URLSearchParams(location.search).toString();
56
65
  // istanbul ignore next - cannot test
57
- if (location.pathname !== oldPath) {
66
+ if (location.pathname !== oldPath || newSearch !== oldSearch) {
58
67
  setRouterState(location.pathname, location.search);
59
68
  }
60
69
  },
@@ -69,25 +78,18 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
69
78
  );
70
79
  const wrapper = main({ ...props, "data-root": "" });
71
80
  const route = matchRoute(routerState.pathname);
81
+ let _searchParams = routerState.searchParams;
72
82
 
73
83
  /* istanbul ignore else */
74
84
  if (!route) return van.add(wrapper, div("No Route Found"));
75
-
85
+ // It's important to READ the params
76
86
  Object.assign(routerState.params, route.params || {});
77
87
 
78
88
  // Server-side rendering
79
89
  if (isServer) {
80
90
  return async () => {
81
91
  try {
82
- // 1. Resolve the module first (to get route lifecycle hooks)
83
- const module = await route.component();
84
-
85
- // 2. Execute lifecycle
86
- await executeLifecycle(module.route, route.params);
87
- const children = resolveChildren(module);
88
-
89
- // 3. Render the component (data is now in dataCache)
90
- return van.add(wrapper, ...children);
92
+ return await executeModule(route, wrapper, true);
91
93
  } catch (error) {
92
94
  /* istanbul ignore next */
93
95
  console.error("Router error:", error);
@@ -98,11 +100,12 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
98
100
  }
99
101
 
100
102
  // Init client here
101
- if (!isServer) initClient();
103
+ initClient();
104
+ let initialized = false;
102
105
 
103
106
  // Client-side: hydrate data cache from SSR output
104
107
  // This must happen BEFORE any component renders so useRouteData() works
105
- if (globalThis.__DATA_CACHE) {
108
+ if (globalThis.__DATA_CACHE && !isDev) {
106
109
  dataCache.hydrateFromJSON(globalThis.__DATA_CACHE);
107
110
  }
108
111
 
@@ -110,30 +113,36 @@ export const Router = (initialProps = /* istanbul ignore next */ {}) => {
110
113
  const root = document.querySelector("[data-root]");
111
114
 
112
115
  if (root) {
116
+ van.derive(() => {
117
+ _searchParams = routerState.searchParams;
118
+ if (!initialized) return;
119
+ const matchedRoute = matchRoute(routerState.pathname);
120
+ if (!matchedRoute) {
121
+ wrapper.replaceChildren(div("No Route Found"));
122
+ return;
123
+ }
124
+ (async () => {
125
+ initialized = true;
126
+ await executeModule(matchedRoute, wrapper);
127
+ })();
128
+ });
113
129
  return async () => {
114
- const module = await route.component();
115
-
116
- await executeLifecycle(module.route, route.params);
117
- const children = resolveChildren(module);
118
- updateHead();
119
- return van.add(wrapper, ...children);
130
+ return await executeModule(route, wrapper, true);
120
131
  };
121
132
  }
122
133
 
123
134
  // Pure SPA path - reactive routing
124
135
  van.derive(() => {
125
136
  const matchedRoute = matchRoute(routerState.pathname);
137
+ _searchParams = routerState.searchParams;
138
+ // routerState.searchParams;
126
139
  if (!matchedRoute) {
127
140
  wrapper.replaceChildren(div("No Route Found"));
128
141
  return;
129
142
  }
130
143
 
131
144
  (async () => {
132
- const module = await matchedRoute.component();
133
- await executeLifecycle(module.route, matchedRoute.params);
134
- const children = resolveChildren(module);
135
- wrapper.replaceChildren(...children);
136
- updateHead();
145
+ await executeModule(matchedRoute, wrapper);
137
146
  })();
138
147
  });
139
148
 
package/router/state.mjs CHANGED
@@ -18,6 +18,16 @@ export const fixRouteUrl = (url) => {
18
18
  const initialPath = !isServer ? globalThis.location.pathname : "/";
19
19
  const initialSearch = !isServer ? globalThis.location.search : "";
20
20
 
21
+ /**
22
+ * Normalize a search string to a plain string (strip leading "?")
23
+ * @param {string} search
24
+ * @returns {string}
25
+ */
26
+ const normalizeSearch = (search) => {
27
+ if (!search) return "";
28
+ return search.startsWith("?") ? search.slice(1) : search;
29
+ };
30
+
21
31
  const STATE_PROXY = "_proxy";
22
32
  const proxyProps = {
23
33
  value: 1,
@@ -72,7 +82,7 @@ export function microStore(init) {
72
82
  target[prop] = defineProxy(sp, sv, {});
73
83
  }
74
84
  } else if (isPlainObject) {
75
- target[prop] = value;
85
+ defineProxy(prop, value, target);
76
86
  } else if (!Array.isArray(value) && value != null) {
77
87
  defineProxy(prop, value, target);
78
88
  } else {
@@ -91,7 +101,7 @@ const initialStatus = isServer
91
101
 
92
102
  export const routerState = microStore({
93
103
  pathname: initialPath,
94
- searchParams: new URLSearchParams(initialSearch),
104
+ searchParams: normalizeSearch(initialSearch),
95
105
  params: {},
96
106
  status: initialStatus,
97
107
  });
@@ -100,11 +110,9 @@ export const routerState = microStore({
100
110
  * @type {typeof import("./types.d.ts").setRouterState}
101
111
  */
102
112
  export const setRouterState = (path, search = undefined, params = {}) => {
103
- const [pathname, searchParams] = fixRouteUrl(path).split("?");
113
+ const [pathname, searchPart] = fixRouteUrl(path).split("?");
104
114
  routerState.pathname = pathname;
105
- routerState.searchParams = new URLSearchParams(
106
- search || searchParams || "",
107
- );
115
+ routerState.searchParams = normalizeSearch(search || searchPart || "");
108
116
  Object.keys(routerState.params).forEach((key) =>
109
117
  delete routerState.params[key]
110
118
  );
package/router/types.d.ts CHANGED
@@ -83,10 +83,9 @@ export const isLazyComponent: (component: unknown) => boolean;
83
83
 
84
84
  export const executeLifecycle: (
85
85
  route: RouteEntry | {
86
- preload?: (params?: Record<string, string>) => unknown;
87
- load?: (params?: Record<string, string>) => unknown;
86
+ preload?: () => unknown;
87
+ load?: () => unknown;
88
88
  } | null,
89
- params: Record<string, string> | undefined,
90
89
  ) => Promise<boolean>;
91
90
 
92
91
  export const useRouteData: <T>() => T | undefined;
@@ -104,12 +103,8 @@ export type RouteEntry = {
104
103
  path: string;
105
104
  component: () => Promise<ComponentModule>;
106
105
  params?: Record<string, string>;
107
- preload?: (
108
- params?: Record<string, string>,
109
- ) => boolean | void | Promise<boolean | void>;
110
- load?: (
111
- params?: Record<string, string>,
112
- ) => boolean | void | Promise<boolean | void>;
106
+ preload?: () => boolean | void | Promise<boolean | void>;
107
+ load?: () => boolean | void | Promise<boolean | void>;
113
108
  };
114
109
 
115
110
  export type ImportFn = () => LazyComponent;
@@ -124,12 +119,8 @@ export type RouteProps = {
124
119
  | VanComponent
125
120
  | ComponentFn
126
121
  | (() => Promise<ComponentModule>);
127
- preload?: (
128
- params?: Record<string, string>,
129
- ) => boolean | void | Promise<boolean | void>;
130
- load?: (
131
- params?: Record<string, string>,
132
- ) => boolean | void | Promise<boolean | void>;
122
+ preload?: () => boolean | void | Promise<boolean | void>;
123
+ load?: () => boolean | void | Promise<boolean | void>;
133
124
  };
134
125
 
135
126
  export type RouteConfig = {
@@ -145,7 +136,7 @@ export const Route: (route: RouteProps) => void;
145
136
  // state.mjs
146
137
  export type RouterState = {
147
138
  pathname: string;
148
- searchParams: URLSearchParams;
139
+ searchParams: string;
149
140
  params: Record<string, string>;
150
141
  status: "idle" | "pending" | "success" | "error";
151
142
  };