@void/react 1.3.0 → 1.5.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/dist/{action-4EvQppia.mjs → action-CyCVdW12.mjs} +2 -3
- package/dist/{context-D2ctU4yT.mjs → context-BQpawTZc.mjs} +1 -2
- package/dist/{deferred-shell-DOuP2YAo.mjs → deferred-shell-D5T32zXz.mjs} +1 -2
- package/dist/index.d.mts +7 -23
- package/dist/index.mjs +25 -54
- package/dist/plugin.d.mts +0 -4
- package/dist/plugin.mjs +48 -86
- package/dist/runtime/pages-client.d.mts +0 -2
- package/dist/runtime/pages-client.mjs +16 -15
- package/dist/runtime/pages-server.d.mts +0 -2
- package/dist/runtime/pages-server.mjs +4 -6
- package/package.json +7 -6
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createActionClient } from "void/pages-client";
|
|
2
|
-
|
|
3
|
-
const client = createActionClient();
|
|
2
|
+
// src/runtime/action.ts
|
|
3
|
+
const client = createActionClient({ _resolveOnShell: true });
|
|
4
4
|
const action = client.action;
|
|
5
5
|
const setActionRouter = client.setActionRouter;
|
|
6
|
-
//#endregion
|
|
7
6
|
export { setActionRouter as n, action as t };
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { createContext } from "react";
|
|
2
2
|
import { idleNavigationState } from "void/pages-client";
|
|
3
|
-
|
|
3
|
+
// src/runtime/context.ts
|
|
4
4
|
const SharedContext = createContext(null);
|
|
5
5
|
const ErrorsContext = createContext(null);
|
|
6
6
|
const ParamsContext = createContext(null);
|
|
7
7
|
const RouterContext = createContext(null);
|
|
8
8
|
const NavigationContext = createContext(idleNavigationState());
|
|
9
9
|
const FrontmatterContext = createContext({});
|
|
10
|
-
//#endregion
|
|
11
10
|
export { RouterContext as a, ParamsContext as i, FrontmatterContext as n, SharedContext as o, NavigationContext as r, ErrorsContext as t };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// src/runtime/deferred-shell.ts
|
|
2
2
|
const DEFERRED_SHELL_END_ID = "__VOID_DEFERRED_SHELL_END__";
|
|
3
3
|
const DEFERRED_SHELL_END_MARKER = `<template id="${DEFERRED_SHELL_END_ID}"></template>`;
|
|
4
4
|
const DEFERRED_SHELL_CANCEL_REASON = "Void deferred shell complete";
|
|
5
|
-
//#endregion
|
|
6
5
|
export { DEFERRED_SHELL_END_ID as n, DEFERRED_SHELL_END_MARKER as r, DEFERRED_SHELL_CANCEL_REASON as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -2,15 +2,12 @@ import { AnchorHTMLAttributes, ButtonHTMLAttributes, Context, FormHTMLAttributes
|
|
|
2
2
|
import { ActionClient, FormMethod, FormModel, FormResetOptions, NavigationState, VoidActionError, VoidRouter } from "void/pages-client";
|
|
3
3
|
import { CloudContextVariables, InferProps } from "void";
|
|
4
4
|
import { ActionFormOptions, ActionUrl, ResolveActionBody } from "void/routes";
|
|
5
|
-
//#region src/runtime/form.d.ts
|
|
6
5
|
type FormProps = Omit<FormHTMLAttributes<HTMLFormElement>, "action" | "onSubmit" | "method" | "onError"> & {
|
|
7
6
|
form: FormModel<[FormData]>;
|
|
8
7
|
method?: FormMethod;
|
|
9
8
|
renderError?: (error: VoidActionError) => ReactNode;
|
|
10
9
|
};
|
|
11
10
|
export declare function Form({ form, method, renderError, children, ...props }: FormProps): ReactElement;
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/runtime/link.d.ts
|
|
14
11
|
type PrefetchStrategy = "hover" | "click" | "mount" | "visible";
|
|
15
12
|
type LinkMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
16
13
|
type CommonProps = {
|
|
@@ -30,18 +27,16 @@ type CommonProps = {
|
|
|
30
27
|
};
|
|
31
28
|
type LinkProps = CommonProps & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof CommonProps> & Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof CommonProps>;
|
|
32
29
|
export declare function Link({ href, method, data, preserveScroll, preserveState, replace, reloadDocument, viewTransition, prefetch: prefetchProp, cacheFor, onNavigate, children, style, onClick: onClickProp, onMouseEnter: onMouseEnterProp, onMouseLeave: onMouseLeaveProp, onFocus: onFocusProp, onMouseDown: onMouseDownProp, onTouchStart: onTouchStartProp, ...rest }: LinkProps): JSX.Element;
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region src/runtime/use-router.d.ts
|
|
35
30
|
export declare function useRouter(): VoidRouter;
|
|
36
|
-
//#endregion
|
|
37
|
-
//#region src/runtime/use-params.d.ts
|
|
38
31
|
export declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/runtime/use-shared.d.ts
|
|
41
32
|
export declare function useShared<T = CloudContextVariables["shared"]>(): T;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
type FormOptions = {
|
|
34
|
+
params?: Record<string, string>;
|
|
35
|
+
resetOnSuccess?: boolean;
|
|
36
|
+
onSuccess?: () => void | Promise<void>;
|
|
37
|
+
onError?: (error: Record<string, string> | VoidActionError) => void | Promise<void>;
|
|
38
|
+
};
|
|
39
|
+
export declare function useForm<U extends ActionUrl & string>(url: U, defaults: ResolveActionBody<U>, options?: ActionFormOptions<U> & Pick<FormOptions, "onSuccess" | "onError">): {
|
|
45
40
|
data: ResolveActionBody<U>;
|
|
46
41
|
setData: <K extends keyof ResolveActionBody<U> & string>(field: K, value: ResolveActionBody<U>[K]) => void;
|
|
47
42
|
errors: Partial<Record<keyof ResolveActionBody<U> & string, string>>;
|
|
@@ -60,11 +55,7 @@ export declare function useForm<U extends ActionUrl & string>(url: U, defaults:
|
|
|
60
55
|
clearErrors(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
|
|
61
56
|
clearError(): void;
|
|
62
57
|
};
|
|
63
|
-
//#endregion
|
|
64
|
-
//#region src/runtime/use-navigation.d.ts
|
|
65
58
|
export declare function useNavigation(): NavigationState;
|
|
66
|
-
//#endregion
|
|
67
|
-
//#region src/runtime/use-island-form.d.ts
|
|
68
59
|
type IslandFormReturn<T extends Record<string, unknown>> = Readonly<{
|
|
69
60
|
data: T;
|
|
70
61
|
setData: <K extends keyof T & string>(field: K, value: T[K]) => void;
|
|
@@ -87,20 +78,13 @@ type IslandFormReturn<T extends Record<string, unknown>> = Readonly<{
|
|
|
87
78
|
* Void Router (which is not available in island mode).
|
|
88
79
|
*/
|
|
89
80
|
export declare function useIslandForm<T extends Record<string, unknown>>(defaults: T): IslandFormReturn<T>;
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region src/runtime/action.d.ts
|
|
92
81
|
export declare const action: ActionClient["action"];
|
|
93
82
|
export declare const setActionRouter: ActionClient["setActionRouter"];
|
|
94
|
-
//#endregion
|
|
95
|
-
//#region src/runtime/context.d.ts
|
|
96
83
|
export declare const SharedContext: Context<Record<string, unknown> | null>;
|
|
97
84
|
export declare const ErrorsContext: Context<Record<string, string> | null>;
|
|
98
85
|
export declare const ParamsContext: Context<Record<string, string> | null>;
|
|
99
86
|
export declare const RouterContext: Context<VoidRouter | null>;
|
|
100
87
|
export declare const NavigationContext: Context<NavigationState>;
|
|
101
88
|
export declare const FrontmatterContext: Context<Record<string, unknown>>;
|
|
102
|
-
//#endregion
|
|
103
|
-
//#region src/runtime/index.d.ts
|
|
104
89
|
export type Deferred<T> = Promise<T>;
|
|
105
|
-
//#endregion
|
|
106
90
|
export type { InferProps };
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as RouterContext, i as ParamsContext, n as FrontmatterContext, o as SharedContext, r as NavigationContext, t as ErrorsContext } from "./context-
|
|
2
|
-
import { n as setActionRouter, t as action } from "./action-
|
|
1
|
+
import { a as RouterContext, i as ParamsContext, n as FrontmatterContext, o as SharedContext, r as NavigationContext, t as ErrorsContext } from "./context-BQpawTZc.mjs";
|
|
2
|
+
import { n as setActionRouter, t as action } from "./action-CyCVdW12.mjs";
|
|
3
3
|
import { useActionState, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { cloneFormValue, formErrorMessage, formRetryMessage, formSubmissionBlocked, getValidationErrors, interpolateRouteParams, isAbortError, isEqualFormValue, isModifiedEvent, mergeDataIntoHref, parseFormResetArgs, scheduleFormRetry, ssrProxy, submitAction, submitIslandAction } from "void/pages-client";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
|
|
6
|
+
// src/runtime/form.tsx
|
|
7
7
|
function Form({ form, method = "post", renderError, children, ...props }) {
|
|
8
8
|
const [now, setNow] = useState(Date.now);
|
|
9
9
|
useEffect(() => scheduleFormRetry(form.error, () => setNow(Date.now())), [form.error]);
|
|
@@ -24,8 +24,7 @@ function Form({ form, method = "post", renderError, children, ...props }) {
|
|
|
24
24
|
}), children]
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
//#region src/runtime/link.tsx
|
|
27
|
+
// src/runtime/link.tsx
|
|
29
28
|
function Link({ href, method = "GET", data, preserveScroll = false, preserveState = false, replace = false, reloadDocument = false, viewTransition, prefetch: prefetchProp = false, cacheFor, onNavigate, children, style, onClick: onClickProp, onMouseEnter: onMouseEnterProp, onMouseLeave: onMouseLeaveProp, onFocus: onFocusProp, onMouseDown: onMouseDownProp, onTouchStart: onTouchStartProp, ...rest }) {
|
|
30
29
|
const router = useContext(RouterContext);
|
|
31
30
|
const elementRef = useRef(null);
|
|
@@ -208,27 +207,23 @@ function Link({ href, method = "GET", data, preserveScroll = false, preserveStat
|
|
|
208
207
|
children
|
|
209
208
|
});
|
|
210
209
|
}
|
|
211
|
-
|
|
212
|
-
//#region src/runtime/use-router.ts
|
|
210
|
+
// src/runtime/use-router.ts
|
|
213
211
|
function useRouter() {
|
|
214
212
|
return useContext(RouterContext) ?? ssrProxy;
|
|
215
213
|
}
|
|
216
|
-
|
|
217
|
-
//#region src/runtime/use-params.ts
|
|
214
|
+
// src/runtime/use-params.ts
|
|
218
215
|
function useParams() {
|
|
219
216
|
const params = useContext(ParamsContext);
|
|
220
217
|
if (params === null) throw new Error("useParams(): must be used inside a Void page.");
|
|
221
218
|
return params;
|
|
222
219
|
}
|
|
223
|
-
|
|
224
|
-
//#region src/runtime/use-shared.ts
|
|
220
|
+
// src/runtime/use-shared.ts
|
|
225
221
|
function useShared() {
|
|
226
222
|
const shared = useContext(SharedContext);
|
|
227
223
|
if (!shared) throw new Error("useShared(): must be used inside a Void page.");
|
|
228
224
|
return shared;
|
|
229
225
|
}
|
|
230
|
-
|
|
231
|
-
//#region src/runtime/use-form.ts
|
|
226
|
+
// src/runtime/use-form.ts
|
|
232
227
|
function useForm(url, defaults, options) {
|
|
233
228
|
const router = useContext(RouterContext);
|
|
234
229
|
const [data, setDataState] = useState(() => cloneFormValue(defaults));
|
|
@@ -309,8 +304,13 @@ function useForm(url, defaults, options) {
|
|
|
309
304
|
}
|
|
310
305
|
if (!result.ok) {
|
|
311
306
|
const validationErrors = getValidationErrors(result.error.body);
|
|
312
|
-
if (validationErrors)
|
|
313
|
-
|
|
307
|
+
if (validationErrors) {
|
|
308
|
+
setErrors(validationErrors);
|
|
309
|
+
await options?.onError?.(validationErrors);
|
|
310
|
+
} else {
|
|
311
|
+
setActionError(result.error);
|
|
312
|
+
await options?.onError?.(result.error);
|
|
313
|
+
}
|
|
314
314
|
return previousSuccessVersion;
|
|
315
315
|
}
|
|
316
316
|
if (options?.resetOnSuccess) {
|
|
@@ -326,6 +326,7 @@ function useForm(url, defaults, options) {
|
|
|
326
326
|
setRecentlySuccessful(false);
|
|
327
327
|
successTimeoutRef.current = null;
|
|
328
328
|
}, 2e3);
|
|
329
|
+
await options?.onSuccess?.();
|
|
329
330
|
return previousSuccessVersion + 1;
|
|
330
331
|
}, 0);
|
|
331
332
|
useEffect(() => {
|
|
@@ -358,22 +359,11 @@ function useForm(url, defaults, options) {
|
|
|
358
359
|
clearError
|
|
359
360
|
};
|
|
360
361
|
}
|
|
361
|
-
|
|
362
|
-
//#region src/runtime/use-navigation.ts
|
|
362
|
+
// src/runtime/use-navigation.ts
|
|
363
363
|
function useNavigation() {
|
|
364
364
|
return useContext(NavigationContext);
|
|
365
365
|
}
|
|
366
|
-
|
|
367
|
-
//#region src/runtime/use-island-form.ts
|
|
368
|
-
async function readActionErrorBody(response) {
|
|
369
|
-
const contentType = response.headers.get("content-type") || "";
|
|
370
|
-
try {
|
|
371
|
-
if (contentType.includes("application/json")) return await response.json();
|
|
372
|
-
return await response.text() || null;
|
|
373
|
-
} catch {
|
|
374
|
-
return null;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
366
|
+
// src/runtime/use-island-form.ts
|
|
377
367
|
/**
|
|
378
368
|
* Form hook for island pages. Uses fetch + page reload instead of the
|
|
379
369
|
* Void Router (which is not available in island mode).
|
|
@@ -424,16 +414,12 @@ function useIslandForm(defaults) {
|
|
|
424
414
|
setErrors({});
|
|
425
415
|
setError(null);
|
|
426
416
|
try {
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
body: JSON.stringify(data)
|
|
431
|
-
});
|
|
432
|
-
if (response.redirected) {
|
|
433
|
-
window.location.href = response.url;
|
|
417
|
+
const result = await submitIslandAction(url, method, data);
|
|
418
|
+
if (result.kind === "redirect") {
|
|
419
|
+
window.location.href = result.url;
|
|
434
420
|
return;
|
|
435
421
|
}
|
|
436
|
-
if (
|
|
422
|
+
if (result.kind === "success") {
|
|
437
423
|
setWasSuccessful(true);
|
|
438
424
|
setRecentlySuccessful(true);
|
|
439
425
|
const nextDefaults = { ...data };
|
|
@@ -441,22 +427,8 @@ function useIslandForm(defaults) {
|
|
|
441
427
|
setDefaultsState(nextDefaults);
|
|
442
428
|
successTimeoutRef.current = setTimeout(() => setRecentlySuccessful(false), 2e3);
|
|
443
429
|
window.location.reload();
|
|
444
|
-
} else
|
|
445
|
-
|
|
446
|
-
const actionError = new VoidActionError({
|
|
447
|
-
body,
|
|
448
|
-
status: response.status,
|
|
449
|
-
statusText: response.statusText,
|
|
450
|
-
url
|
|
451
|
-
});
|
|
452
|
-
const validationErrors = response.status === 422 ? getValidationErrors(body) : null;
|
|
453
|
-
if (validationErrors) {
|
|
454
|
-
setErrors(validationErrors);
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
if (categorizeActionError(response.status) === "boundary") throw actionError;
|
|
458
|
-
setError(actionError);
|
|
459
|
-
}
|
|
430
|
+
} else if (result.kind === "validation") setErrors(result.errors);
|
|
431
|
+
else setError(result.error);
|
|
460
432
|
} finally {
|
|
461
433
|
setPending(false);
|
|
462
434
|
}
|
|
@@ -479,5 +451,4 @@ function useIslandForm(defaults) {
|
|
|
479
451
|
delete: (url) => submit(url, "DELETE")
|
|
480
452
|
};
|
|
481
453
|
}
|
|
482
|
-
//#endregion
|
|
483
454
|
export { ErrorsContext, Form, FrontmatterContext, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|
package/dist/plugin.d.mts
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import react from "@vitejs/plugin-react";
|
|
2
2
|
import { Plugin } from "vite";
|
|
3
|
-
//#region src/plugin-pages.d.ts
|
|
4
3
|
type PrefetchConfig = {
|
|
5
4
|
hoverDelay?: number;
|
|
6
5
|
cacheFor?: number | string | [string, string];
|
|
7
6
|
};
|
|
8
|
-
//#endregion
|
|
9
|
-
//#region src/plugin.d.ts
|
|
10
7
|
export type VoidReactOptions = Readonly<{
|
|
11
8
|
react?: Parameters<typeof react>[0];
|
|
12
9
|
viewTransitions?: boolean;
|
|
13
10
|
prefetch?: PrefetchConfig;
|
|
14
11
|
}>;
|
|
15
12
|
export declare function voidReact(options?: VoidReactOptions): Array<Plugin>;
|
|
16
|
-
//#endregion
|
|
17
13
|
export {};
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import react from "@vitejs/plugin-react";
|
|
2
|
-
import { generateComponentManifest, resolveEffectiveLayoutChain, resolvePagesDir,
|
|
2
|
+
import { createPagesPlugin, generateComponentManifest, resolveEffectiveLayoutChain, resolvePagesDir, scanPages } from "void/pages";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import nodePath from "node:path";
|
|
5
5
|
import { normalizePath } from "vite";
|
|
6
6
|
import { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, computePagesNeedingClientJS, editIslandSource, findIslandImports, findStaticCssImports, hasIslandPages, islandStylesPlugin, recordIslandStyles, recordedIslandStyles, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan } from "void/pages-islands-plugin";
|
|
7
|
-
|
|
7
|
+
// ../../node_modules/.pnpm/pathslash@0.1.0/node_modules/pathslash/dist/index.mjs
|
|
8
8
|
const BACKSLASH_RE = /\\/g;
|
|
9
9
|
const isWindows = process.platform === "win32";
|
|
10
10
|
/**
|
|
@@ -42,9 +42,7 @@ const posix = { ...nodePath.posix };
|
|
|
42
42
|
posix.posix = win32.posix = posix;
|
|
43
43
|
posix.win32 = win32.win32 = win32;
|
|
44
44
|
const { basename, delimiter, dirname, extname, format, isAbsolute, join, matchesGlob, normalize, parse, relative, resolve, sep, toNamespacedPath } = isWindows ? win32 : posix;
|
|
45
|
-
|
|
46
|
-
//#region src/plugin-pages.ts
|
|
47
|
-
const RENDERER_ID = "virtual:void-pages-renderer";
|
|
45
|
+
// src/plugin-pages.ts
|
|
48
46
|
const CLIENT_ENTRY_ID = "virtual:void-pages-client";
|
|
49
47
|
const WORKER_ENVIRONMENT_NAME = "void_worker";
|
|
50
48
|
const REACT_DEDUPE = ["react", "react-dom"];
|
|
@@ -69,88 +67,28 @@ const REACT_REFRESH_PREAMBLE = `
|
|
|
69
67
|
window.__vite_plugin_react_preamble_installed__ = true
|
|
70
68
|
<\/script>`;
|
|
71
69
|
function pagesPlugin(pagesDir, options) {
|
|
72
|
-
let pageScan = null;
|
|
73
|
-
let isDev = false;
|
|
74
|
-
let viteConfig;
|
|
75
70
|
const root = options?.root ?? dirname(pagesDir);
|
|
76
|
-
const normalizedPagesDir = normalizePath(pagesDir);
|
|
77
71
|
const pagesOptimizeEntries = `${relative(root, pagesDir)}/**/*.{js,jsx,ts,tsx}`;
|
|
78
|
-
|
|
79
|
-
const rescanPages = async () => {
|
|
80
|
-
pageScan = existsSync(pagesDir) ? await scanPages(root, {
|
|
81
|
-
pagesDir,
|
|
82
|
-
environment: isDev ? "dev" : "prod"
|
|
83
|
-
}) : null;
|
|
84
|
-
};
|
|
85
|
-
const runRescan = () => {
|
|
86
|
-
pendingRescan ??= rescanPages().finally(() => {
|
|
87
|
-
pendingRescan = null;
|
|
88
|
-
});
|
|
89
|
-
return pendingRescan;
|
|
90
|
-
};
|
|
91
|
-
const invalidatePageModules = (server) => {
|
|
92
|
-
for (const env of Object.values(server.environments)) for (const id of ["\0" + RENDERER_ID, "\0" + CLIENT_ENTRY_ID]) {
|
|
93
|
-
const mod = env.moduleGraph.getModuleById(id);
|
|
94
|
-
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
return {
|
|
72
|
+
return createPagesPlugin(pagesDir, {
|
|
98
73
|
name: "void-react:pages",
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
resolveId: {
|
|
103
|
-
filter: { id: /^virtual:void-pages-(renderer|client)$/ },
|
|
104
|
-
handler(id) {
|
|
105
|
-
return "\0" + id;
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
load: {
|
|
109
|
-
filter: { id: /^\0virtual:void-pages-(renderer|client)$/ },
|
|
110
|
-
async handler(id) {
|
|
111
|
-
if (isDev && pendingRescan) await pendingRescan;
|
|
112
|
-
if (!pageScan && existsSync(pagesDir)) await runRescan();
|
|
113
|
-
if (id === "\0" + RENDERER_ID) {
|
|
114
|
-
if (!pageScan) return "export function renderPage() { throw new Error('No pages found'); }";
|
|
115
|
-
return generateRendererModule(pageScan, pagesDir, isDev);
|
|
116
|
-
}
|
|
117
|
-
if (id === "\0" + CLIENT_ENTRY_ID) {
|
|
118
|
-
if (!pageScan) return "";
|
|
119
|
-
return generateClientEntry(pageScan, pagesDir, options?.viewTransitions, options?.prefetch, resolveStaticPageDataBuildOptions(root, { publicDir: viteConfig?.publicDir }));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
74
|
+
root,
|
|
75
|
+
generateRenderer(scan, isDev) {
|
|
76
|
+
return generateRendererModule(scan, pagesDir, isDev);
|
|
122
77
|
},
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const normalizedPath = normalizePath(path);
|
|
126
|
-
if (normalizedPath !== normalizedPagesDir && !normalizedPath.startsWith(normalizedPagesDir + "/")) return;
|
|
127
|
-
if (event === "change" && !normalizedPath.endsWith(".md")) return;
|
|
128
|
-
try {
|
|
129
|
-
await runRescan();
|
|
130
|
-
} catch (error) {
|
|
131
|
-
server.config.logger.error(error instanceof Error ? error.message : String(error), {
|
|
132
|
-
error: error instanceof Error ? error : void 0,
|
|
133
|
-
timestamp: true
|
|
134
|
-
});
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
invalidatePageModules(server);
|
|
138
|
-
};
|
|
139
|
-
server.watcher.on("add", (path) => handlePagesChange(path, "add"));
|
|
140
|
-
server.watcher.on("change", (path) => handlePagesChange(path, "change"));
|
|
141
|
-
server.watcher.on("unlink", (path) => handlePagesChange(path, "unlink"));
|
|
142
|
-
},
|
|
143
|
-
configResolved(config) {
|
|
144
|
-
viteConfig = config;
|
|
78
|
+
generateClient(scan, staticPageData) {
|
|
79
|
+
return generateClientEntry(scan, pagesDir, options?.viewTransitions, options?.prefetch, staticPageData);
|
|
145
80
|
},
|
|
146
|
-
async config(
|
|
147
|
-
isDev = env.command === "serve";
|
|
148
|
-
if (!existsSync(pagesDir)) return;
|
|
81
|
+
async config({ isDev, scan }) {
|
|
149
82
|
if (isDev) return {
|
|
150
83
|
resolve: { dedupe: REACT_DEDUPE },
|
|
151
84
|
optimizeDeps: {
|
|
152
85
|
exclude: ["@void/react"],
|
|
153
|
-
|
|
86
|
+
entries: clientOptimizeEntries(await scan(), pagesDir, root),
|
|
87
|
+
include: [
|
|
88
|
+
...CLIENT_OPTIMIZE_DEPS,
|
|
89
|
+
"void/pages-client",
|
|
90
|
+
"void/pages-prefetch"
|
|
91
|
+
]
|
|
154
92
|
},
|
|
155
93
|
ssr: { optimizeDeps: { include: [...CLIENT_OPTIMIZE_DEPS, "react-dom/server"] } },
|
|
156
94
|
environments: { [WORKER_ENVIRONMENT_NAME]: { optimizeDeps: {
|
|
@@ -159,8 +97,7 @@ function pagesPlugin(pagesDir, options) {
|
|
|
159
97
|
include: WORKER_OPTIMIZE_DEPS
|
|
160
98
|
} } }
|
|
161
99
|
};
|
|
162
|
-
await
|
|
163
|
-
if (!pageScan?.pages.some((page) => !page.island)) return;
|
|
100
|
+
if (!(await scan())?.pages.some((page) => !page.island)) return;
|
|
164
101
|
return {
|
|
165
102
|
resolve: { dedupe: REACT_DEDUPE },
|
|
166
103
|
build: {
|
|
@@ -169,7 +106,35 @@ function pagesPlugin(pagesDir, options) {
|
|
|
169
106
|
}
|
|
170
107
|
};
|
|
171
108
|
}
|
|
172
|
-
};
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function clientOptimizeEntries(scan, pagesDir, root) {
|
|
112
|
+
if (!scan) return [];
|
|
113
|
+
const browserFiles = /* @__PURE__ */ new Set();
|
|
114
|
+
const islandFiles = /* @__PURE__ */ new Set();
|
|
115
|
+
for (const page of scan.pages) {
|
|
116
|
+
const layouts = resolveEffectiveLayoutChain(page, scan.layouts, scan.namedLayouts);
|
|
117
|
+
if (page.island) {
|
|
118
|
+
islandFiles.add(resolve(pagesDir, page.componentPath));
|
|
119
|
+
for (const layout of layouts) if (layout.island) islandFiles.add(resolve(pagesDir, layout.filePath));
|
|
120
|
+
} else {
|
|
121
|
+
browserFiles.add(resolve(pagesDir, page.componentPath));
|
|
122
|
+
for (const layout of layouts) browserFiles.add(resolve(pagesDir, layout.filePath));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const file of islandFiles) {
|
|
126
|
+
if (!/\.island\.(tsx|jsx)$/.test(file)) continue;
|
|
127
|
+
for (const { specifier } of findIslandImports(readFileSync(file, "utf8"), file)) {
|
|
128
|
+
const component = resolveSpecifier(specifier, dirname(file), [
|
|
129
|
+
".tsx",
|
|
130
|
+
".jsx",
|
|
131
|
+
".ts",
|
|
132
|
+
".js"
|
|
133
|
+
]);
|
|
134
|
+
if (component) browserFiles.add(component);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return [...browserFiles].map((file) => relative(root, file).replace(/[()[\]{}*?!|]/g, "\\$&"));
|
|
173
138
|
}
|
|
174
139
|
function generateRendererModule(scan, pagesDir, isDev) {
|
|
175
140
|
const preambleScript = isDev ? REACT_REFRESH_PREAMBLE : "";
|
|
@@ -213,8 +178,7 @@ function generateClientEntry(scan, pagesDir, viewTransitions, prefetch, staticPa
|
|
|
213
178
|
export { router };
|
|
214
179
|
`;
|
|
215
180
|
}
|
|
216
|
-
|
|
217
|
-
//#region src/plugin-islands.ts
|
|
181
|
+
// src/plugin-islands.ts
|
|
218
182
|
const COMPONENT_EXTENSIONS = [
|
|
219
183
|
".tsx",
|
|
220
184
|
".jsx",
|
|
@@ -465,8 +429,7 @@ initIslands(islands, hydrateIsland);
|
|
|
465
429
|
`);
|
|
466
430
|
return lines.join("\n");
|
|
467
431
|
}
|
|
468
|
-
|
|
469
|
-
//#region src/plugin.ts
|
|
432
|
+
// src/plugin.ts
|
|
470
433
|
function voidReact(options) {
|
|
471
434
|
const root = process.cwd();
|
|
472
435
|
const pagesDir = resolvePagesDir(root);
|
|
@@ -480,5 +443,4 @@ function voidReact(options) {
|
|
|
480
443
|
...islandsPlugin(pagesDir, { root })
|
|
481
444
|
];
|
|
482
445
|
}
|
|
483
|
-
//#endregion
|
|
484
446
|
export { voidReact };
|
|
@@ -2,7 +2,6 @@ import { ComponentType } from "react";
|
|
|
2
2
|
import { VoidRouter } from "void/pages-client";
|
|
3
3
|
import { CacheForResult } from "void/pages-prefetch";
|
|
4
4
|
import { PageObject } from "void/pages-protocol";
|
|
5
|
-
//#region src/runtime/pages-client.d.ts
|
|
6
5
|
type ComponentModule = {
|
|
7
6
|
default: ComponentType<any>;
|
|
8
7
|
};
|
|
@@ -29,5 +28,4 @@ export type ReactPagesClient = Readonly<{
|
|
|
29
28
|
router: VoidRouter;
|
|
30
29
|
}>;
|
|
31
30
|
export declare function startReactPages(config: ReactPagesConfig): Promise<ReactPagesClient>;
|
|
32
|
-
//#endregion
|
|
33
31
|
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { a as RouterContext, i as ParamsContext, o as SharedContext, r as NavigationContext, t as ErrorsContext } from "../context-
|
|
2
|
-
import { n as setActionRouter } from "../action-
|
|
3
|
-
import { n as DEFERRED_SHELL_END_ID } from "../deferred-shell-
|
|
1
|
+
import { a as RouterContext, i as ParamsContext, o as SharedContext, r as NavigationContext, t as ErrorsContext } from "../context-BQpawTZc.mjs";
|
|
2
|
+
import { n as setActionRouter } from "../action-CyCVdW12.mjs";
|
|
3
|
+
import { n as DEFERRED_SHELL_END_ID } from "../deferred-shell-D5T32zXz.mjs";
|
|
4
4
|
import React, { Suspense, startTransition, use, useCallback, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useReducer, useState, useTransition } from "react";
|
|
5
|
-
import { createPageNavigationEngine, createRouterFacade, idleNavigationState, isAbortError, isCallSiteActionError, refreshPage } from "void/pages-client";
|
|
5
|
+
import { createPageNavigationEngine, createRouterFacade, idleNavigationState, isAbortError, isCallSiteActionError, refreshPage, toUrlString } from "void/pages-client";
|
|
6
6
|
import { createRoot, hydrateRoot } from "react-dom/client";
|
|
7
|
-
|
|
7
|
+
// src/runtime/pages-client.ts
|
|
8
8
|
const deferredState = /* @__PURE__ */ new WeakMap();
|
|
9
9
|
const viewTransitionWaiters = /* @__PURE__ */ new Map();
|
|
10
10
|
const pendingVisitSettlers = /* @__PURE__ */ new Map();
|
|
@@ -51,8 +51,9 @@ function currentUrl() {
|
|
|
51
51
|
return window.location.pathname + window.location.search + window.location.hash;
|
|
52
52
|
}
|
|
53
53
|
function normalizeUrl(value) {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const urlString = toUrlString(value);
|
|
55
|
+
if (urlString.startsWith("#")) return urlString;
|
|
56
|
+
const url = new URL(urlString, window.location.origin);
|
|
56
57
|
return url.origin === window.location.origin ? url.pathname + url.search + url.hash : url.href;
|
|
57
58
|
}
|
|
58
59
|
/** Page data holds a decoded path. Compare it with browser URLs in their encoded form. */
|
|
@@ -182,12 +183,11 @@ function RouteResource({ resource, onCommit }) {
|
|
|
182
183
|
resource,
|
|
183
184
|
result
|
|
184
185
|
]);
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
const routeParams = pageData.params ?? EMPTY_PARAMS;
|
|
186
|
+
const pageData = result?.pageData;
|
|
187
|
+
const pageDataUrl = pageData?.url ?? "";
|
|
188
|
+
const routeParams = pageData?.params ?? EMPTY_PARAMS;
|
|
189
189
|
const routeRouter = useMemo(() => {
|
|
190
|
-
const pageUrl = encodedPageUrl(
|
|
190
|
+
const pageUrl = encodedPageUrl(pageDataUrl);
|
|
191
191
|
return createRouterFacade(outerRouter, () => {
|
|
192
192
|
const outerUrl = outerRouter.url;
|
|
193
193
|
const outerPath = outerUrl.pathname + outerUrl.search;
|
|
@@ -198,11 +198,13 @@ function RouteResource({ resource, onCommit }) {
|
|
|
198
198
|
}, () => routeParams);
|
|
199
199
|
}, [
|
|
200
200
|
outerRouter,
|
|
201
|
-
|
|
201
|
+
pageDataUrl,
|
|
202
202
|
resource.fetchUrl,
|
|
203
203
|
resource.hash,
|
|
204
204
|
routeParams
|
|
205
205
|
]);
|
|
206
|
+
if (!result || !pageData) return null;
|
|
207
|
+
const PageComponent = result.component;
|
|
206
208
|
let element = React.createElement(PageComponent, pageData.props);
|
|
207
209
|
for (let i = result.layouts.length - 1; i >= 0; i--) {
|
|
208
210
|
const Layout = result.layouts[i];
|
|
@@ -349,7 +351,7 @@ function App({ engine, router, viewTransitions, navigationFallback: NavigationFa
|
|
|
349
351
|
pendingVisitSettlers.delete(prepared.id);
|
|
350
352
|
if (activePendingNavigationId === prepared.id) activePendingNavigationId = null;
|
|
351
353
|
dispatch({
|
|
352
|
-
type:
|
|
354
|
+
type: "abandon",
|
|
353
355
|
resource: prepared,
|
|
354
356
|
location: currentUrl()
|
|
355
357
|
});
|
|
@@ -512,5 +514,4 @@ async function startReactPages(config) {
|
|
|
512
514
|
engine.start();
|
|
513
515
|
return { router };
|
|
514
516
|
}
|
|
515
|
-
//#endregion
|
|
516
517
|
export { startReactPages };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ComponentType } from "react";
|
|
2
2
|
import { PageObject } from "void/pages-protocol";
|
|
3
|
-
//#region src/runtime/pages-server.d.ts
|
|
4
3
|
type ComponentModule = {
|
|
5
4
|
default: ComponentType<any>;
|
|
6
5
|
};
|
|
@@ -16,5 +15,4 @@ export type ReactPagesServerConfig = Readonly<{
|
|
|
16
15
|
preambleScript?: string;
|
|
17
16
|
}>;
|
|
18
17
|
export declare function renderReactPage({ pageObj, assetTags, components, layoutTree, preambleScript }: ReactPagesServerConfig): Promise<ReadableStream<Uint8Array>>;
|
|
19
|
-
//#endregion
|
|
20
18
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { a as RouterContext, i as ParamsContext, o as SharedContext, r as NavigationContext, t as ErrorsContext } from "../context-
|
|
2
|
-
import { n as DEFERRED_SHELL_END_ID, r as DEFERRED_SHELL_END_MARKER, t as DEFERRED_SHELL_CANCEL_REASON } from "../deferred-shell-
|
|
1
|
+
import { a as RouterContext, i as ParamsContext, o as SharedContext, r as NavigationContext, t as ErrorsContext } from "../context-BQpawTZc.mjs";
|
|
2
|
+
import { n as DEFERRED_SHELL_END_ID, r as DEFERRED_SHELL_END_MARKER, t as DEFERRED_SHELL_CANCEL_REASON } from "../deferred-shell-D5T32zXz.mjs";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { createSsrRouter, idleNavigationState } from "void/pages-client";
|
|
5
5
|
import { renderToReadableStream } from "react-dom/server";
|
|
6
6
|
import { renderBodyAttrs, renderHeadToString, renderHtmlAttrs } from "void/pages-head";
|
|
7
7
|
import { serializePageData } from "void/pages-server";
|
|
8
|
-
|
|
8
|
+
// src/runtime/page-streams.ts
|
|
9
9
|
function concatStreams(streams) {
|
|
10
10
|
let index = 0;
|
|
11
11
|
let reader;
|
|
@@ -94,8 +94,7 @@ function readDeferredShell(stream) {
|
|
|
94
94
|
}
|
|
95
95
|
}, { highWaterMark: 0 });
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
//#region src/runtime/pages-server.ts
|
|
97
|
+
// src/runtime/pages-server.ts
|
|
99
98
|
function createPendingDeferred() {
|
|
100
99
|
return new Promise(() => {});
|
|
101
100
|
}
|
|
@@ -155,5 +154,4 @@ async function renderReactPage({ pageObj, assetTags, components, layoutTree, pre
|
|
|
155
154
|
</html>`)
|
|
156
155
|
]);
|
|
157
156
|
}
|
|
158
|
-
//#endregion
|
|
159
157
|
export { renderReactPage };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "React pages, server rendering, forms, and navigation for Void.",
|
|
5
5
|
"homepage": "https://void.cloud",
|
|
6
6
|
"bugs": {
|
|
@@ -41,21 +41,22 @@
|
|
|
41
41
|
"@vitejs/plugin-react": "^6.1.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@types/node": "^26.6.
|
|
44
|
+
"@types/node": "^26.6.6",
|
|
45
45
|
"@types/react": "^19.3.0",
|
|
46
46
|
"@types/react-dom": "^19.3.0",
|
|
47
|
+
"happy-dom": "^21.0.4",
|
|
47
48
|
"pathslash": "^0.1.0",
|
|
48
49
|
"react": "^19.3.1",
|
|
49
50
|
"react-dom": "^19.3.1",
|
|
50
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@1.
|
|
51
|
-
"vite-plus": "1.
|
|
52
|
-
"void": "1.
|
|
51
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
|
|
52
|
+
"vite-plus": "1.4.0",
|
|
53
|
+
"void": "1.5.0"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|
|
55
56
|
"react": "^19.0.0",
|
|
56
57
|
"react-dom": "^19.0.0",
|
|
57
58
|
"vite": "^8.0.0",
|
|
58
|
-
"void": "1.
|
|
59
|
+
"void": "1.5.0"
|
|
59
60
|
},
|
|
60
61
|
"scripts": {
|
|
61
62
|
"build": "vp pack",
|