@nestjs-ssr/react 0.2.6 → 0.3.1

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/client.js CHANGED
@@ -1,16 +1,43 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
3
+ var React2 = require('react');
4
+ var client = require('react-dom/client');
4
5
 
5
6
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
7
 
7
- var React__default = /*#__PURE__*/_interopDefault(React);
8
+ var React2__default = /*#__PURE__*/_interopDefault(React2);
8
9
 
9
10
  var __defProp = Object.defineProperty;
10
11
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
11
- var PageContext = /* @__PURE__ */ React.createContext(null);
12
- function PageContextProvider({ context, children }) {
13
- return /* @__PURE__ */ React__default.default.createElement(PageContext.Provider, {
12
+ var CONTEXT_KEY = /* @__PURE__ */ Symbol.for("nestjs-ssr.PageContext");
13
+ var globalStore = globalThis;
14
+ function getOrCreateContext() {
15
+ if (!globalStore[CONTEXT_KEY]) {
16
+ globalStore[CONTEXT_KEY] = /* @__PURE__ */ React2.createContext(null);
17
+ }
18
+ return globalStore[CONTEXT_KEY];
19
+ }
20
+ __name(getOrCreateContext, "getOrCreateContext");
21
+ var PageContext = getOrCreateContext();
22
+ var setPageContextState = null;
23
+ function registerPageContextState(setter) {
24
+ setPageContextState = setter;
25
+ }
26
+ __name(registerPageContextState, "registerPageContextState");
27
+ function updatePageContext(context) {
28
+ setPageContextState?.(context);
29
+ }
30
+ __name(updatePageContext, "updatePageContext");
31
+ function PageContextProvider({ context: initialContext, children, isSegment = false }) {
32
+ const [context, setContext] = React2.useState(initialContext);
33
+ React2.useEffect(() => {
34
+ if (!isSegment) {
35
+ registerPageContextState(setContext);
36
+ }
37
+ }, [
38
+ isSegment
39
+ ]);
40
+ return /* @__PURE__ */ React2__default.default.createElement(PageContext.Provider, {
14
41
  value: context
15
42
  }, children);
16
43
  }
@@ -22,7 +49,7 @@ function createSSRHooks() {
22
49
  * Contains URL metadata, headers, and any custom properties you've added.
23
50
  */
24
51
  usePageContext: /* @__PURE__ */ __name(() => {
25
- const context = React.useContext(PageContext);
52
+ const context = React2.useContext(PageContext);
26
53
  if (!context) {
27
54
  throw new Error("usePageContext must be used within PageContextProvider");
28
55
  }
@@ -39,7 +66,7 @@ function createSSRHooks() {
39
66
  * ```
40
67
  */
41
68
  useParams: /* @__PURE__ */ __name(() => {
42
- const context = React.useContext(PageContext);
69
+ const context = React2.useContext(PageContext);
43
70
  if (!context) {
44
71
  throw new Error("useParams must be used within PageContextProvider");
45
72
  }
@@ -57,7 +84,7 @@ function createSSRHooks() {
57
84
  * ```
58
85
  */
59
86
  useQuery: /* @__PURE__ */ __name(() => {
60
- const context = React.useContext(PageContext);
87
+ const context = React2.useContext(PageContext);
61
88
  if (!context) {
62
89
  throw new Error("useQuery must be used within PageContextProvider");
63
90
  }
@@ -77,7 +104,7 @@ function createSSRHooks() {
77
104
  * ```
78
105
  */
79
106
  useRequest: /* @__PURE__ */ __name(() => {
80
- const context = React.useContext(PageContext);
107
+ const context = React2.useContext(PageContext);
81
108
  if (!context) {
82
109
  throw new Error("useRequest must be used within PageContextProvider");
83
110
  }
@@ -103,7 +130,7 @@ function createSSRHooks() {
103
130
  * ```
104
131
  */
105
132
  useHeaders: /* @__PURE__ */ __name(() => {
106
- const context = React.useContext(PageContext);
133
+ const context = React2.useContext(PageContext);
107
134
  if (!context) {
108
135
  throw new Error("useHeaders must be used within PageContextProvider");
109
136
  }
@@ -138,7 +165,7 @@ function createSSRHooks() {
138
165
  * ```
139
166
  */
140
167
  useHeader: /* @__PURE__ */ __name((name) => {
141
- const context = React.useContext(PageContext);
168
+ const context = React2.useContext(PageContext);
142
169
  if (!context) {
143
170
  throw new Error("useHeader must be used within PageContextProvider");
144
171
  }
@@ -164,7 +191,7 @@ function createSSRHooks() {
164
191
  * ```
165
192
  */
166
193
  useCookies: /* @__PURE__ */ __name(() => {
167
- const context = React.useContext(PageContext);
194
+ const context = React2.useContext(PageContext);
168
195
  if (!context) {
169
196
  throw new Error("useCookies must be used within PageContextProvider");
170
197
  }
@@ -186,7 +213,7 @@ function createSSRHooks() {
186
213
  * ```
187
214
  */
188
215
  useCookie: /* @__PURE__ */ __name((name) => {
189
- const context = React.useContext(PageContext);
216
+ const context = React2.useContext(PageContext);
190
217
  if (!context) {
191
218
  throw new Error("useCookie must be used within PageContextProvider");
192
219
  }
@@ -202,6 +229,326 @@ function createSSRHooks() {
202
229
  };
203
230
  }
204
231
  __name(createSSRHooks, "createSSRHooks");
232
+ var defaultHooks = createSSRHooks();
233
+ var usePageContext = defaultHooks.usePageContext;
234
+ var useParams = defaultHooks.useParams;
235
+ var useQuery = defaultHooks.useQuery;
236
+ var useRequest = defaultHooks.useRequest;
237
+ var useHeaders = defaultHooks.useHeaders;
238
+ var useHeader = defaultHooks.useHeader;
239
+ var useCookies = defaultHooks.useCookies;
240
+ var useCookie = defaultHooks.useCookie;
241
+ var rootRegistry = /* @__PURE__ */ new WeakMap();
242
+ function hydrateSegment(outlet, componentName, props) {
243
+ const modules = window.__MODULES__;
244
+ if (!modules) {
245
+ console.warn("[navigation] Module registry not available for segment hydration. Make sure entry-client.tsx exports window.__MODULES__.");
246
+ return;
247
+ }
248
+ const ViewComponent = resolveComponent(componentName, modules);
249
+ if (!ViewComponent) {
250
+ console.warn(`[navigation] Component "${componentName}" not found for hydration. Available components: ` + Object.keys(modules).map((path) => {
251
+ const c = modules[path].default;
252
+ return c?.displayName || c?.name || "anonymous";
253
+ }).join(", "));
254
+ return;
255
+ }
256
+ const context = window.__CONTEXT__ || {};
257
+ const element = /* @__PURE__ */ React2__default.default.createElement(PageContextProvider, {
258
+ context,
259
+ isSegment: true
260
+ }, /* @__PURE__ */ React2__default.default.createElement(ViewComponent, props));
261
+ let wrapper = outlet.querySelector("[data-segment-root]");
262
+ if (wrapper) {
263
+ const existingRoot = rootRegistry.get(wrapper);
264
+ if (existingRoot) {
265
+ existingRoot.unmount();
266
+ rootRegistry.delete(wrapper);
267
+ }
268
+ }
269
+ wrapper = document.createElement("div");
270
+ wrapper.setAttribute("data-segment-root", "true");
271
+ outlet.innerHTML = "";
272
+ outlet.appendChild(wrapper);
273
+ const root = client.createRoot(wrapper);
274
+ root.render(element);
275
+ rootRegistry.set(wrapper, root);
276
+ }
277
+ __name(hydrateSegment, "hydrateSegment");
278
+ function resolveComponent(name, modules) {
279
+ const componentMap = Object.entries(modules).filter(([path, module]) => {
280
+ const filename = path.split("/").pop();
281
+ if (filename === "entry-client.tsx" || filename === "entry-server.tsx") {
282
+ return false;
283
+ }
284
+ return module.default !== void 0;
285
+ }).map(([path, module]) => {
286
+ const component = module.default;
287
+ const componentName = component.displayName || component.name;
288
+ const filename = path.split("/").pop()?.replace(".tsx", "");
289
+ const normalizedFilename = filename ? filename.charAt(0).toUpperCase() + filename.slice(1) : void 0;
290
+ return {
291
+ component,
292
+ name: componentName,
293
+ filename,
294
+ normalizedFilename
295
+ };
296
+ });
297
+ let match = componentMap.find((c) => c.name === name || c.normalizedFilename === name || c.filename === name.toLowerCase());
298
+ if (!match && /^default(_\d+)?$/.test(name)) {
299
+ if (componentMap.length === 1) {
300
+ match = componentMap[0];
301
+ } else {
302
+ const indexMatch = name.match(/^default_(\d+)$/);
303
+ const index = indexMatch ? parseInt(indexMatch[1], 10) - 1 : 0;
304
+ const defaultComponents = componentMap.filter((c) => c.name === "default").sort((a, b) => (a.filename || "").localeCompare(b.filename || ""));
305
+ if (defaultComponents[index]) {
306
+ match = defaultComponents[index];
307
+ }
308
+ }
309
+ }
310
+ return match?.component;
311
+ }
312
+ __name(resolveComponent, "resolveComponent");
313
+
314
+ // src/react/navigation/navigate.ts
315
+ var setNavigationState = null;
316
+ function registerNavigationState(setter) {
317
+ setNavigationState = setter;
318
+ }
319
+ __name(registerNavigationState, "registerNavigationState");
320
+ async function navigate(url, options = {}) {
321
+ const { replace = false, scroll = true } = options;
322
+ setNavigationState?.("loading");
323
+ const parsedUrl = new URL(url, window.location.origin);
324
+ const currentContext = window.__CONTEXT__;
325
+ if (currentContext) {
326
+ const optimisticContext = {
327
+ ...currentContext,
328
+ path: parsedUrl.pathname,
329
+ url
330
+ };
331
+ updatePageContext(optimisticContext);
332
+ }
333
+ try {
334
+ const currentLayouts = getCurrentLayouts();
335
+ if (currentLayouts.length === 0) {
336
+ window.location.href = url;
337
+ return;
338
+ }
339
+ const response = await fetchSegment(url, currentLayouts);
340
+ if (!response.swapTarget) {
341
+ window.location.href = url;
342
+ return;
343
+ }
344
+ const outlet = await swapContent(response.html, response.swapTarget);
345
+ if (outlet) {
346
+ hydrateSegment(outlet, response.componentName, response.props);
347
+ }
348
+ if (replace) {
349
+ history.replaceState({
350
+ url
351
+ }, "", url);
352
+ } else {
353
+ history.pushState({
354
+ url
355
+ }, "", url);
356
+ }
357
+ if (response.context) {
358
+ updatePageContext(response.context);
359
+ window.__CONTEXT__ = response.context;
360
+ }
361
+ if (response.head) {
362
+ updateHead(response.head);
363
+ }
364
+ if (scroll) {
365
+ window.scrollTo(0, 0);
366
+ }
367
+ window.__COMPONENT_NAME__ = response.componentName;
368
+ window.__INITIAL_STATE__ = response.props;
369
+ } catch (error) {
370
+ console.error("Navigation failed:", error);
371
+ window.location.href = url;
372
+ } finally {
373
+ setNavigationState?.("idle");
374
+ }
375
+ }
376
+ __name(navigate, "navigate");
377
+ function getCurrentLayouts() {
378
+ return Array.from(document.querySelectorAll("[data-layout]")).map((el) => el.getAttribute("data-layout"));
379
+ }
380
+ __name(getCurrentLayouts, "getCurrentLayouts");
381
+ async function fetchSegment(url, currentLayouts) {
382
+ const res = await fetch(url, {
383
+ headers: {
384
+ "X-Current-Layouts": currentLayouts.join(",")
385
+ }
386
+ });
387
+ if (!res.ok) {
388
+ throw new Error(`Navigation failed: ${res.status}`);
389
+ }
390
+ return res.json();
391
+ }
392
+ __name(fetchSegment, "fetchSegment");
393
+ async function swapContent(html, swapTarget) {
394
+ const outlet = document.querySelector(`[data-outlet="${swapTarget}"]`);
395
+ if (!outlet) {
396
+ window.location.reload();
397
+ return null;
398
+ }
399
+ const swap = /* @__PURE__ */ __name(() => {
400
+ outlet.innerHTML = html;
401
+ }, "swap");
402
+ if ("startViewTransition" in document) {
403
+ try {
404
+ await document.startViewTransition(swap).finished;
405
+ } catch {
406
+ swap();
407
+ }
408
+ } else {
409
+ swap();
410
+ }
411
+ return outlet;
412
+ }
413
+ __name(swapContent, "swapContent");
414
+ function updateHead(head) {
415
+ if (head.title) {
416
+ document.title = head.title;
417
+ }
418
+ const updateMeta = /* @__PURE__ */ __name((name, content) => {
419
+ let el = document.querySelector(`meta[name="${name}"]`);
420
+ if (!el) {
421
+ el = document.createElement("meta");
422
+ el.setAttribute("name", name);
423
+ document.head.appendChild(el);
424
+ }
425
+ el.setAttribute("content", content);
426
+ }, "updateMeta");
427
+ if (head.description) {
428
+ updateMeta("description", head.description);
429
+ }
430
+ if (head.keywords) {
431
+ updateMeta("keywords", head.keywords);
432
+ }
433
+ const updateOgMeta = /* @__PURE__ */ __name((property, content) => {
434
+ let el = document.querySelector(`meta[property="${property}"]`);
435
+ if (!el) {
436
+ el = document.createElement("meta");
437
+ el.setAttribute("property", property);
438
+ document.head.appendChild(el);
439
+ }
440
+ el.setAttribute("content", content);
441
+ }, "updateOgMeta");
442
+ if (head.ogTitle) {
443
+ updateOgMeta("og:title", head.ogTitle);
444
+ }
445
+ if (head.ogDescription) {
446
+ updateOgMeta("og:description", head.ogDescription);
447
+ }
448
+ if (head.ogImage) {
449
+ updateOgMeta("og:image", head.ogImage);
450
+ }
451
+ if (head.canonical) {
452
+ let canonical = document.querySelector('link[rel="canonical"]');
453
+ if (!canonical) {
454
+ canonical = document.createElement("link");
455
+ canonical.setAttribute("rel", "canonical");
456
+ document.head.appendChild(canonical);
457
+ }
458
+ canonical.setAttribute("href", head.canonical);
459
+ }
460
+ }
461
+ __name(updateHead, "updateHead");
462
+
463
+ // src/react/navigation/navigation-context.tsx
464
+ var NavigationContext = /* @__PURE__ */ React2.createContext(null);
465
+ function NavigationProvider({ children }) {
466
+ const [state, setState] = React2.useState("idle");
467
+ React2.useEffect(() => {
468
+ registerNavigationState(setState);
469
+ }, []);
470
+ return /* @__PURE__ */ React2__default.default.createElement(NavigationContext.Provider, {
471
+ value: {
472
+ state
473
+ }
474
+ }, children);
475
+ }
476
+ __name(NavigationProvider, "NavigationProvider");
477
+ function useNavigationContext() {
478
+ const context = React2.useContext(NavigationContext);
479
+ if (!context) {
480
+ return {
481
+ state: "idle"
482
+ };
483
+ }
484
+ return context;
485
+ }
486
+ __name(useNavigationContext, "useNavigationContext");
487
+ function Link({ href, replace = false, scroll = true, children, onClick, ...props }) {
488
+ const handleClick = /* @__PURE__ */ __name((e) => {
489
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0 || props.target === "_blank" || !isSameOrigin(href)) {
490
+ onClick?.(e);
491
+ return;
492
+ }
493
+ e.preventDefault();
494
+ onClick?.(e);
495
+ navigate(href, {
496
+ replace,
497
+ scroll
498
+ });
499
+ }, "handleClick");
500
+ return /* @__PURE__ */ React2__default.default.createElement("a", {
501
+ href,
502
+ onClick: handleClick,
503
+ ...props
504
+ }, children);
505
+ }
506
+ __name(Link, "Link");
507
+ function isSameOrigin(url) {
508
+ try {
509
+ const parsed = new URL(url, window.location.origin);
510
+ return parsed.origin === window.location.origin;
511
+ } catch {
512
+ return true;
513
+ }
514
+ }
515
+ __name(isSameOrigin, "isSameOrigin");
516
+ function useNavigation() {
517
+ const { state } = useNavigationContext();
518
+ return {
519
+ state,
520
+ navigate
521
+ };
522
+ }
523
+ __name(useNavigation, "useNavigation");
524
+ function useNavigationState() {
525
+ return useNavigationContext().state;
526
+ }
527
+ __name(useNavigationState, "useNavigationState");
528
+ function useNavigate() {
529
+ return React2.useCallback(navigate, []);
530
+ }
531
+ __name(useNavigate, "useNavigate");
532
+ function useIsNavigating() {
533
+ return useNavigationContext().state === "loading";
534
+ }
535
+ __name(useIsNavigating, "useIsNavigating");
205
536
 
537
+ exports.Link = Link;
538
+ exports.NavigationProvider = NavigationProvider;
206
539
  exports.PageContextProvider = PageContextProvider;
207
540
  exports.createSSRHooks = createSSRHooks;
541
+ exports.navigate = navigate;
542
+ exports.updatePageContext = updatePageContext;
543
+ exports.useCookie = useCookie;
544
+ exports.useCookies = useCookies;
545
+ exports.useHeader = useHeader;
546
+ exports.useHeaders = useHeaders;
547
+ exports.useIsNavigating = useIsNavigating;
548
+ exports.useNavigate = useNavigate;
549
+ exports.useNavigation = useNavigation;
550
+ exports.useNavigationState = useNavigationState;
551
+ exports.usePageContext = usePageContext;
552
+ exports.useParams = useParams;
553
+ exports.useQuery = useQuery;
554
+ exports.useRequest = useRequest;