@void/react 0.7.4 → 0.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,6 +36,7 @@ Main runtime APIs live at `@void/react`:
36
36
 
37
37
  - `Link`
38
38
  - `useRouter()`
39
+ - `useParams()`
39
40
  - `useNavigation()`
40
41
  - `useShared()`
41
42
  - `useForm()`
@@ -142,6 +143,11 @@ accepts `{ data, method, params }`.
142
143
  `useRouter()` returns the Void Router. `visit()`, `refresh()`, and `prefetch()`
143
144
  are awaitable; `prefetch()` resolves when the page data has been cached.
144
145
 
146
+ ### `useParams`
147
+
148
+ `useParams()` returns dynamic route params for the page currently being rendered.
149
+ Use it instead of parsing `useRouter().path` in dynamic Pages routes.
150
+
145
151
  ### `Link`
146
152
 
147
153
  `Link` is the React Pages navigation component. GET links render an `<a>` and
@@ -3,7 +3,8 @@ import { idleNavigationState } from "void/pages-client";
3
3
  //#region src/runtime/context.ts
4
4
  const SharedContext = createContext(null);
5
5
  const ErrorsContext = createContext(null);
6
+ const ParamsContext = createContext(null);
6
7
  const RouterContext = createContext(null);
7
8
  const NavigationContext = createContext(idleNavigationState());
8
9
  //#endregion
9
- export { SharedContext as i, NavigationContext as n, RouterContext as r, ErrorsContext as t };
10
+ export { SharedContext as a, RouterContext as i, NavigationContext as n, ParamsContext as r, ErrorsContext as t };
@@ -48,6 +48,9 @@ declare function Link({
48
48
  //#region src/runtime/use-router.d.ts
49
49
  declare function useRouter(): VoidRouter;
50
50
  //#endregion
51
+ //#region src/runtime/use-params.d.ts
52
+ declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
53
+ //#endregion
51
54
  //#region src/runtime/use-shared.d.ts
52
55
  declare function useShared<T = CloudContextVariables["shared"]>(): T;
53
56
  //#endregion
@@ -116,10 +119,11 @@ declare function action<U extends ActionUrl & string>(url: U, ...args: HasParams
116
119
  //#region src/runtime/context.d.ts
117
120
  declare const SharedContext: Context<Record<string, unknown> | null>;
118
121
  declare const ErrorsContext: Context<Record<string, string> | null>;
122
+ declare const ParamsContext: Context<Record<string, string> | null>;
119
123
  declare const RouterContext: Context<VoidRouter | null>;
120
124
  declare const NavigationContext: Context<NavigationState>;
121
125
  //#endregion
122
126
  //#region src/runtime/index.d.ts
123
127
  type Deferred<T> = Promise<T>;
124
128
  //#endregion
125
- export { RouterContext as a, setActionRouter as c, useForm as d, useShared as f, NavigationContext as i, useIslandForm as l, Link as m, InferProps as n, SharedContext as o, useRouter as p, ErrorsContext as r, action as s, Deferred as t, useNavigation as u };
129
+ export { 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/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as RouterContext, c as setActionRouter, d as useForm, f as useShared, i as NavigationContext, l as useIslandForm, m as Link, n as InferProps, o as SharedContext, p as useRouter, r as ErrorsContext, s as action, t as Deferred, u as useNavigation } from "./index-6hxGVqsE.mjs";
2
- export { Deferred, ErrorsContext, InferProps, Link, NavigationContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useRouter, useShared };
1
+ import { 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-75EO-lLe.mjs";
2
+ export { Deferred, ErrorsContext, InferProps, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as SharedContext, n as NavigationContext, r as RouterContext, t as ErrorsContext } from "./context-BCeFV8Jy.mjs";
2
- import { a as useRouter, i as useShared, n as useNavigation, o as Link, r as useForm, t as useIslandForm } from "./runtime-C24S_Dlg.mjs";
1
+ import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "./context-CBGCQE3p.mjs";
2
+ import { a as useParams, i as useShared, n as useNavigation, o as useRouter, r as useForm, s as Link, t as useIslandForm } from "./runtime-BpEAl7K2.mjs";
3
3
  import { n as setActionRouter, t as action } from "./action-BFWtavbf.mjs";
4
- export { ErrorsContext, Link, NavigationContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useRouter, useShared };
4
+ export { ErrorsContext, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
package/dist/plugin.mjs CHANGED
@@ -2,7 +2,7 @@ import react from "@vitejs/plugin-react";
2
2
  import { existsSync, readFileSync, readdirSync } from "node:fs";
3
3
  import { normalizePath } from "vite";
4
4
  import { generateComponentManifest, resolveEffectiveLayoutChain, resolveStaticPageDataBuildOptions, scanPages } from "void/pages";
5
- import { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, computePagesNeedingClientJS, configureIslandCssServer, extractCssImports, fileHasCssImports, needsIslandClientBundle, rebuildIslandCssMap, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan } from "void/pages-islands-plugin";
5
+ import { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, computePagesNeedingClientJS, extractCssImports, needsIslandClientBundle, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan } from "void/pages-islands-plugin";
6
6
  //#region ../../node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
7
7
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
8
8
  function normalizeWindowsPath(input = "") {
@@ -148,13 +148,7 @@ function islandsPlugin(pagesDir) {
148
148
  const islandManifest = /* @__PURE__ */ new Map();
149
149
  const islandCssFiles = /* @__PURE__ */ new Set();
150
150
  let viteRoot = "";
151
- const islandCssMap = /* @__PURE__ */ new Map();
152
151
  let resolvedConfig = null;
153
- /** Rebuild islandCssMap from current file contents (re-runs fileHasCssImports). */
154
- function rebuildCssMapLocal() {
155
- if (!pageScan) return;
156
- rebuildIslandCssMap(pageScan, pagesDir, islandCssMap, fileHasCssImports);
157
- }
158
152
  function computePagesNeedingClientJSLocal() {
159
153
  if (!pageScan) return /* @__PURE__ */ new Set();
160
154
  return computePagesNeedingClientJS(pageScan, pagesDir, resolvedConfig);
@@ -167,10 +161,8 @@ function islandsPlugin(pagesDir) {
167
161
  isDev = config.command === "serve";
168
162
  },
169
163
  configureServer(server) {
170
- configureIslandCssServer(server, islandCssFiles, viteRoot, rebuildCssMapLocal);
171
164
  watchPagesForRescan(server, pagesDir, root, (scan) => {
172
165
  pageScan = scan;
173
- rebuildCssMapLocal();
174
166
  });
175
167
  },
176
168
  async buildStart() {
@@ -218,7 +210,6 @@ function islandsPlugin(pagesDir) {
218
210
  const layoutChain = resolveEffectiveLayoutChain(page, pageScan.layouts, pageScan.namedLayouts);
219
211
  for (const layout of layoutChain) islandCssFiles.add(normalizePath(resolve(pagesDir, layout.filePath)));
220
212
  }
221
- rebuildCssMapLocal();
222
213
  }
223
214
  },
224
215
  resolveId: {
@@ -243,7 +234,7 @@ function islandsPlugin(pagesDir) {
243
234
  const mdPlugin = resolvedConfig?.plugins?.find((p) => p.name === "void-md");
244
235
  const mdScriptIds = new Set(mdPlugin?.api?.getClientScripts?.()?.keys() ?? []);
245
236
  const needsJS = computePagesNeedingClientJSLocal();
246
- return generateIslandRenderer(pageScan, pagesDir, isDev, islandCssMap, viteRoot, mdScriptIds, needsJS);
237
+ return generateIslandRenderer(pageScan, pagesDir, isDev, mdScriptIds, needsJS);
247
238
  }
248
239
  if (id === "\0" + ISLANDS_CLIENT_ENTRY_ID) {
249
240
  if (!pageScan) return "";
@@ -304,7 +295,7 @@ function islandsPlugin(pagesDir) {
304
295
  }
305
296
  }];
306
297
  }
307
- function generateIslandRenderer(scan, pagesDir, isDev, islandCssMap, viteRoot, mdScriptIds, pagesNeedingClientJS) {
298
+ function generateIslandRenderer(scan, pagesDir, isDev, mdScriptIds, pagesNeedingClientJS) {
308
299
  const preambleScript = isDev ? `
309
300
  <script type="module">
310
301
  import RefreshRuntime from "/@react-refresh"
@@ -313,22 +304,15 @@ function generateIslandRenderer(scan, pagesDir, isDev, islandCssMap, viteRoot, m
313
304
  window.$RefreshSig$ = () => (type) => type
314
305
  window.__vite_plugin_react_preamble_installed__ = true
315
306
  <\/script>` : "";
316
- let cssMapCode = "";
317
- if (isDev && islandCssMap.size > 0) {
318
- const cssMapObj = {};
319
- for (const [compId, paths] of islandCssMap) cssMapObj[compId] = paths.map((p) => "/" + normalizePath(relative(viteRoot, p)) + ".css");
320
- cssMapCode = `const __cssPaths = ${JSON.stringify(cssMapObj)};`;
321
- }
322
307
  const mdScriptIdsCode = `const __mdClientScriptIds = new Set(${JSON.stringify([...mdScriptIds])});`;
323
308
  const needsJSCode = `const __pagesNeedingClientJS = new Set(${JSON.stringify([...pagesNeedingClientJS])});`;
324
309
  return `
325
310
  import React from "react";
326
311
  import { renderToString } from "react-dom/server";
327
- import { SharedContext, ErrorsContext } from "@void/react";
312
+ import { SharedContext, ErrorsContext, ParamsContext } from "@void/react";
328
313
  import { renderHeadToString, renderHtmlAttrs, renderBodyAttrs } from "void/pages-head";
329
314
 
330
315
  ${generateComponentManifest(scan, pagesDir)}
331
- ${cssMapCode}
332
316
  ${mdScriptIdsCode}
333
317
  ${needsJSCode}
334
318
 
@@ -355,9 +339,13 @@ export async function renderIslandPage(pageObj, assetTags) {
355
339
  SharedContext.Provider,
356
340
  { value: pageObj.shared || {} },
357
341
  React.createElement(
358
- ErrorsContext.Provider,
359
- { value: pageObj.errors || {} },
360
- element
342
+ ParamsContext.Provider,
343
+ { value: pageObj.params || {} },
344
+ React.createElement(
345
+ ErrorsContext.Provider,
346
+ { value: pageObj.errors || {} },
347
+ element
348
+ )
361
349
  )
362
350
  );
363
351
 
@@ -369,7 +357,7 @@ export async function renderIslandPage(pageObj, assetTags) {
369
357
 
370
358
  return \`<!doctype html>
371
359
  <html\${htmlAttrs}>
372
- <head>\${headHtml}\${typeof __cssPaths !== "undefined" ? (__cssPaths[pageObj.component] || []).map(url => '<link rel="stylesheet" href="' + url + '">').join("\\n") : ""}\${PREAMBLE_SCRIPT}\${assetTags.css}\${__pagesNeedingClientJS.has(pageObj.component) ? assetTags.preloads : ""}</head>
360
+ <head>\${headHtml}\${PREAMBLE_SCRIPT}\${assetTags.css}\${__pagesNeedingClientJS.has(pageObj.component) ? assetTags.preloads : ""}</head>
373
361
  <body\${bodyAttrs}>
374
362
  <div id="app"\${__mdScriptAttr}>\${html}</div>
375
363
  \${__pagesNeedingClientJS.has(pageObj.component) ? assetTags.body : ""}
@@ -1,2 +1,2 @@
1
- import { a as RouterContext, c as setActionRouter, d as useForm, f as useShared, i as NavigationContext, l as useIslandForm, m as Link, n as InferProps, o as SharedContext, p as useRouter, r as ErrorsContext, s as action, t as Deferred, u as useNavigation } from "../index-6hxGVqsE.mjs";
2
- export { Deferred, ErrorsContext, InferProps, Link, NavigationContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useRouter, useShared };
1
+ import { 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-75EO-lLe.mjs";
2
+ export { Deferred, ErrorsContext, InferProps, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
@@ -1,4 +1,4 @@
1
- import { i as SharedContext, n as NavigationContext, r as RouterContext, t as ErrorsContext } from "../context-BCeFV8Jy.mjs";
2
- import { a as useRouter, i as useShared, n as useNavigation, o as Link, r as useForm, t as useIslandForm } from "../runtime-C24S_Dlg.mjs";
1
+ import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "../context-CBGCQE3p.mjs";
2
+ import { a as useParams, i as useShared, n as useNavigation, o as useRouter, r as useForm, s as Link, t as useIslandForm } from "../runtime-BpEAl7K2.mjs";
3
3
  import { n as setActionRouter, t as action } from "../action-BFWtavbf.mjs";
4
- export { ErrorsContext, Link, NavigationContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useRouter, useShared };
4
+ export { ErrorsContext, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
@@ -1,4 +1,4 @@
1
- import { i as SharedContext, n as NavigationContext, r as RouterContext, t as ErrorsContext } from "../context-BCeFV8Jy.mjs";
1
+ import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "../context-CBGCQE3p.mjs";
2
2
  import { n as setActionRouter } from "../action-BFWtavbf.mjs";
3
3
  import React, { Suspense, startTransition, use, useCallback, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useReducer, useRef, useTransition } from "react";
4
4
  import { createPageNavigationEngine, createRouterFacade, idleNavigationState, isAbortError, isCallSiteActionError } from "void/pages-client";
@@ -8,6 +8,7 @@ const deferredState = /* @__PURE__ */ new WeakMap();
8
8
  const viewTransitionWaiters = /* @__PURE__ */ new Map();
9
9
  const pendingVisitSettlers = /* @__PURE__ */ new Map();
10
10
  const committedResources = /* @__PURE__ */ new Set();
11
+ const EMPTY_PARAMS = {};
11
12
  let activePendingNavigationId = null;
12
13
  let scheduleVisit = null;
13
14
  function createDeferredResource() {
@@ -116,6 +117,9 @@ function createRouter(engine) {
116
117
  get query() {
117
118
  return this.url.searchParams;
118
119
  },
120
+ get params() {
121
+ return {};
122
+ },
119
123
  visit(url, options = {}) {
120
124
  if (!scheduleVisit) return Promise.reject(/* @__PURE__ */ new Error("router.visit() called before router initialized"));
121
125
  return scheduleVisit(normalizeUrl(url), options);
@@ -141,6 +145,7 @@ function createRouter(engine) {
141
145
  }
142
146
  function RouteResource({ resource, onCommit }) {
143
147
  const result = use(resource.shell);
148
+ const outerRouter = React.useContext(RouterContext);
144
149
  useLayoutEffect(() => {
145
150
  if (result) onCommit(resource, result);
146
151
  }, [
@@ -151,12 +156,23 @@ function RouteResource({ resource, onCommit }) {
151
156
  if (!result) return null;
152
157
  const pageData = result.pageData;
153
158
  const PageComponent = result.component;
159
+ const routeParams = pageData.params ?? EMPTY_PARAMS;
160
+ const routeRouter = useMemo(() => createRouterFacade(outerRouter, () => {
161
+ const outerUrl = outerRouter.url;
162
+ const outerPath = outerUrl.pathname + outerUrl.search;
163
+ return outerPath === pageData.url ? outerPath + outerUrl.hash : pageData.url + resource.hash;
164
+ }, () => routeParams), [
165
+ outerRouter,
166
+ pageData.url,
167
+ resource.hash,
168
+ routeParams
169
+ ]);
154
170
  let element = React.createElement(PageComponent, pageData.props);
155
171
  for (let i = result.layouts.length - 1; i >= 0; i--) {
156
172
  const Layout = result.layouts[i];
157
173
  element = React.createElement(Layout, null, element);
158
174
  }
159
- return React.createElement(SharedContext.Provider, { value: pageData.shared }, React.createElement(ErrorsContext.Provider, { value: pageData.errors ?? {} }, element));
175
+ return React.createElement(RouterContext.Provider, { value: routeRouter }, React.createElement(ParamsContext.Provider, { value: routeParams }, React.createElement(SharedContext.Provider, { value: pageData.shared }, React.createElement(ErrorsContext.Provider, { value: pageData.errors ?? {} }, element))));
160
176
  }
161
177
  function startViewTransition(resource, reveal, enabled) {
162
178
  if (!enabled || typeof document.startViewTransition !== "function") {
@@ -1,4 +1,4 @@
1
- import { i as SharedContext, n as NavigationContext, r as RouterContext, t as ErrorsContext } from "../context-BCeFV8Jy.mjs";
1
+ import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "../context-CBGCQE3p.mjs";
2
2
  import React from "react";
3
3
  import { createSsrRouter, idleNavigationState } from "void/pages-client";
4
4
  import { renderToReadableStream } from "react-dom/server";
@@ -84,7 +84,7 @@ async function renderReactPage({ pageObj, assetTags, components, layoutTree, pre
84
84
  const renderProps = propsForReactRender(pageObj);
85
85
  let element = React.createElement(PageComponent, renderProps);
86
86
  for (let i = layoutComponents.length - 1; i >= 0; i--) element = React.createElement(layoutComponents[i], null, element);
87
- element = React.createElement(RouterContext.Provider, { value: createSsrRouter(pageObj.url || "/") }, React.createElement(NavigationContext.Provider, { value: idleNavigationState() }, React.createElement(React.Suspense, { fallback: null }, React.createElement(SharedContext.Provider, { value: pageObj.shared || {} }, React.createElement(ErrorsContext.Provider, { value: pageObj.errors || {} }, element)))));
87
+ 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))))));
88
88
  const appStream = await renderToReadableStream(pageObj.deferredKeys ? React.createElement(React.Fragment, null, element, React.createElement("template", { id: DEFERRED_SHELL_END_ID })) : element, { onError(error) {
89
89
  if (error === DEFERRED_SHELL_CANCEL_REASON) return;
90
90
  throw error;
@@ -1,4 +1,4 @@
1
- import { i as SharedContext, n as NavigationContext, r as RouterContext } from "./context-BCeFV8Jy.mjs";
1
+ import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext } from "./context-CBGCQE3p.mjs";
2
2
  import "./action-BFWtavbf.mjs";
3
3
  import { useActionState, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { VoidActionError, categorizeActionError, isAbortError, isEqualFormValue, ssrProxy, submitAction } from "void/pages-client";
@@ -232,6 +232,13 @@ function useRouter() {
232
232
  return useContext(RouterContext) ?? ssrProxy;
233
233
  }
234
234
  //#endregion
235
+ //#region src/runtime/use-params.ts
236
+ function useParams() {
237
+ const params = useContext(ParamsContext);
238
+ if (params === null) throw new Error("useParams(): must be used inside a Void page.");
239
+ return params;
240
+ }
241
+ //#endregion
235
242
  //#region src/runtime/use-shared.ts
236
243
  function useShared() {
237
244
  const shared = useContext(SharedContext);
@@ -489,4 +496,4 @@ function useIslandForm(defaults) {
489
496
  };
490
497
  }
491
498
  //#endregion
492
- export { useRouter as a, useShared as i, useNavigation as n, Link as o, useForm as r, useIslandForm as t };
499
+ export { useParams as a, useShared as i, useNavigation as n, useRouter as o, useForm as r, Link as s, useIslandForm as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@void/react",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voidzero-dev/void.git",
@@ -51,13 +51,13 @@
51
51
  "react-dom": "^19.2.6",
52
52
  "vite": "npm:@voidzero-dev/vite-plus-core@0.1.20",
53
53
  "vite-plus": "^0.1.20",
54
- "void": "0.7.4"
54
+ "void": "0.7.6"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "react": "^19.0.0",
58
58
  "react-dom": "^19.0.0",
59
59
  "vite": "npm:@voidzero-dev/vite-plus-core@0.1.20",
60
- "void": "0.7.4"
60
+ "void": "0.7.6"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "vp pack",