vite-plugin-vanjs 0.0.9 → 0.1.0

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/router/types.d.ts CHANGED
@@ -12,22 +12,26 @@ import type {
12
12
  PropValueOrDerived,
13
13
  State,
14
14
  } from "vanjs-core";
15
+ import { LayoutFile } from "../plugin/types";
15
16
 
16
17
  // type VanElement = VElement | Exclude<Primitive, boolean | undefined>;
17
- type DOMElement = globalThis.Element;
18
+ // type DOMElement = globalThis.Element;
18
19
  type PrimitiveChild = Primitive | State<Primitive | undefined | null>;
19
20
 
20
- type VanElement =
21
+ type DOMElement<T extends keyof HTMLElementTagNameMap = "div"> =
21
22
  | SVGElement
22
23
  | HTMLElement
23
- | DOMElement
24
24
  | Node
25
+ | HTMLElementTagNameMap[T];
26
+
27
+ export type VanElement =
28
+ | DOMElement
25
29
  | VElement
26
30
  | PrimitiveChild;
27
31
  export type VanNode =
28
32
  | VanElement
29
- | VanNode[]
30
- | (() => VanNode)
33
+ | VanElement[]
34
+ | (() => VanElement | VanElement[])
31
35
  | null
32
36
  | undefined;
33
37
 
@@ -70,6 +74,9 @@ export type VanComponent<
70
74
  export const Router:
71
75
  & VanComponent<"main">
72
76
  & JSX.Component<"main">;
77
+ export const FileSystemRouter:
78
+ & VanComponent<"main">
79
+ & JSX.Component<"main">;
73
80
 
74
81
  // a.mjs
75
82
  /**
@@ -122,17 +129,51 @@ export const redirect: (href?: string) => void | (() => void);
122
129
  // routes.mjs
123
130
  export type RouteEntry = {
124
131
  path: string;
125
- component: Promise<ComponentModule>;
126
- preload?: (params?: Record<string, string>) => void;
127
- load?: (params?: Record<string, string>) => void;
132
+ component: () => Promise<ComponentModule>;
133
+ // component: () => Promise<DynamicModule>;
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
 
143
+ export type ImportFn = () => LazyComponent;
144
+
145
+ /**
146
+ * Registers a lazy component.
147
+ * @param importFn
148
+ */
149
+ export const lazy: (importFn: ImportFn) => () => Promise<ComponentModule>;
150
+
130
151
  export type RouteProps = {
131
152
  path: string;
132
- component: VanComponent | (() => ComponentModule);
133
- preload?: (params?: Record<string, string>) => void;
134
- load?: (params?: Record<string, string>) => void;
153
+ params?: Record<string, string>;
154
+ component:
155
+ // | (() => HTMLElementTagNameMap[unknown] | HTMLElementTagNameMap[unknown][])
156
+ // | VanNode
157
+ | (() => DOMElement)
158
+ | VanComponent
159
+ | ComponentFn
160
+ | (() => Promise<DynamicModule>);
161
+ // | ComponentModule["component"];
162
+ // component: ComponentFn | (() => Promise<ComponentModule>);
163
+ preload?: (
164
+ params?: Record<string, string>,
165
+ ) => boolean | void | Promise<boolean | void>;
166
+ load?: (
167
+ params?: Record<string, string>,
168
+ ) => boolean | void | Promise<boolean | void>;
135
169
  };
170
+
171
+ export type RouteConfig = {
172
+ routePath: string;
173
+ path: string;
174
+ layouts?: LayoutFile[];
175
+ };
176
+
136
177
  export const routes: RouteEntry[];
137
178
 
138
179
  /**
@@ -178,43 +219,79 @@ export const setRouterState: (
178
219
  params?: Record<string, string>,
179
220
  ) => void;
180
221
 
222
+ export type UnwrapResult = {
223
+ children: VanNode[];
224
+ };
225
+
181
226
  /**
182
227
  * Merge the children of an Element or an array of elements with an optional array of children
183
- * into the childen of a single HTMLFragmentElement element.
184
- * @param source
185
- * @param {...VanNode[]} children
228
+ * into the childen prperty of a simple object.
229
+ * @param source
230
+ * @param children
186
231
  */
232
+
187
233
  export const unwrap: (
188
234
  source:
189
235
  | VanNode
190
- | IsoTagFunc
191
236
  | VanNode[]
192
- | IsoTagFunc[]
193
- | (() => IsoTagFunc | IsoTagFunc[] | VanNode | VanNode[]),
237
+ | VanComponent
238
+ | { children: VanNode[] }
239
+ | ComponentFn
240
+ | ReturnType<IsoTagFunc>
241
+ | ReturnType<IsoTagFunc>[]
242
+ | (() =>
243
+ | ReturnType<IsoTagFunc>
244
+ | ReturnType<IsoTagFunc>[]
245
+ | VanNode
246
+ | VanNode[]),
194
247
  ...children: VanNode[]
195
- ) => VanNode;
248
+ ) => UnwrapResult;
249
+
250
+ export type JSXComponentFn = () => JSX.Element;
251
+ export type FragmentFn = () => ChildDom | ChildDom[];
252
+ export type ComponentFn = FragmentFn | VanComponent | JSXComponentFn;
196
253
 
197
254
  export type ComponentModule = {
198
- component: VanComponent | JSX.Element;
199
- route: Pick<RouteEntry, "load" | "preload">;
255
+ component: ComponentFn;
256
+ route?: Pick<RouteEntry, "load" | "preload">;
200
257
  };
201
258
 
202
259
  export type DynamicModule = {
203
- Page: VanComponent | JSX.Element;
204
- default?: VanComponent | JSX.Element;
260
+ Page: ComponentFn;
261
+ default?: ComponentFn;
205
262
  route?: Pick<RouteEntry, "load" | "preload">;
206
263
  };
207
264
 
208
265
  export type LazyComponent = Promise<DynamicModule>;
209
266
 
210
- /**
211
- * Registers a lazy component.
212
- * @param importFn
213
- */
214
- export const lazy: (importFn: () => LazyComponent) => () => ComponentModule;
215
-
216
267
  /**
217
268
  * Fixes the URL of a route.
218
269
  * @param url
219
270
  */
220
271
  export const fixRouteUrl: (url: string) => string;
272
+
273
+ /**
274
+ * Cache a route.
275
+ * @param key the cache route key
276
+ * @param value the cache route
277
+ */
278
+ export const cache: (key: ImportFn, value: ComponentModule) => void;
279
+
280
+ /**
281
+ * Return a route cache.
282
+ * @param key the cache route key
283
+ */
284
+ export const getCached: (key: typeof importFn) => ComponentModule | undefined;
285
+
286
+ /**
287
+ * Execute lifecycle methods preload and / or load
288
+ */
289
+ export const executeLifecycle: (
290
+ { route }: ComponentModule,
291
+ params: Record<string, string> | undefined,
292
+ ) => Promise<boolean>;
293
+
294
+ /**
295
+ * Find a registered route that matches the given path
296
+ */
297
+ export const matchRoute: (path: string) => RouteEntry | null;
package/server/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { basename } from "node:path";
2
+ export * from "../plugin/helpers.mjs";
2
3
 
3
4
  /**
4
5
  * @type {typeof import("./types.d.ts").renderToString}
@@ -66,32 +67,54 @@ function renderPreloadLink(file) {
66
67
  }
67
68
  }
68
69
 
70
+ /**
71
+ * @type {typeof import("./types.d.ts").renderPreloadLinks}
72
+ */
69
73
  /**
70
74
  * @type {typeof import("./types.d.ts").renderPreloadLinks}
71
75
  */
72
76
  export function renderPreloadLinks(modules, manifest) {
73
77
  let links = "";
74
78
  const seen = new Set();
79
+ const ignoredAssets = new Set();
80
+
81
+ // First pass: collect all assets from ignored paths
82
+ Object.entries(manifest).forEach(([id, files]) => {
83
+ // istanbul ignore else
84
+ if (["src/pages", "src/routes"].some((l) => id.includes(l))) {
85
+ files.forEach((asset) => ignoredAssets.add(asset));
86
+ }
87
+ });
88
+
89
+ // Second pass: generate preload links
75
90
  modules.forEach((id) => {
76
91
  const files = manifest[id];
77
- /* istanbul ignore else */
92
+
93
+ // istanbul ignore else
78
94
  if (files?.length) {
79
95
  files.forEach((file) => {
80
- /* istanbul ignore else */
81
- if (!seen.has(file)) {
82
- seen.add(file);
83
- const filename = basename(file);
84
- /* istanbul ignore next - impossible to test */
85
- if (manifest[filename]) {
86
- for (const depFile of manifest[filename]) {
96
+ // Skip if we've seen this file or if it's an asset from an ignored path
97
+ if (seen.has(file) || ignoredAssets.has(file)) return;
98
+
99
+ seen.add(file);
100
+ const filename = basename(file);
101
+
102
+ // Handle dependencies
103
+ // istanbul ignore next - no way to test this
104
+ if (manifest[filename]) {
105
+ for (const depFile of manifest[filename]) {
106
+ // istanbul ignore else
107
+ if (!seen.has(depFile) && !ignoredAssets.has(depFile)) {
87
108
  links += renderPreloadLink(depFile);
88
109
  seen.add(depFile);
89
110
  }
90
111
  }
91
- links += renderPreloadLink(file);
92
112
  }
113
+
114
+ links += renderPreloadLink(file);
93
115
  });
94
116
  }
95
117
  });
118
+
96
119
  return links;
97
120
  }
package/server/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /// <reference path="global.d.ts" />
2
2
  import type { Element as VanElement, TagFunc } from "mini-van-plate/van-plate";
3
+ import { ResolvedConfig } from "vite";
3
4
 
4
5
  /**
5
6
  * A function that takes a list of files and a manifest and returns a string
@@ -36,3 +37,58 @@ export type Source =
36
37
  * @returns HTML string
37
38
  */
38
39
  export const renderToString: (source: Source) => Promise<string>;
40
+
41
+ // FILE SYSTEM ROUTER
42
+ /**
43
+ * Get the file most probable route path for a given potential route.
44
+ */
45
+ export const fileToRoute: (file: string, routesDir: string) => string;
46
+
47
+ export type PageFile = { path: string; routePath: string };
48
+ export type LayoutFile = { id: string; path: string };
49
+ export type RouteFile = PageFile & { layouts: Array<LayoutFile> };
50
+ export type PluginConfig = { routesDir: string; extensions: string[] };
51
+ /**
52
+ * Find all layout files for a given route.
53
+ */
54
+
55
+ export const findLayouts: (
56
+ routePath: string,
57
+ config: ResolvedConfig,
58
+ pluginConfig: PluginConfig,
59
+ ) => Array<LayoutFile>;
60
+
61
+ /**
62
+ * Identify all files in a folder.
63
+ */
64
+ export const globFiles: (
65
+ dir: string,
66
+ extensions: string[],
67
+ ) => Promise<Array<string>>;
68
+
69
+ /**
70
+ * Scan routes directory and generate routes.
71
+ */
72
+ export const scanRoutes: (
73
+ config: ResolvedConfig,
74
+ pluginConfig: PluginConfig,
75
+ ) => Promise<
76
+ Array<{ path: string; routePath: string }>
77
+ >;
78
+
79
+ /**
80
+ * Process routes and identify their layouts
81
+ */
82
+ export const processLayoutRoutes: (
83
+ routes: Array<PageFile>,
84
+ config: ResolvedConfig,
85
+ pluginConfig: PluginConfig,
86
+ ) => Array<PRouteFile>;
87
+
88
+ /**
89
+ * Scan and process layouts and return them.
90
+ */
91
+ export const getRoutes: (
92
+ config: ResolvedConfig,
93
+ pluginConfig: PluginConfig,
94
+ ) => Promise<Array<RouteFile>>;
package/setup/global.d.ts CHANGED
@@ -5,49 +5,51 @@ declare module "@vanjs/van" {
5
5
  }
6
6
 
7
7
  declare module "@vanjs/vanX" {
8
- import {
9
- calc,
10
- compact,
11
- list,
12
- noreactive,
13
- raw,
14
- reactive,
15
- replace,
16
- stateFields,
8
+ import type {
9
+ CalcFunction,
10
+ CompactFunction,
11
+ ListFunction,
12
+ NoreactiveFunction,
13
+ RawFunction,
14
+ ReactiveFunction,
15
+ ReplaceFunction,
16
+ StateFieldsFunction,
17
17
  } from "vanjs-ext";
18
- const vanX: {
19
- calc: typeof calc;
20
- reactive: typeof reactive;
21
- noreactive: typeof noreactive;
22
- stateFields: typeof stateFields;
23
- raw: typeof raw;
24
- list: typeof list;
25
- replace: typeof replace;
26
- compact: typeof compact;
27
- };
18
+ import type { VanXObj } from "mini-van-plate/shared";
19
+
20
+ export const calc: CalcFunction;
21
+ export const compact: CompactFunction;
22
+ export const list: ListFunction;
23
+ export const noreactive: NoreactiveFunction;
24
+ export const raw: RawFunction;
25
+ export const reactive: ReactiveFunction;
26
+ export const replace: ReplaceFunction;
27
+ export const stateFields: StateFieldsFunction;
28
+
29
+ // Define the vanX object type
30
+ interface VanXObject {
31
+ calc: CalcFunction;
32
+ reactive: ReactiveFunction;
33
+ noreactive: NoreactiveFunction;
34
+ stateFields: StateFieldsFunction;
35
+ raw: RawFunction;
36
+ list: ListFunction;
37
+ replace: ReplaceFunction;
38
+ compact: CompactFunction;
39
+ readonly default: VanXObject;
40
+ }
41
+ interface VanXPlate extends VanXObj {
42
+ readonly default: VanXObj;
43
+ }
44
+
45
+ const vanX: VanXObject | VanXPlate;
28
46
  export default vanX;
29
- export {
30
- calc,
31
- compact,
32
- list,
33
- noreactive,
34
- raw,
35
- reactive,
36
- replace,
37
- stateFields,
38
- };
39
47
  }
40
48
 
41
49
  declare module "@vanjs/setup" {
42
- import type { Van } from "vanjs-core";
43
- import type * as vanX from "vanjs-ext";
44
- import type { dummyVanX } from "mini-van-plate/shared";
50
+ import van from "@vanjs/van";
51
+ import vanX from "@vanjs/vanX";
45
52
 
46
- interface Setup {
47
- isServer: boolean;
48
- van: Van;
49
- vanX: typeof vanX | typeof dummyVanX;
50
- }
51
- const setup: Setup;
52
- export default setup;
53
+ const isServer: boolean;
54
+ export { isServer, van, vanX };
53
55
  }
@@ -0,0 +1,7 @@
1
+ /** @type {(props: Record<string, unknown>) => boolean} */
2
+ export function needsHydration(props) {
3
+ return props && (
4
+ Object.keys(props).some((k) => k.startsWith("on")) || // has events
5
+ Object.values(props).some((v) => v && typeof v === "object" && "val" in v) // has state
6
+ );
7
+ }
@@ -1,21 +1,9 @@
1
- import { dummyVanX, registerEnv } from "mini-van-plate/shared";
2
- import vanPlate from "mini-van-plate/van-plate";
3
- import vanCore from "vanjs-core/debug";
4
- import * as vanX from "vanjs-ext";
5
-
6
- const vanXObject = { ...vanX, default: vanX };
7
- const dummyObject = { ...dummyVanX, default: dummyVanX };
8
-
9
- const setup = {
10
- isServer: typeof window === "undefined",
11
- get van() {
12
- return this.isServer ? vanPlate : vanCore;
13
- },
14
- get vanX() {
15
- return this.isServer ? dummyObject : vanXObject;
16
- },
1
+ import van from "./van-debug.mjs";
2
+ import vanX from "./vanX.mjs";
3
+ import isServer from "./isServer.mjs";
4
+
5
+ export default {
6
+ isServer,
7
+ van,
8
+ vanX,
17
9
  };
18
-
19
- registerEnv(setup);
20
-
21
- export default setup;
@@ -0,0 +1,5 @@
1
+ import van from "./van-ssr.mjs";
2
+ import vanX from "./vanX-ssr.mjs";
3
+ import isServer from "./isServer.mjs";
4
+
5
+ export { isServer, van, vanX };
package/setup/index.mjs CHANGED
@@ -1,21 +1,5 @@
1
- import { dummyVanX, registerEnv } from "mini-van-plate/shared";
2
- import vanPlate from "mini-van-plate/van-plate";
3
- import vanCore from "vanjs-core";
4
- import * as vanX from "vanjs-ext";
1
+ import van from "./van.mjs";
2
+ import vanX from "./vanX.mjs";
3
+ import isServer from "./isServer.mjs";
5
4
 
6
- const vanXObject = { ...vanX, default: vanX };
7
- const dummyObject = { ...dummyVanX, default: dummyVanX };
8
-
9
- const setup = {
10
- isServer: typeof window === "undefined",
11
- get van() {
12
- return this.isServer ? vanPlate : vanCore;
13
- },
14
- get vanX() {
15
- return this.isServer ? dummyObject : vanXObject;
16
- },
17
- };
18
-
19
- registerEnv(setup);
20
-
21
- export default setup;
5
+ export { isServer, van, vanX };
@@ -0,0 +1,2 @@
1
+ const isServer = typeof window === "undefined";
2
+ export default isServer;
@@ -9,13 +9,29 @@
9
9
  "types": "./types.d.ts",
10
10
  "import": "./index.mjs"
11
11
  },
12
+ "./debug": {
13
+ "types": "./types.d.ts",
14
+ "import": "./index-debug.mjs"
15
+ },
12
16
  "./van": {
13
17
  "types": "./van.d.ts",
14
18
  "import": "./van.mjs"
15
19
  },
20
+ "./van-ssr": {
21
+ "types": "./van-ssr.d.ts",
22
+ "import": "./van-ssr.mjs"
23
+ },
24
+ "./van-debug": {
25
+ "types": "./van.d.ts",
26
+ "import": "./van-debug.mjs"
27
+ },
16
28
  "./vanX": {
17
29
  "types": "./vanX.d.ts",
18
30
  "import": "./vanX.mjs"
31
+ },
32
+ "./vanX-ssr": {
33
+ "types": "./vanX.d.ts",
34
+ "import": "./vanX-ssr.mjs"
19
35
  }
20
36
  },
21
37
  "peerDependencies": {
@@ -1,2 +1,2 @@
1
- import setup from "./index-debug.mjs";
2
- export default setup.van;
1
+ import van from "vanjs-core/debug";
2
+ export default van;
@@ -0,0 +1 @@
1
+ export * from "mini-van-plate/van-plate";
@@ -0,0 +1,51 @@
1
+ // setup/van-ssr.mjs
2
+ import { registerEnv } from "mini-van-plate/shared";
3
+ import van from "mini-van-plate/van-plate";
4
+ import { needsHydration } from "./helpers.mjs";
5
+
6
+ function tagWithHydration(ns, name, ...args) {
7
+ const originalTag = ns ? van.tags(ns)[name] : van.tags[name];
8
+
9
+ const [props, ...children] =
10
+ Object.getPrototypeOf(args[0] ?? /* istanbul ignore next */ 0) ===
11
+ Object.prototype
12
+ ? args
13
+ : [{}, ...args];
14
+
15
+ // Create new props object without event handlers
16
+ const finalProps = { ...props };
17
+
18
+ if (needsHydration(props)) {
19
+ // Add data-hk for hydration
20
+ finalProps["data-hk"] = "";
21
+
22
+ // Remove all inline event handlers for server rendering
23
+ for (const key in finalProps) {
24
+ if (key.startsWith("on")) {
25
+ delete finalProps[key];
26
+ }
27
+ }
28
+ }
29
+
30
+ // Use filtered props for server rendering
31
+ return originalTag(finalProps, ...children);
32
+ }
33
+
34
+ const tagsProxy = new Proxy(
35
+ (ns) =>
36
+ new Proxy(tagWithHydration.bind(undefined, ns), {
37
+ get: (_, name) => tagWithHydration.bind(undefined, ns, name),
38
+ }),
39
+ {
40
+ get: (_, name) => tagWithHydration.bind(undefined, undefined, name),
41
+ },
42
+ );
43
+
44
+ const vanSSR = {
45
+ ...van,
46
+ tags: tagsProxy,
47
+ };
48
+
49
+ registerEnv({ van: vanSSR });
50
+
51
+ export default vanSSR;
package/setup/van.mjs CHANGED
@@ -1,2 +1,44 @@
1
- import setup from "./index.mjs";
2
- export default setup.van;
1
+ // setup/van.mjs
2
+ import van from "vanjs-core";
3
+ import { needsHydration } from "./helpers.mjs";
4
+
5
+ // Create our own tag function that wraps the original
6
+ function tagWithHydration(ns, name, ...args) {
7
+ // Get the correct tag function based on namespace
8
+ const originalTag = ns ? van.tags(ns)[name] : van.tags[name];
9
+
10
+ const [props, ...children] =
11
+ Object.getPrototypeOf(args[0] ?? 0) === Object.prototype
12
+ ? args
13
+ : [{}, ...args];
14
+
15
+ // Create new props object without event handlers
16
+ const finalProps = { ...props };
17
+
18
+ // Add data-hk if element needs hydration
19
+ if (needsHydration(props)) {
20
+ finalProps["data-hk"] = "";
21
+ }
22
+
23
+ // Call the original tag function
24
+ return originalTag(finalProps, ...children);
25
+ }
26
+
27
+ // Create our tags proxy using the same pattern as vanjs-core
28
+ const tagsProxy = new Proxy(
29
+ (ns) =>
30
+ new Proxy(tagWithHydration.bind(undefined, ns), {
31
+ get: (_, name) => tagWithHydration.bind(undefined, ns, name),
32
+ }),
33
+ {
34
+ get: (_, name) => tagWithHydration.bind(undefined, undefined, name),
35
+ },
36
+ );
37
+
38
+ // Export modified van instance with only tags overridden
39
+ const vanClient = {
40
+ ...van,
41
+ tags: tagsProxy,
42
+ };
43
+
44
+ export default vanClient;
@@ -0,0 +1 @@
1
+ export * from "mini-van-plate/van-plate";
@@ -0,0 +1,12 @@
1
+ import { dummyVanX, registerEnv } from "mini-van-plate/shared";
2
+
3
+ const vanX = {
4
+ ...dummyVanX,
5
+ get default() {
6
+ return dummyVanX;
7
+ },
8
+ };
9
+
10
+ registerEnv({ vanX });
11
+
12
+ export default vanX;