@zubyjs/react 1.0.46 → 1.0.48

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.
@@ -0,0 +1,8 @@
1
+ import { ComponentType } from 'react';
2
+ export type PreloadableComponent<T extends ComponentType<any>> = T & {
3
+ preload: () => Promise<T>;
4
+ };
5
+ export declare function lazyWithPreload<T extends ComponentType<any>>(factory: () => Promise<{
6
+ default: T;
7
+ }>): PreloadableComponent<T>;
8
+ export default lazyWithPreload;
@@ -0,0 +1,27 @@
1
+ // Original code from:
2
+ // https://github.com/ianschmitz/react-lazy-with-preload/blob/master/src/index.ts
3
+ import { createElement, forwardRef, lazy, useRef } from 'react';
4
+ export function lazyWithPreload(factory) {
5
+ const ReactLazyComponent = lazy(factory);
6
+ let PreloadedComponent;
7
+ let factoryPromise;
8
+ const Component = forwardRef(function LazyWithPreload(props, ref) {
9
+ // Once one of these is chosen, we must ensure that it continues to be
10
+ // used for all subsequent renders, otherwise it can cause the
11
+ // underlying component to be unmounted and remounted.
12
+ const ComponentToRender = useRef(PreloadedComponent ?? ReactLazyComponent);
13
+ return createElement(ComponentToRender.current, Object.assign(ref ? { ref } : {}, props));
14
+ });
15
+ const LazyWithPreload = Component;
16
+ LazyWithPreload.preload = () => {
17
+ if (!factoryPromise) {
18
+ factoryPromise = factory().then(module => {
19
+ PreloadedComponent = module.default;
20
+ return PreloadedComponent;
21
+ });
22
+ }
23
+ return factoryPromise;
24
+ };
25
+ return LazyWithPreload;
26
+ }
27
+ export default lazyWithPreload;
@@ -21,8 +21,11 @@ interface OptionsWithoutMetadata extends Options {
21
21
  metadata?: false;
22
22
  }
23
23
  interface UseFetch {
24
- (input: RequestInfo, init?: RequestInit | undefined, options?: number | OptionsWithoutMetadata): FetchResponse;
25
- (input: RequestInfo, init: RequestInit | undefined, options: OptionsWithMetadata): FetchResponseMetadata;
24
+ (input: RequestInfo, init?: UseRequestInit | undefined, options?: number | OptionsWithoutMetadata): FetchResponse;
25
+ (input: RequestInfo, init: UseRequestInit | undefined, options: OptionsWithMetadata): FetchResponseMetadata;
26
+ }
27
+ export interface UseRequestInit extends RequestInit {
28
+ priority?: 'low' | 'high' | 'auto';
26
29
  }
27
30
  export declare const useFetch: UseFetch;
28
31
  export {};
@@ -0,0 +1 @@
1
+ export default function useProps(path: string, priority?: 'low' | 'high' | 'auto'): string | Object;
@@ -0,0 +1,8 @@
1
+ import { useFetch } from './useFetch.js';
2
+ import { getContext } from 'zuby/context/index.js';
3
+ export default function useProps(path, priority = 'auto') {
4
+ const { buildId } = getContext();
5
+ path = `/_props${path}/?${buildId}`;
6
+ path = path.replace(/\/+/g, '/');
7
+ return useFetch(path);
8
+ }
package/index.js CHANGED
@@ -11,6 +11,7 @@ export default () => ({
11
11
  layoutTemplateFile: normalizePath(resolve(__dirname, 'templates', 'layout.js')),
12
12
  innerLayoutTemplateFile: normalizePath(resolve(__dirname, 'templates', 'innerLayout.js')),
13
13
  errorTemplateFile: normalizePath(resolve(__dirname, 'templates', 'error.js')),
14
+ loaderTemplateFile: normalizePath(resolve(__dirname, 'templates', 'loader.js')),
14
15
  renderFile: normalizePath(resolve(__dirname, 'render.js')),
15
16
  getPlugins: () => react(),
16
17
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zubyjs/react",
3
- "version": "1.0.46",
3
+ "version": "1.0.48",
4
4
  "description": "Zuby.js JsxProvider for react",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/router.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { useRoute, useRouter, useParams, useLocation, Link, LinkProps, Redirect, RedirectProps, } from 'wouter';
1
+ export { useRoute, useRouter, useParams, useLocation, Redirect, RedirectProps } from 'wouter';
2
2
  import { LazyTemplate } from 'zuby/templates/types.js';
3
3
  import { ZubyPageContext } from 'zuby/pageContext/index.js';
4
4
  /**
@@ -15,3 +15,14 @@ export declare function Error({ error, context }: {
15
15
  error: LazyTemplate;
16
16
  context: ZubyPageContext;
17
17
  }): import("react/jsx-runtime").JSX.Element;
18
+ export declare function loadAsyncTemplates(templates?: LazyTemplate[]): any[];
19
+ export declare function loadSyncTemplates(templates?: LazyTemplate[]): any[];
20
+ export interface LinkProps {
21
+ href?: string;
22
+ to?: string;
23
+ activeClassName?: string;
24
+ className?: string;
25
+ onClick?: (event: MouseEvent) => void;
26
+ children?: any;
27
+ }
28
+ export declare function Link(props: LinkProps): import("react/jsx-runtime").JSX.Element;
package/router.js CHANGED
@@ -1,42 +1,41 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Router as WouterRouter, Route as WouterRoute, Switch as WouterSwitch } from 'wouter';
3
- export { useRoute, useRouter, useParams, useLocation, Link, Redirect, } from 'wouter';
2
+ import { Router as WouterRouter, Route as WouterRoute, Switch as WouterSwitch, Link as WouterLink, useRoute, } from 'wouter';
3
+ export { useRoute, useRouter, useParams, useLocation, Redirect } from 'wouter';
4
4
  import { useParams } from 'wouter';
5
- import { createElement, Suspense, lazy } from 'react';
6
- import { useFetch } from './hooks/index.js';
5
+ import { createElement, Suspense } from 'react';
6
+ import { preloadPage } from 'zuby/preload/index.js';
7
+ import lazyWithPreload from './components/lazyWithPreload.js';
8
+ import useProps from './hooks/useProps.js';
7
9
  let pages;
8
10
  let apps;
9
11
  let errors;
12
+ let loaders;
10
13
  /**
11
14
  * Zuby's Router component provides support for file-system based routing.
12
15
  */
13
16
  export default function Router({ context }) {
14
17
  const isProduction = import.meta?.env?.MODE === 'production';
15
- const loadTemplates = (templates) => templates?.map((pageTemplate) => ({
16
- ...pageTemplate,
17
- component: lazy(pageTemplate.component),
18
- })) || [];
19
18
  if (!pages || !isProduction) {
20
- pages = loadTemplates(context.templates?.pages);
19
+ pages = loadAsyncTemplates(context.templates?.pages);
21
20
  }
22
21
  if (!apps || !isProduction) {
23
- apps = loadTemplates(context.templates?.apps);
22
+ apps = loadAsyncTemplates(context.templates?.apps);
24
23
  }
25
24
  if (!errors || !isProduction) {
26
- errors = loadTemplates(context.templates?.errors);
25
+ errors = loadAsyncTemplates(context.templates?.errors);
27
26
  }
28
- const app = apps.find(({ pathRegex }) => {
29
- return pathRegex.test(context.url.pathname ?? '');
30
- });
31
- const error = errors.find(({ pathRegex }) => {
32
- return pathRegex.test(context.url.pathname ?? '');
33
- });
27
+ if (!loaders || !isProduction) {
28
+ loaders = loadSyncTemplates(context.templates?.loaders);
29
+ }
30
+ const app = matchTemplate(apps, context.url.pathname);
31
+ const error = matchTemplate(errors, context.url.pathname);
32
+ const loader = matchTemplate(loaders, context.url.pathname);
34
33
  const routes = [
35
34
  ...pages.map((page) => (_jsx(WouterRoute, { path: page.path, children: _jsx(Page, { context: context, page: page }) }, page.path))),
36
35
  _jsx(WouterRoute, { children: error?.component && _jsx(Error, { context: context, error: error }) }, "error"),
37
36
  ];
38
37
  const page = _jsx(WouterSwitch, { children: routes });
39
- return (_jsx(WouterRouter, { ssrPath: context.url.pathname, children: _jsx(Suspense, { fallback: _jsx("div", { children: "Loading..." }), children: createElement(app?.component, {
38
+ return (_jsx(WouterRouter, { ssrPath: context.url.pathname, children: _jsx(Suspense, { fallback: loader?.component && _jsx(loader.component, {}), children: createElement(app?.component, {
40
39
  children: page,
41
40
  }) }) }));
42
41
  }
@@ -44,7 +43,7 @@ export function Page({ page, context }) {
44
43
  context.statusCode = 200;
45
44
  context.params = useParams();
46
45
  if (typeof window !== 'undefined' && !context.isInitialPath) {
47
- context.props = useFetch(`/_props${context.url.pathname}`);
46
+ context.props = useProps(context.url.pathname);
48
47
  }
49
48
  return _jsx(page.component, { ...context.props, context: context });
50
49
  }
@@ -54,3 +53,36 @@ export function Error({ error, context }) {
54
53
  context.props = {};
55
54
  return _jsx(error.component, { context: context, message: 'Page was not found' });
56
55
  }
56
+ export function loadAsyncTemplates(templates) {
57
+ return (templates?.map((pageTemplate) => ({
58
+ ...pageTemplate,
59
+ component: lazyWithPreload(pageTemplate.component),
60
+ })) || []);
61
+ }
62
+ export function loadSyncTemplates(templates) {
63
+ return (templates?.map((pageTemplate) => ({
64
+ ...pageTemplate,
65
+ component: pageTemplate.component(),
66
+ })) || []);
67
+ }
68
+ function matchTemplate(template, path) {
69
+ return template.find(({ pathRegex }) => {
70
+ return pathRegex.test(path ?? '');
71
+ });
72
+ }
73
+ export function Link(props) {
74
+ const [isActive] = useRoute(props.href);
75
+ const href = props.href || props.to;
76
+ const className = `${props?.className} ${isActive ? props.activeClassName : ''}`;
77
+ // Preload the page
78
+ if (href) {
79
+ preloadPage(href, () => {
80
+ try {
81
+ matchTemplate(pages || [], href)?.component?.preload();
82
+ useProps(href, 'low');
83
+ }
84
+ catch (_e) { }
85
+ });
86
+ }
87
+ return (_jsx(WouterLink, { ...props, className: className, children: props.children }));
88
+ }
@@ -0,0 +1 @@
1
+ export default function Loader(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ export default function Loader() {
3
+ return _jsx(_Fragment, { children: "Loading..." });
4
+ }