@void/react 0.27.0 → 0.28.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/index.d.mts +103 -2
- package/dist/index.mjs +460 -1
- package/dist/plugin.mjs +177 -197
- package/dist/runtime/pages-server.mjs +12 -1
- package/package.json +3 -3
- package/dist/index-D5M3zrVB.d.mts +0 -103
- package/dist/runtime/index.d.mts +0 -2
- package/dist/runtime/index.mjs +0 -4
- package/dist/runtime--GesOVur.mjs +0 -468
package/dist/plugin.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { generateComponentManifest, resolveEffectiveLayoutChain, resolvePagesDir
|
|
|
3
3
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
4
|
import nodePath from "node:path";
|
|
5
5
|
import { normalizePath } from "vite";
|
|
6
|
-
import { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, computePagesNeedingClientJS, editIslandSource,
|
|
6
|
+
import { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, computePagesNeedingClientJS, editIslandSource, findIslandImports, findStaticCssImports, needsIslandClientBundle, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan } from "void/pages-islands-plugin";
|
|
7
7
|
//#region ../../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";
|
|
@@ -43,6 +43,179 @@ 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
45
|
//#endregion
|
|
46
|
+
//#region src/plugin-pages.ts
|
|
47
|
+
const RENDERER_ID = "virtual:void-pages-renderer";
|
|
48
|
+
const CLIENT_ENTRY_ID = "virtual:void-pages-client";
|
|
49
|
+
const WORKER_ENVIRONMENT_NAME = "void_worker";
|
|
50
|
+
const REACT_DEDUPE = ["react", "react-dom"];
|
|
51
|
+
const REACT_OPTIMIZE_DEPS = [
|
|
52
|
+
"react",
|
|
53
|
+
"react/jsx-runtime",
|
|
54
|
+
"react/jsx-dev-runtime",
|
|
55
|
+
"react-dom",
|
|
56
|
+
"react-dom/client",
|
|
57
|
+
"react-dom/server"
|
|
58
|
+
];
|
|
59
|
+
const WORKER_OPTIMIZE_DEPS = [...REACT_OPTIMIZE_DEPS, "void/live/client"];
|
|
60
|
+
const REACT_REFRESH_PREAMBLE = `
|
|
61
|
+
<script type="module">
|
|
62
|
+
import RefreshRuntime from "/@react-refresh"
|
|
63
|
+
RefreshRuntime.injectIntoGlobalHook(window)
|
|
64
|
+
window.$RefreshReg$ = () => {}
|
|
65
|
+
window.$RefreshSig$ = () => (type) => type
|
|
66
|
+
window.__vite_plugin_react_preamble_installed__ = true
|
|
67
|
+
<\/script>`;
|
|
68
|
+
/** Check if the pages directory has any non-island page files (.tsx/.jsx without .island.). */
|
|
69
|
+
function hasNonIslandPages(dir) {
|
|
70
|
+
try {
|
|
71
|
+
for (const entry of readdirSync(dir, {
|
|
72
|
+
withFileTypes: true,
|
|
73
|
+
recursive: true
|
|
74
|
+
})) {
|
|
75
|
+
if (!entry.isFile()) continue;
|
|
76
|
+
const name = entry.name;
|
|
77
|
+
if (name.startsWith("_") || name.startsWith("layout.")) continue;
|
|
78
|
+
if (/\.(tsx|jsx)$/.test(name) && !name.includes(".island.")) return true;
|
|
79
|
+
}
|
|
80
|
+
} catch {}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
function pagesPlugin(pagesDir, options) {
|
|
84
|
+
let pageScan = null;
|
|
85
|
+
let isDev = false;
|
|
86
|
+
let viteConfig;
|
|
87
|
+
const root = options?.root ?? dirname(pagesDir);
|
|
88
|
+
const normalizedPagesDir = normalizePath(pagesDir);
|
|
89
|
+
const pagesOptimizeEntries = `${relative(root, pagesDir)}/**/*.{js,jsx,ts,tsx}`;
|
|
90
|
+
let pendingRescan = null;
|
|
91
|
+
const rescanPages = async () => {
|
|
92
|
+
pageScan = existsSync(pagesDir) ? await scanPages(root, { pagesDir }) : null;
|
|
93
|
+
};
|
|
94
|
+
const runRescan = () => {
|
|
95
|
+
pendingRescan ??= rescanPages().finally(() => {
|
|
96
|
+
pendingRescan = null;
|
|
97
|
+
});
|
|
98
|
+
return pendingRescan;
|
|
99
|
+
};
|
|
100
|
+
const invalidatePageModules = (server) => {
|
|
101
|
+
for (const env of Object.values(server.environments)) for (const id of ["\0" + RENDERER_ID, "\0" + CLIENT_ENTRY_ID]) {
|
|
102
|
+
const mod = env.moduleGraph.getModuleById(id);
|
|
103
|
+
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
return {
|
|
107
|
+
name: "void-react:pages",
|
|
108
|
+
async buildStart() {
|
|
109
|
+
await runRescan();
|
|
110
|
+
},
|
|
111
|
+
resolveId: {
|
|
112
|
+
filter: { id: /^virtual:void-pages-(renderer|client)$/ },
|
|
113
|
+
handler(id) {
|
|
114
|
+
return "\0" + id;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
load: {
|
|
118
|
+
filter: { id: /^\0virtual:void-pages-(renderer|client)$/ },
|
|
119
|
+
async handler(id) {
|
|
120
|
+
if (isDev && pendingRescan) await pendingRescan;
|
|
121
|
+
if (!pageScan && existsSync(pagesDir)) await runRescan();
|
|
122
|
+
if (id === "\0" + RENDERER_ID) {
|
|
123
|
+
if (!pageScan) return "export function renderPage() { throw new Error('No pages found'); }";
|
|
124
|
+
return generateRendererModule(pageScan, pagesDir, isDev);
|
|
125
|
+
}
|
|
126
|
+
if (id === "\0" + CLIENT_ENTRY_ID) {
|
|
127
|
+
if (!pageScan) return "";
|
|
128
|
+
return generateClientEntry(pageScan, pagesDir, options?.viewTransitions, options?.prefetch, resolveStaticPageDataBuildOptions(root, { publicDir: viteConfig?.publicDir }));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
configureServer(server) {
|
|
133
|
+
const handlePagesChange = async (path, event) => {
|
|
134
|
+
const normalizedPath = normalizePath(path);
|
|
135
|
+
if (normalizedPath !== normalizedPagesDir && !normalizedPath.startsWith(normalizedPagesDir + "/")) return;
|
|
136
|
+
if (event === "change" && !normalizedPath.endsWith(".md")) return;
|
|
137
|
+
await runRescan();
|
|
138
|
+
invalidatePageModules(server);
|
|
139
|
+
};
|
|
140
|
+
server.watcher.on("add", (path) => handlePagesChange(path, "add"));
|
|
141
|
+
server.watcher.on("change", (path) => handlePagesChange(path, "change"));
|
|
142
|
+
server.watcher.on("unlink", (path) => handlePagesChange(path, "unlink"));
|
|
143
|
+
},
|
|
144
|
+
configResolved(config) {
|
|
145
|
+
viteConfig = config;
|
|
146
|
+
},
|
|
147
|
+
config(_, env) {
|
|
148
|
+
if (!existsSync(pagesDir)) return;
|
|
149
|
+
if (env.command === "serve") {
|
|
150
|
+
isDev = true;
|
|
151
|
+
return {
|
|
152
|
+
resolve: { dedupe: REACT_DEDUPE },
|
|
153
|
+
optimizeDeps: {
|
|
154
|
+
exclude: ["@void/react"],
|
|
155
|
+
include: REACT_OPTIMIZE_DEPS
|
|
156
|
+
},
|
|
157
|
+
ssr: { optimizeDeps: { include: REACT_OPTIMIZE_DEPS } },
|
|
158
|
+
environments: { [WORKER_ENVIRONMENT_NAME]: { optimizeDeps: {
|
|
159
|
+
exclude: ["@void/react"],
|
|
160
|
+
entries: [pagesOptimizeEntries],
|
|
161
|
+
include: WORKER_OPTIMIZE_DEPS
|
|
162
|
+
} } }
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (!hasNonIslandPages(pagesDir)) return;
|
|
166
|
+
return {
|
|
167
|
+
resolve: { dedupe: REACT_DEDUPE },
|
|
168
|
+
build: {
|
|
169
|
+
manifest: true,
|
|
170
|
+
rollupOptions: { input: { "pages-client": CLIENT_ENTRY_ID } }
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function generateRendererModule(scan, pagesDir, isDev) {
|
|
177
|
+
const preambleScript = isDev ? REACT_REFRESH_PREAMBLE : "";
|
|
178
|
+
return `
|
|
179
|
+
import { renderReactPage } from "@void/react/pages-server";
|
|
180
|
+
|
|
181
|
+
${generateComponentManifest(scan, pagesDir)}
|
|
182
|
+
|
|
183
|
+
const PREAMBLE_SCRIPT = ${JSON.stringify(preambleScript)};
|
|
184
|
+
|
|
185
|
+
export async function renderPage(pageObj, assetTags) {
|
|
186
|
+
return renderReactPage({
|
|
187
|
+
pageObj,
|
|
188
|
+
assetTags,
|
|
189
|
+
components,
|
|
190
|
+
layoutTree,
|
|
191
|
+
preambleScript: PREAMBLE_SCRIPT,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
renderPage.preambleScript = PREAMBLE_SCRIPT;
|
|
195
|
+
`;
|
|
196
|
+
}
|
|
197
|
+
function generateClientEntry(scan, pagesDir, viewTransitions, prefetch, staticPageData) {
|
|
198
|
+
return `
|
|
199
|
+
import { startReactPages } from "@void/react/pages-client";
|
|
200
|
+
import { parseCacheFor } from "void/pages-prefetch";
|
|
201
|
+
|
|
202
|
+
${generateComponentManifest(scan, pagesDir)}
|
|
203
|
+
|
|
204
|
+
const { router } = await startReactPages({
|
|
205
|
+
components,
|
|
206
|
+
layoutTree,
|
|
207
|
+
routeMeta,
|
|
208
|
+
matchRoute,
|
|
209
|
+
staticPageData: ${!!staticPageData?.enabled},
|
|
210
|
+
staticPageDataFastPath: ${!!staticPageData?.fastPath},
|
|
211
|
+
viewTransitions: ${!!viewTransitions},
|
|
212
|
+
prefetchHoverDelay: ${prefetch?.hoverDelay ?? 75},
|
|
213
|
+
prefetchDefaultCacheFor: parseCacheFor(${JSON.stringify(prefetch?.cacheFor ?? "30s")}),
|
|
214
|
+
});
|
|
215
|
+
export { router };
|
|
216
|
+
`;
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
46
219
|
//#region src/plugin-islands.ts
|
|
47
220
|
const COMPONENT_EXTENSIONS = [
|
|
48
221
|
".tsx",
|
|
@@ -56,7 +229,6 @@ function islandsPlugin(pagesDir, options) {
|
|
|
56
229
|
const root = options?.root ?? dirname(pagesDir);
|
|
57
230
|
const islandManifest = /* @__PURE__ */ new Map();
|
|
58
231
|
const islandCssFiles = /* @__PURE__ */ new Set();
|
|
59
|
-
let viteRoot = "";
|
|
60
232
|
let resolvedConfig = null;
|
|
61
233
|
/** Resolve an island import and record it in the client manifest. */
|
|
62
234
|
function registerIsland(spec, importerDir, hydrate) {
|
|
@@ -80,7 +252,6 @@ function islandsPlugin(pagesDir, options) {
|
|
|
80
252
|
name: "void-react:islands",
|
|
81
253
|
configResolved(config) {
|
|
82
254
|
resolvedConfig = config;
|
|
83
|
-
viteRoot = config.root;
|
|
84
255
|
isDev = config.command === "serve";
|
|
85
256
|
},
|
|
86
257
|
configureServer(server) {
|
|
@@ -121,21 +292,13 @@ function islandsPlugin(pagesDir, options) {
|
|
|
121
292
|
}
|
|
122
293
|
},
|
|
123
294
|
resolveId: {
|
|
124
|
-
filter: { id: /^virtual:void-islands-(renderer|client)
|
|
295
|
+
filter: { id: /^virtual:void-islands-(renderer|client)$/ },
|
|
125
296
|
handler(source) {
|
|
126
297
|
if (source === ISLANDS_RENDERER_ID || source === ISLANDS_CLIENT_ENTRY_ID) return "\0" + source;
|
|
127
|
-
if (source.endsWith(".css")) {
|
|
128
|
-
const srcPath = normalizePath(source.slice(0, -4));
|
|
129
|
-
if (islandCssFiles.has(srcPath)) return srcPath + ".css";
|
|
130
|
-
if (srcPath.startsWith("/")) {
|
|
131
|
-
const absPath = normalizePath(resolve(viteRoot, srcPath.slice(1)));
|
|
132
|
-
if (islandCssFiles.has(absPath)) return absPath + ".css";
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
298
|
}
|
|
136
299
|
},
|
|
137
300
|
load: {
|
|
138
|
-
filter: { id: /^\0virtual:void-islands-(renderer|client)
|
|
301
|
+
filter: { id: /^\0virtual:void-islands-(renderer|client)$/ },
|
|
139
302
|
handler(id) {
|
|
140
303
|
if (id === "\0" + ISLANDS_RENDERER_ID) {
|
|
141
304
|
if (!pageScan) return "export function renderIslandPage() { throw new Error('No pages found'); }";
|
|
@@ -148,10 +311,6 @@ function islandsPlugin(pagesDir, options) {
|
|
|
148
311
|
if (!pageScan) return "";
|
|
149
312
|
return generateIslandClientEntry(islandManifest, islandCssFiles, isDev);
|
|
150
313
|
}
|
|
151
|
-
if (id.endsWith(".css") && !id.startsWith("\0")) {
|
|
152
|
-
const srcPath = id.slice(0, -4);
|
|
153
|
-
if (islandCssFiles.has(srcPath)) return extractCssImports(srcPath);
|
|
154
|
-
}
|
|
155
314
|
}
|
|
156
315
|
},
|
|
157
316
|
transform: {
|
|
@@ -193,14 +352,7 @@ function islandsPlugin(pagesDir, options) {
|
|
|
193
352
|
}];
|
|
194
353
|
}
|
|
195
354
|
function generateIslandRenderer(scan, pagesDir, isDev, mdScriptIds, pagesNeedingClientJS) {
|
|
196
|
-
const preambleScript = isDev ?
|
|
197
|
-
<script type="module">
|
|
198
|
-
import RefreshRuntime from "/@react-refresh"
|
|
199
|
-
RefreshRuntime.injectIntoGlobalHook(window)
|
|
200
|
-
window.$RefreshReg$ = () => {}
|
|
201
|
-
window.$RefreshSig$ = () => (type) => type
|
|
202
|
-
window.__vite_plugin_react_preamble_installed__ = true
|
|
203
|
-
<\/script>` : "";
|
|
355
|
+
const preambleScript = isDev ? REACT_REFRESH_PREAMBLE : "";
|
|
204
356
|
const mdScriptIdsCode = `const __mdClientScriptIds = new Set(${JSON.stringify([...mdScriptIds])});`;
|
|
205
357
|
const needsJSCode = `const __pagesNeedingClientJS = new Set(${JSON.stringify([...pagesNeedingClientJS])});`;
|
|
206
358
|
return `
|
|
@@ -295,178 +447,6 @@ initIslands(islands, hydrateIsland);
|
|
|
295
447
|
return lines.join("\n");
|
|
296
448
|
}
|
|
297
449
|
//#endregion
|
|
298
|
-
//#region src/plugin-pages.ts
|
|
299
|
-
const RENDERER_ID = "virtual:void-pages-renderer";
|
|
300
|
-
const CLIENT_ENTRY_ID = "virtual:void-pages-client";
|
|
301
|
-
const WORKER_ENVIRONMENT_NAME = "void_worker";
|
|
302
|
-
const REACT_DEDUPE = ["react", "react-dom"];
|
|
303
|
-
const REACT_OPTIMIZE_DEPS = [
|
|
304
|
-
"react",
|
|
305
|
-
"react/jsx-runtime",
|
|
306
|
-
"react/jsx-dev-runtime",
|
|
307
|
-
"react-dom",
|
|
308
|
-
"react-dom/client",
|
|
309
|
-
"react-dom/server"
|
|
310
|
-
];
|
|
311
|
-
const WORKER_OPTIMIZE_DEPS = [...REACT_OPTIMIZE_DEPS, "void/live/client"];
|
|
312
|
-
/** Check if the pages directory has any non-island page files (.tsx/.jsx without .island.). */
|
|
313
|
-
function hasNonIslandPages(dir) {
|
|
314
|
-
try {
|
|
315
|
-
for (const entry of readdirSync(dir, {
|
|
316
|
-
withFileTypes: true,
|
|
317
|
-
recursive: true
|
|
318
|
-
})) {
|
|
319
|
-
if (!entry.isFile()) continue;
|
|
320
|
-
const name = entry.name;
|
|
321
|
-
if (name.startsWith("_") || name.startsWith("layout.")) continue;
|
|
322
|
-
if (/\.(tsx|jsx)$/.test(name) && !name.includes(".island.")) return true;
|
|
323
|
-
}
|
|
324
|
-
} catch {}
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
function pagesPlugin(pagesDir, options) {
|
|
328
|
-
let pageScan = null;
|
|
329
|
-
let isDev = false;
|
|
330
|
-
let viteConfig;
|
|
331
|
-
const root = options?.root ?? dirname(pagesDir);
|
|
332
|
-
const normalizedPagesDir = normalizePath(pagesDir);
|
|
333
|
-
const pagesOptimizeEntries = `${relative(root, pagesDir)}/**/*.{js,jsx,ts,tsx}`;
|
|
334
|
-
let pendingRescan = null;
|
|
335
|
-
const rescanPages = async () => {
|
|
336
|
-
pageScan = existsSync(pagesDir) ? await scanPages(root, { pagesDir }) : null;
|
|
337
|
-
};
|
|
338
|
-
const runRescan = () => {
|
|
339
|
-
pendingRescan ??= rescanPages().finally(() => {
|
|
340
|
-
pendingRescan = null;
|
|
341
|
-
});
|
|
342
|
-
return pendingRescan;
|
|
343
|
-
};
|
|
344
|
-
const invalidatePageModules = (server) => {
|
|
345
|
-
for (const env of Object.values(server.environments)) for (const id of ["\0" + RENDERER_ID, "\0" + CLIENT_ENTRY_ID]) {
|
|
346
|
-
const mod = env.moduleGraph.getModuleById(id);
|
|
347
|
-
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
return {
|
|
351
|
-
name: "void-react:pages",
|
|
352
|
-
async buildStart() {
|
|
353
|
-
await runRescan();
|
|
354
|
-
},
|
|
355
|
-
resolveId: {
|
|
356
|
-
filter: { id: /^virtual:void-pages-(renderer|client)$/ },
|
|
357
|
-
handler(id) {
|
|
358
|
-
return "\0" + id;
|
|
359
|
-
}
|
|
360
|
-
},
|
|
361
|
-
load: {
|
|
362
|
-
filter: { id: /^\0virtual:void-pages-(renderer|client)$/ },
|
|
363
|
-
async handler(id) {
|
|
364
|
-
if (isDev && pendingRescan) await pendingRescan;
|
|
365
|
-
if (!pageScan && existsSync(pagesDir)) await runRescan();
|
|
366
|
-
if (id === "\0" + RENDERER_ID) {
|
|
367
|
-
if (!pageScan) return "export function renderPage() { throw new Error('No pages found'); }";
|
|
368
|
-
return generateRendererModule(pageScan, pagesDir, isDev);
|
|
369
|
-
}
|
|
370
|
-
if (id === "\0" + CLIENT_ENTRY_ID) {
|
|
371
|
-
if (!pageScan) return "";
|
|
372
|
-
return generateClientEntry(pageScan, pagesDir, options?.viewTransitions, options?.prefetch, resolveStaticPageDataBuildOptions(root, { publicDir: viteConfig?.publicDir }));
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
},
|
|
376
|
-
configureServer(server) {
|
|
377
|
-
const handlePagesChange = async (path, event) => {
|
|
378
|
-
const normalizedPath = normalizePath(path);
|
|
379
|
-
if (normalizedPath !== normalizedPagesDir && !normalizedPath.startsWith(normalizedPagesDir + "/")) return;
|
|
380
|
-
if (event === "change" && !normalizedPath.endsWith(".md")) return;
|
|
381
|
-
await runRescan();
|
|
382
|
-
invalidatePageModules(server);
|
|
383
|
-
};
|
|
384
|
-
server.watcher.on("add", (path) => handlePagesChange(path, "add"));
|
|
385
|
-
server.watcher.on("change", (path) => handlePagesChange(path, "change"));
|
|
386
|
-
server.watcher.on("unlink", (path) => handlePagesChange(path, "unlink"));
|
|
387
|
-
},
|
|
388
|
-
configResolved(config) {
|
|
389
|
-
viteConfig = config;
|
|
390
|
-
},
|
|
391
|
-
config(_, env) {
|
|
392
|
-
if (!existsSync(pagesDir)) return;
|
|
393
|
-
if (env.command === "serve") {
|
|
394
|
-
isDev = true;
|
|
395
|
-
return {
|
|
396
|
-
resolve: { dedupe: REACT_DEDUPE },
|
|
397
|
-
optimizeDeps: {
|
|
398
|
-
exclude: ["@void/react"],
|
|
399
|
-
include: REACT_OPTIMIZE_DEPS
|
|
400
|
-
},
|
|
401
|
-
ssr: { optimizeDeps: { include: REACT_OPTIMIZE_DEPS } },
|
|
402
|
-
environments: { [WORKER_ENVIRONMENT_NAME]: { optimizeDeps: {
|
|
403
|
-
exclude: ["@void/react"],
|
|
404
|
-
entries: [pagesOptimizeEntries],
|
|
405
|
-
include: WORKER_OPTIMIZE_DEPS
|
|
406
|
-
} } }
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
if (!hasNonIslandPages(pagesDir)) return;
|
|
410
|
-
return {
|
|
411
|
-
resolve: { dedupe: REACT_DEDUPE },
|
|
412
|
-
build: {
|
|
413
|
-
manifest: true,
|
|
414
|
-
rollupOptions: { input: { "pages-client": CLIENT_ENTRY_ID } }
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
function generateRendererModule(scan, pagesDir, isDev) {
|
|
421
|
-
const preambleScript = isDev ? `
|
|
422
|
-
<script type="module">
|
|
423
|
-
import RefreshRuntime from "/@react-refresh"
|
|
424
|
-
RefreshRuntime.injectIntoGlobalHook(window)
|
|
425
|
-
window.$RefreshReg$ = () => {}
|
|
426
|
-
window.$RefreshSig$ = () => (type) => type
|
|
427
|
-
window.__vite_plugin_react_preamble_installed__ = true
|
|
428
|
-
<\/script>` : "";
|
|
429
|
-
return `
|
|
430
|
-
import { renderReactPage } from "@void/react/pages-server";
|
|
431
|
-
|
|
432
|
-
${generateComponentManifest(scan, pagesDir)}
|
|
433
|
-
|
|
434
|
-
const PREAMBLE_SCRIPT = ${JSON.stringify(preambleScript)};
|
|
435
|
-
|
|
436
|
-
export async function renderPage(pageObj, assetTags) {
|
|
437
|
-
return renderReactPage({
|
|
438
|
-
pageObj,
|
|
439
|
-
assetTags,
|
|
440
|
-
components,
|
|
441
|
-
layoutTree,
|
|
442
|
-
preambleScript: PREAMBLE_SCRIPT,
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
renderPage.preambleScript = PREAMBLE_SCRIPT;
|
|
446
|
-
`;
|
|
447
|
-
}
|
|
448
|
-
function generateClientEntry(scan, pagesDir, viewTransitions, prefetch, staticPageData) {
|
|
449
|
-
return `
|
|
450
|
-
import { startReactPages } from "@void/react/pages-client";
|
|
451
|
-
import { parseCacheFor } from "void/pages-prefetch";
|
|
452
|
-
|
|
453
|
-
${generateComponentManifest(scan, pagesDir)}
|
|
454
|
-
|
|
455
|
-
const { router } = await startReactPages({
|
|
456
|
-
components,
|
|
457
|
-
layoutTree,
|
|
458
|
-
routeMeta,
|
|
459
|
-
matchRoute,
|
|
460
|
-
staticPageData: ${!!staticPageData?.enabled},
|
|
461
|
-
staticPageDataFastPath: ${!!staticPageData?.fastPath},
|
|
462
|
-
viewTransitions: ${!!viewTransitions},
|
|
463
|
-
prefetchHoverDelay: ${prefetch?.hoverDelay ?? 75},
|
|
464
|
-
prefetchDefaultCacheFor: parseCacheFor(${JSON.stringify(prefetch?.cacheFor ?? "30s")}),
|
|
465
|
-
});
|
|
466
|
-
export { router };
|
|
467
|
-
`;
|
|
468
|
-
}
|
|
469
|
-
//#endregion
|
|
470
450
|
//#region src/plugin.ts
|
|
471
451
|
function voidReact(options) {
|
|
472
452
|
const root = process.cwd();
|
|
@@ -121,10 +121,21 @@ async function renderReactPage({ pageObj, assetTags, components, layoutTree, pre
|
|
|
121
121
|
for (let i = layoutComponents.length - 1; i >= 0; i--) element = React.createElement(layoutComponents[i], null, element);
|
|
122
122
|
element = React.createElement(RouterContext.Provider, { value: createSsrRouter(pageObj.url || "/", pageObj.params ?? {}) }, React.createElement(NavigationContext.Provider, { value: idleNavigationState() }, React.createElement(React.Suspense, { fallback: null }, React.createElement(SharedContext.Provider, { value: pageObj.shared || {} }, React.createElement(ParamsContext.Provider, { value: pageObj.params ?? {} }, React.createElement(ErrorsContext.Provider, { value: pageObj.errors || {} }, element))))));
|
|
123
123
|
const renderElement = pageObj.deferredKeys ? React.createElement(React.Fragment, null, element, React.createElement("template", { id: DEFERRED_SHELL_END_ID })) : element;
|
|
124
|
+
let shellError;
|
|
125
|
+
let shellSent = false;
|
|
124
126
|
const appStream = await renderToReadableStream(renderElement, { onError(error) {
|
|
125
127
|
if (error === "Void deferred shell complete") return;
|
|
126
|
-
|
|
128
|
+
if (shellSent) {
|
|
129
|
+
console.error(error);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
shellError ??= { error };
|
|
127
133
|
} });
|
|
134
|
+
if (shellError) {
|
|
135
|
+
await appStream.cancel(shellError.error);
|
|
136
|
+
throw shellError.error;
|
|
137
|
+
}
|
|
138
|
+
shellSent = true;
|
|
128
139
|
const renderedAppStream = pageObj.deferredKeys ? readDeferredShell(appStream) : appStream;
|
|
129
140
|
const pageData = serializePageData(pageObj);
|
|
130
141
|
const headHtml = pageObj.head ? renderHeadToString(pageObj.head) : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "React pages, server rendering, forms, and navigation for Void.",
|
|
5
5
|
"homepage": "https://void.cloud",
|
|
6
6
|
"bugs": {
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
"react-dom": "^19.3.0",
|
|
50
50
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
51
51
|
"vite-plus": "1.1.0",
|
|
52
|
-
"void": "0.
|
|
52
|
+
"void": "0.28.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"react": "^19.0.0",
|
|
56
56
|
"react-dom": "^19.0.0",
|
|
57
57
|
"vite": "^8.0.0",
|
|
58
|
-
"void": "0.
|
|
58
|
+
"void": "0.28.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "vp pack",
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { AnchorHTMLAttributes, ButtonHTMLAttributes, Context, FormHTMLAttributes, JSX, ReactElement, ReactNode } from "react";
|
|
2
|
-
import { ActionClient, FormMethod, FormModel, NavigationState, VoidActionError, VoidRouter } from "void/pages-client";
|
|
3
|
-
import { CloudContextVariables, InferProps } from "void";
|
|
4
|
-
import { ActionFormOptions, ActionUrl, ResolveActionBody } from "void/routes";
|
|
5
|
-
//#region src/runtime/form.d.ts
|
|
6
|
-
type FormProps = Omit<FormHTMLAttributes<HTMLFormElement>, "action" | "onSubmit" | "method" | "onError"> & {
|
|
7
|
-
form: FormModel<[FormData]>;
|
|
8
|
-
method?: FormMethod;
|
|
9
|
-
renderError?: (error: VoidActionError) => ReactNode;
|
|
10
|
-
};
|
|
11
|
-
declare function Form({ form, method, renderError, children, ...props }: FormProps): ReactElement;
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/runtime/link.d.ts
|
|
14
|
-
type PrefetchStrategy = "hover" | "click" | "mount" | "visible";
|
|
15
|
-
type LinkMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
16
|
-
type CommonProps = {
|
|
17
|
-
href: string;
|
|
18
|
-
method?: LinkMethod | Lowercase<LinkMethod>;
|
|
19
|
-
data?: Record<string, unknown>;
|
|
20
|
-
preserveScroll?: boolean;
|
|
21
|
-
preserveState?: boolean;
|
|
22
|
-
replace?: boolean;
|
|
23
|
-
reloadDocument?: boolean;
|
|
24
|
-
viewTransition?: boolean;
|
|
25
|
-
prefetch?: boolean | PrefetchStrategy | ReadonlyArray<PrefetchStrategy>;
|
|
26
|
-
cacheFor?: number | string | [string, string];
|
|
27
|
-
onNavigate?: (event: {
|
|
28
|
-
preventDefault(): void;
|
|
29
|
-
}) => void;
|
|
30
|
-
};
|
|
31
|
-
type LinkProps = CommonProps & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof CommonProps> & Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof CommonProps>;
|
|
32
|
-
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
|
-
declare function useRouter(): VoidRouter;
|
|
36
|
-
//#endregion
|
|
37
|
-
//#region src/runtime/use-params.d.ts
|
|
38
|
-
declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/runtime/use-shared.d.ts
|
|
41
|
-
declare function useShared<T = CloudContextVariables["shared"]>(): T;
|
|
42
|
-
//#endregion
|
|
43
|
-
//#region src/runtime/use-form.d.ts
|
|
44
|
-
declare function useForm<U extends ActionUrl & string>(url: U, defaults: ResolveActionBody<U>, options?: ActionFormOptions<U>): {
|
|
45
|
-
data: ResolveActionBody<U>;
|
|
46
|
-
setData: <K extends keyof ResolveActionBody<U> & string>(field: K, value: ResolveActionBody<U>[K]) => void;
|
|
47
|
-
errors: Partial<Record<keyof ResolveActionBody<U> & string, string>>;
|
|
48
|
-
error: VoidActionError | null;
|
|
49
|
-
pending: boolean;
|
|
50
|
-
hasChanges: boolean;
|
|
51
|
-
wasSuccessful: boolean;
|
|
52
|
-
recentlySuccessful: boolean;
|
|
53
|
-
post(formData: FormData): void;
|
|
54
|
-
put(formData: FormData): void;
|
|
55
|
-
patch(formData: FormData): void;
|
|
56
|
-
delete(formData: FormData): void;
|
|
57
|
-
reset(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
|
|
58
|
-
clearErrors(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
|
|
59
|
-
clearError(): void;
|
|
60
|
-
};
|
|
61
|
-
//#endregion
|
|
62
|
-
//#region src/runtime/use-navigation.d.ts
|
|
63
|
-
declare function useNavigation(): NavigationState;
|
|
64
|
-
//#endregion
|
|
65
|
-
//#region src/runtime/use-island-form.d.ts
|
|
66
|
-
type IslandFormReturn<T extends Record<string, unknown>> = Readonly<{
|
|
67
|
-
data: T;
|
|
68
|
-
setData: <K extends keyof T & string>(field: K, value: T[K]) => void;
|
|
69
|
-
errors: Record<string, string>;
|
|
70
|
-
error: VoidActionError | null;
|
|
71
|
-
pending: boolean;
|
|
72
|
-
hasChanges: boolean;
|
|
73
|
-
wasSuccessful: boolean;
|
|
74
|
-
recentlySuccessful: boolean;
|
|
75
|
-
reset: (...fields: ReadonlyArray<keyof T & string>) => void;
|
|
76
|
-
clearErrors: (...fields: ReadonlyArray<string>) => void;
|
|
77
|
-
clearError: () => void;
|
|
78
|
-
post: (url: string) => Promise<void>;
|
|
79
|
-
put: (url: string) => Promise<void>;
|
|
80
|
-
patch: (url: string) => Promise<void>;
|
|
81
|
-
delete: (url: string) => Promise<void>;
|
|
82
|
-
}>;
|
|
83
|
-
/**
|
|
84
|
-
* Form hook for island pages. Uses fetch + page reload instead of the
|
|
85
|
-
* Void Router (which is not available in island mode).
|
|
86
|
-
*/
|
|
87
|
-
declare function useIslandForm<T extends Record<string, unknown>>(defaults: T): IslandFormReturn<T>;
|
|
88
|
-
//#endregion
|
|
89
|
-
//#region src/runtime/action.d.ts
|
|
90
|
-
declare const action: ActionClient["action"];
|
|
91
|
-
declare const setActionRouter: ActionClient["setActionRouter"];
|
|
92
|
-
//#endregion
|
|
93
|
-
//#region src/runtime/context.d.ts
|
|
94
|
-
declare const SharedContext: Context<Record<string, unknown> | null>;
|
|
95
|
-
declare const ErrorsContext: Context<Record<string, string> | null>;
|
|
96
|
-
declare const ParamsContext: Context<Record<string, string> | null>;
|
|
97
|
-
declare const RouterContext: Context<VoidRouter | null>;
|
|
98
|
-
declare const NavigationContext: Context<NavigationState>;
|
|
99
|
-
//#endregion
|
|
100
|
-
//#region src/runtime/index.d.ts
|
|
101
|
-
type Deferred<T> = Promise<T>;
|
|
102
|
-
//#endregion
|
|
103
|
-
export { Form as _, ParamsContext as a, action as c, useNavigation as d, useForm as f, Link as g, useRouter as h, NavigationContext as i, setActionRouter as l, useParams as m, InferProps as n, RouterContext as o, useShared as p, ErrorsContext as r, SharedContext as s, Deferred as t, useIslandForm as u };
|
package/dist/runtime/index.d.mts
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { _ as Form, a as ParamsContext, c as action, d as useNavigation, f as useForm, g as Link, h as useRouter, i as NavigationContext, l as setActionRouter, m as useParams, n as InferProps, o as RouterContext, p as useShared, r as ErrorsContext, s as SharedContext, t as Deferred, u as useIslandForm } from "../index-D5M3zrVB.mjs";
|
|
2
|
-
export { Deferred, ErrorsContext, Form, type InferProps, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|
package/dist/runtime/index.mjs
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { a as useParams, c as Form, i as useShared, n as useNavigation, o as useRouter, r as useForm, s as Link, t as useIslandForm } from "../runtime--GesOVur.mjs";
|
|
2
|
-
import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "../context-B0GL7p5m.mjs";
|
|
3
|
-
import { n as setActionRouter, t as action } from "../action-4EvQppia.mjs";
|
|
4
|
-
export { ErrorsContext, Form, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|