@tramvai/module-render 2.70.1 → 2.72.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.
Files changed (58) hide show
  1. package/lib/browser.js +9 -233
  2. package/lib/client/index.browser.js +48 -0
  3. package/lib/client/renderer.browser.js +50 -0
  4. package/lib/react/index.browser.js +11 -0
  5. package/lib/react/index.es.js +11 -0
  6. package/lib/react/index.js +15 -0
  7. package/lib/react/pageErrorBoundary.browser.js +23 -0
  8. package/lib/react/pageErrorBoundary.es.js +23 -0
  9. package/lib/react/pageErrorBoundary.js +27 -0
  10. package/lib/react/root.browser.js +58 -0
  11. package/lib/react/root.es.js +58 -0
  12. package/lib/react/root.js +62 -0
  13. package/lib/resourcesInliner/externalFilesHelper.es.js +17 -0
  14. package/lib/resourcesInliner/externalFilesHelper.js +26 -0
  15. package/lib/resourcesInliner/fileProcessor.es.js +31 -0
  16. package/lib/resourcesInliner/fileProcessor.js +40 -0
  17. package/lib/resourcesInliner/resourcesInliner.es.js +204 -0
  18. package/lib/resourcesInliner/resourcesInliner.js +213 -0
  19. package/lib/resourcesInliner/tokens.es.js +15 -0
  20. package/lib/resourcesInliner/tokens.js +20 -0
  21. package/lib/resourcesRegistry/index.es.js +28 -0
  22. package/lib/resourcesRegistry/index.js +36 -0
  23. package/lib/server/PageBuilder.es.js +93 -0
  24. package/lib/server/PageBuilder.js +102 -0
  25. package/lib/server/ReactRenderServer.es.js +90 -0
  26. package/lib/server/ReactRenderServer.js +98 -0
  27. package/lib/server/blocks/bundleResource/bundleResource.es.js +62 -0
  28. package/lib/server/blocks/bundleResource/bundleResource.js +71 -0
  29. package/lib/server/blocks/polyfill.es.js +35 -0
  30. package/lib/server/blocks/polyfill.js +39 -0
  31. package/lib/{server_inline.inline.es.js → server/blocks/preload/onload.inline.es.js} +1 -1
  32. package/lib/{server_inline.inline.js → server/blocks/preload/onload.inline.js} +2 -0
  33. package/lib/server/blocks/preload/preloadBlock.es.js +21 -0
  34. package/lib/server/blocks/preload/preloadBlock.js +30 -0
  35. package/lib/server/blocks/utils/fetchWebpackStats.es.js +88 -0
  36. package/lib/server/blocks/utils/fetchWebpackStats.js +115 -0
  37. package/lib/server/blocks/utils/flushFiles.es.js +33 -0
  38. package/lib/server/blocks/utils/flushFiles.js +44 -0
  39. package/lib/server/blocks/utils/requireFunc.es.js +5 -0
  40. package/lib/server/blocks/utils/requireFunc.js +9 -0
  41. package/lib/server/constants/performance.es.js +3 -0
  42. package/lib/server/constants/performance.js +7 -0
  43. package/lib/server/htmlPageSchema.es.js +33 -0
  44. package/lib/server/htmlPageSchema.js +37 -0
  45. package/lib/server/utils.es.js +16 -0
  46. package/lib/server/utils.js +20 -0
  47. package/lib/server.es.js +18 -859
  48. package/lib/server.js +33 -909
  49. package/lib/shared/LayoutModule.browser.js +40 -0
  50. package/lib/shared/LayoutModule.es.js +40 -0
  51. package/lib/shared/LayoutModule.js +42 -0
  52. package/lib/shared/pageErrorStore.browser.js +19 -0
  53. package/lib/shared/pageErrorStore.es.js +19 -0
  54. package/lib/shared/pageErrorStore.js +26 -0
  55. package/lib/shared/providers.browser.js +18 -0
  56. package/lib/shared/providers.es.js +18 -0
  57. package/lib/shared/providers.js +22 -0
  58. package/package.json +23 -24
package/lib/browser.js CHANGED
@@ -1,238 +1,14 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { Module, provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core';
3
- import { COMBINE_REDUCERS, STORE_TOKEN, LOGGER_TOKEN, CONTEXT_TOKEN } from '@tramvai/tokens-common';
4
- import { DEFAULT_LAYOUT_COMPONENT, LAYOUT_OPTIONS, DEFAULT_FOOTER_COMPONENT, DEFAULT_HEADER_COMPONENT, DEFAULT_ERROR_BOUNDARY_COMPONENT, TRAMVAI_RENDER_MODE, RESOURCES_REGISTRY, CUSTOM_RENDER, EXTEND_RENDER, RENDERER_CALLBACK, USE_REACT_STRICT_MODE, RENDER_MODE } from '@tramvai/tokens-render';
3
+ import { STORE_TOKEN, LOGGER_TOKEN, CONTEXT_TOKEN } from '@tramvai/tokens-common';
4
+ import { RESOURCES_REGISTRY, CUSTOM_RENDER, EXTEND_RENDER, RENDERER_CALLBACK, USE_REACT_STRICT_MODE, RENDER_MODE } from '@tramvai/tokens-render';
5
5
  export * from '@tramvai/tokens-render';
6
- import { PAGE_SERVICE_TOKEN, ROUTER_TOKEN } from '@tramvai/tokens-router';
7
- import each from '@tinkoff/utils/array/each';
8
- import { useMemo, createElement, StrictMode } from 'react';
9
- import { jsx } from 'react/jsx-runtime';
10
- import { createEvent, createReducer, useStore, Provider } from '@tramvai/state';
11
- import { useDi, ERROR_BOUNDARY_TOKEN, ERROR_BOUNDARY_FALLBACK_COMPONENT_TOKEN, UniversalErrorBoundary, DIContext } from '@tramvai/react';
12
- import { useUrl, usePageService } from '@tramvai/module-router';
13
- import debounce from '@tinkoff/utils/function/debounce';
14
- import { useIsomorphicLayoutEffect } from '@tinkoff/react-hooks';
15
- import { composeLayoutOptions, createLayout } from '@tinkoff/layout-factory';
16
-
17
- function serializeError(error) {
18
- return {
19
- ...error,
20
- message: error.message,
21
- stack: error.stack,
22
- };
23
- }
24
- function deserializeError(serializedError) {
25
- const error = new Error(serializedError.message);
26
- Object.assign(error, serializedError);
27
- return error;
28
- }
29
- const setPageErrorEvent = createEvent('setPageError');
30
- const initialState = null;
31
- const PageErrorStore = createReducer('pageError', initialState).on(setPageErrorEvent, (state, error) => error && serializeError(error));
32
-
33
- const PageErrorBoundary = (props) => {
34
- const { children } = props;
35
- const pageService = useDi(PAGE_SERVICE_TOKEN);
36
- const url = useUrl();
37
- const serializedError = useStore(PageErrorStore);
38
- const error = useMemo(() => {
39
- return serializedError && deserializeError(serializedError);
40
- }, [serializedError]);
41
- const errorHandlers = useDi({ token: ERROR_BOUNDARY_TOKEN, optional: true });
42
- const fallbackFromDi = useDi({ token: ERROR_BOUNDARY_FALLBACK_COMPONENT_TOKEN, optional: true });
43
- const fallback = pageService.resolveComponentFromConfig('errorBoundary');
44
- return (jsx(UniversalErrorBoundary, { url: url, error: error, errorHandlers: errorHandlers, fallback: fallback, fallbackFromDi: fallbackFromDi, children: children }));
45
- };
46
-
47
- /**
48
- * Result component structure:
49
- *
50
- * <Root>
51
- * <RootComponent>
52
- * <LayoutComponent>
53
- * <NestedLayoutComponent>
54
- * <ErrorBoundaryComponent>
55
- * <PageComponent />
56
- * </ErrorBoundaryComponent>
57
- * </NestedLayoutComponent>
58
- * </LayoutComponent>
59
- * </RootComponent>
60
- * </Root>
61
- *
62
- * All components separated for a few reasons:
63
- * - Page subtree can be rendered independently when Layout and Nested Layout the same
64
- * - Nested Layout can be rerendered only on its changes
65
- * - Layout can be rendered only on its changes
66
- */
67
- const LayoutRenderComponent = ({ children }) => {
68
- const pageService = usePageService();
69
- const LayoutComponent = pageService.resolveComponentFromConfig('layout');
70
- const HeaderComponent = pageService.resolveComponentFromConfig('header');
71
- const FooterComponent = pageService.resolveComponentFromConfig('footer');
72
- const layout = useMemo(() => (jsx(LayoutComponent, { Header: HeaderComponent, Footer: FooterComponent, children: children })), [LayoutComponent, HeaderComponent, FooterComponent, children]);
73
- return layout;
74
- };
75
- const NestedLayoutRenderComponent = ({ children }) => {
76
- const pageService = usePageService();
77
- const NestedLayoutComponent = pageService.resolveComponentFromConfig('nestedLayout');
78
- const nestedLayout = useMemo(() => jsx(NestedLayoutComponent, { children: children }), [NestedLayoutComponent, children]);
79
- return nestedLayout;
80
- };
81
- const PageRenderComponent = () => {
82
- const pageService = usePageService();
83
- const { pageComponent } = pageService.getConfig();
84
- let PageComponent = pageService.getComponent(pageComponent);
85
- if (!PageComponent) {
86
- PageComponent = () => {
87
- throw new Error(`Page component '${pageComponent}' not found`);
88
- };
89
- }
90
- const page = useMemo(() => (jsx(PageErrorBoundary, { children: jsx(PageComponent, {}) })), [PageComponent]);
91
- return page;
92
- };
93
- const Root = () => {
94
- const pageRenderComponent = useMemo(() => jsx(PageRenderComponent, {}), []);
95
- const nestedLayoutRenderComponent = useMemo(() => jsx(NestedLayoutRenderComponent, { children: pageRenderComponent }), [pageRenderComponent]);
96
- return jsx(LayoutRenderComponent, { children: nestedLayoutRenderComponent });
97
- };
98
-
99
- function renderReact({ di }, context) {
100
- const serverState = typeof window !== 'undefined' ? context.getState() : undefined;
101
- return (jsx(Provider, { context: context, serverState: serverState, children: jsx(DIContext.Provider, { value: di, children: jsx(Root, {}) }) }));
102
- }
103
-
104
- let hydrateRoot;
105
- let startTransition;
106
- try {
107
- // eslint-disable-next-line import/no-unresolved, import/extensions
108
- hydrateRoot = require('react-dom/client').hydrateRoot;
109
- startTransition = require('react').startTransition;
110
- }
111
- catch { }
112
- const ExecuteRenderCallback = ({ children, callback, }) => {
113
- // eslint-disable-next-line react-hooks/exhaustive-deps
114
- useIsomorphicLayoutEffect(callback, []);
115
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
- return children;
117
- };
118
- const renderer = ({ element, container, callback, log }) => {
119
- if (process.env.__TRAMVAI_CONCURRENT_FEATURES && typeof hydrateRoot === 'function') {
120
- const wrappedElement = createElement(ExecuteRenderCallback, { callback }, element);
121
- let allErrors = new Map();
122
- const logHydrateRecoverableError = debounce(50, () => {
123
- if (allErrors.size === 0) {
124
- return;
125
- }
126
- const [{ error, errorInfo }, ...otherErrors] = Array.from(allErrors.values());
127
- allErrors = new Map();
128
- log.error({
129
- event: 'hydrate:recover-after-error',
130
- error,
131
- errorInfo,
132
- otherErrors,
133
- });
134
- });
135
- return startTransition(() => {
136
- hydrateRoot(container, wrappedElement, {
137
- onRecoverableError: (error, errorInfo) => {
138
- // deduplicate by unique important string values
139
- allErrors.set((error === null || error === void 0 ? void 0 : error.message) + (errorInfo === null || errorInfo === void 0 ? void 0 : errorInfo.componentStack), { error, errorInfo });
140
- logHydrateRecoverableError();
141
- },
142
- });
143
- });
144
- }
145
- const { hydrate } = require('react-dom');
146
- return hydrate(element, container, callback);
147
- };
148
-
149
- function rendering({ logger, consumerContext, customRender, extendRender, di, useStrictMode, rendererCallback, }) {
150
- const log = logger('module-render');
151
- return new Promise((resolve, reject) => {
152
- let renderResult = renderReact({ di }, consumerContext);
153
- if (extendRender) {
154
- each((render) => {
155
- renderResult = render(renderResult);
156
- }, extendRender);
157
- }
158
- if (customRender) {
159
- return customRender(renderResult);
160
- }
161
- if (useStrictMode) {
162
- renderResult = createElement(StrictMode, null, renderResult);
163
- }
164
- const container = document.querySelector('.application');
165
- const executeRendererCallbacks = (renderErr) => rendererCallback === null || rendererCallback === void 0 ? void 0 : rendererCallback.forEach((cb) => {
166
- try {
167
- cb(renderErr);
168
- }
169
- catch (cbError) {
170
- // eslint-disable-next-line no-console
171
- console.error(cbError);
172
- }
173
- });
174
- const callback = () => {
175
- log.debug('App rendering');
176
- document.querySelector('html').classList.remove('no-js');
177
- executeRendererCallbacks();
178
- resolve();
179
- };
180
- const params = { element: renderResult, container, callback, log };
181
- try {
182
- renderer(params);
183
- }
184
- catch (e) {
185
- executeRendererCallbacks(e);
186
- reject(e);
187
- }
188
- });
189
- }
190
-
191
- const RenderChildrenComponent = ({ children }) => children;
192
- let LayoutModule = class LayoutModule {
193
- };
194
- LayoutModule = __decorate([
195
- Module({
196
- providers: [
197
- {
198
- provide: DEFAULT_LAYOUT_COMPONENT,
199
- useFactory: ({ layoutOptions }) => {
200
- const options = composeLayoutOptions(layoutOptions);
201
- return createLayout(options);
202
- },
203
- deps: {
204
- layoutOptions: { token: LAYOUT_OPTIONS, optional: true },
205
- },
206
- },
207
- {
208
- provide: 'componentDefaultList',
209
- multi: true,
210
- useFactory: (components) => ({
211
- ...components,
212
- nestedLayoutDefault: RenderChildrenComponent,
213
- }),
214
- deps: {
215
- layoutDefault: DEFAULT_LAYOUT_COMPONENT,
216
- footerDefault: { token: DEFAULT_FOOTER_COMPONENT, optional: true },
217
- headerDefault: { token: DEFAULT_HEADER_COMPONENT, optional: true },
218
- errorBoundaryDefault: { token: DEFAULT_ERROR_BOUNDARY_COMPONENT, optional: true },
219
- },
220
- },
221
- ],
222
- })
223
- ], LayoutModule);
224
-
225
- const providers = [
226
- provide({
227
- provide: COMBINE_REDUCERS,
228
- multi: true,
229
- useValue: PageErrorStore,
230
- }),
231
- provide({
232
- provide: TRAMVAI_RENDER_MODE,
233
- useValue: 'ssr',
234
- }),
235
- ];
6
+ import { ROUTER_TOKEN } from '@tramvai/tokens-router';
7
+ import { rendering } from './client/index.browser.js';
8
+ import { LayoutModule } from './shared/LayoutModule.browser.js';
9
+ import { providers } from './shared/providers.browser.js';
10
+ import { PageErrorStore, setPageErrorEvent } from './shared/pageErrorStore.browser.js';
11
+ export { PageErrorStore, deserializeError, serializeError, setPageErrorEvent } from './shared/pageErrorStore.browser.js';
236
12
 
237
13
  var RenderModule_1;
238
14
  const DEFAULT_POLYFILL_CONDITION = '';
@@ -339,4 +115,4 @@ RenderModule = RenderModule_1 = __decorate([
339
115
  })
340
116
  ], RenderModule);
341
117
 
342
- export { DEFAULT_POLYFILL_CONDITION, PageErrorStore, RenderModule, deserializeError, serializeError, setPageErrorEvent };
118
+ export { DEFAULT_POLYFILL_CONDITION, RenderModule };
@@ -0,0 +1,48 @@
1
+ import each from '@tinkoff/utils/array/each';
2
+ import { createElement, StrictMode } from 'react';
3
+ import { renderReact } from '../react/index.browser.js';
4
+ import { renderer } from './renderer.browser.js';
5
+
6
+ function rendering({ logger, consumerContext, customRender, extendRender, di, useStrictMode, rendererCallback, }) {
7
+ const log = logger('module-render');
8
+ return new Promise((resolve, reject) => {
9
+ let renderResult = renderReact({ di }, consumerContext);
10
+ if (extendRender) {
11
+ each((render) => {
12
+ renderResult = render(renderResult);
13
+ }, extendRender);
14
+ }
15
+ if (customRender) {
16
+ return customRender(renderResult);
17
+ }
18
+ if (useStrictMode) {
19
+ renderResult = createElement(StrictMode, null, renderResult);
20
+ }
21
+ const container = document.querySelector('.application');
22
+ const executeRendererCallbacks = (renderErr) => rendererCallback === null || rendererCallback === void 0 ? void 0 : rendererCallback.forEach((cb) => {
23
+ try {
24
+ cb(renderErr);
25
+ }
26
+ catch (cbError) {
27
+ // eslint-disable-next-line no-console
28
+ console.error(cbError);
29
+ }
30
+ });
31
+ const callback = () => {
32
+ log.debug('App rendering');
33
+ document.querySelector('html').classList.remove('no-js');
34
+ executeRendererCallbacks();
35
+ resolve();
36
+ };
37
+ const params = { element: renderResult, container, callback, log };
38
+ try {
39
+ renderer(params);
40
+ }
41
+ catch (e) {
42
+ executeRendererCallbacks(e);
43
+ reject(e);
44
+ }
45
+ });
46
+ }
47
+
48
+ export { rendering };
@@ -0,0 +1,50 @@
1
+ import debounce from '@tinkoff/utils/function/debounce';
2
+ import { createElement } from 'react';
3
+ import { useIsomorphicLayoutEffect } from '@tinkoff/react-hooks';
4
+
5
+ let hydrateRoot;
6
+ let startTransition;
7
+ try {
8
+ // eslint-disable-next-line import/no-unresolved, import/extensions
9
+ hydrateRoot = require('react-dom/client').hydrateRoot;
10
+ startTransition = require('react').startTransition;
11
+ }
12
+ catch { }
13
+ const ExecuteRenderCallback = ({ children, callback, }) => {
14
+ // eslint-disable-next-line react-hooks/exhaustive-deps
15
+ useIsomorphicLayoutEffect(callback, []);
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ return children;
18
+ };
19
+ const renderer = ({ element, container, callback, log }) => {
20
+ if (process.env.__TRAMVAI_CONCURRENT_FEATURES && typeof hydrateRoot === 'function') {
21
+ const wrappedElement = createElement(ExecuteRenderCallback, { callback }, element);
22
+ let allErrors = new Map();
23
+ const logHydrateRecoverableError = debounce(50, () => {
24
+ if (allErrors.size === 0) {
25
+ return;
26
+ }
27
+ const [{ error, errorInfo }, ...otherErrors] = Array.from(allErrors.values());
28
+ allErrors = new Map();
29
+ log.error({
30
+ event: 'hydrate:recover-after-error',
31
+ error,
32
+ errorInfo,
33
+ otherErrors,
34
+ });
35
+ });
36
+ return startTransition(() => {
37
+ hydrateRoot(container, wrappedElement, {
38
+ onRecoverableError: (error, errorInfo) => {
39
+ // deduplicate by unique important string values
40
+ allErrors.set((error === null || error === void 0 ? void 0 : error.message) + (errorInfo === null || errorInfo === void 0 ? void 0 : errorInfo.componentStack), { error, errorInfo });
41
+ logHydrateRecoverableError();
42
+ },
43
+ });
44
+ });
45
+ }
46
+ const { hydrate } = require('react-dom');
47
+ return hydrate(element, container, callback);
48
+ };
49
+
50
+ export { renderer };
@@ -0,0 +1,11 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { Provider } from '@tramvai/state';
3
+ import { DIContext } from '@tramvai/react';
4
+ import { Root } from './root.browser.js';
5
+
6
+ function renderReact({ di }, context) {
7
+ const serverState = typeof window !== 'undefined' ? context.getState() : undefined;
8
+ return (jsx(Provider, { context: context, serverState: serverState, children: jsx(DIContext.Provider, { value: di, children: jsx(Root, {}) }) }));
9
+ }
10
+
11
+ export { renderReact };
@@ -0,0 +1,11 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { Provider } from '@tramvai/state';
3
+ import { DIContext } from '@tramvai/react';
4
+ import { Root } from './root.es.js';
5
+
6
+ function renderReact({ di }, context) {
7
+ const serverState = typeof window !== 'undefined' ? context.getState() : undefined;
8
+ return (jsx(Provider, { context: context, serverState: serverState, children: jsx(DIContext.Provider, { value: di, children: jsx(Root, {}) }) }));
9
+ }
10
+
11
+ export { renderReact };
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var state = require('@tramvai/state');
7
+ var react = require('@tramvai/react');
8
+ var root = require('./root.js');
9
+
10
+ function renderReact({ di }, context) {
11
+ const serverState = typeof window !== 'undefined' ? context.getState() : undefined;
12
+ return (jsxRuntime.jsx(state.Provider, { context: context, serverState: serverState, children: jsxRuntime.jsx(react.DIContext.Provider, { value: di, children: jsxRuntime.jsx(root.Root, {}) }) }));
13
+ }
14
+
15
+ exports.renderReact = renderReact;
@@ -0,0 +1,23 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { useDi, ERROR_BOUNDARY_TOKEN, ERROR_BOUNDARY_FALLBACK_COMPONENT_TOKEN, UniversalErrorBoundary } from '@tramvai/react';
4
+ import { useUrl } from '@tramvai/module-router';
5
+ import { PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
6
+ import { useStore } from '@tramvai/state';
7
+ import { PageErrorStore, deserializeError } from '../shared/pageErrorStore.browser.js';
8
+
9
+ const PageErrorBoundary = (props) => {
10
+ const { children } = props;
11
+ const pageService = useDi(PAGE_SERVICE_TOKEN);
12
+ const url = useUrl();
13
+ const serializedError = useStore(PageErrorStore);
14
+ const error = useMemo(() => {
15
+ return serializedError && deserializeError(serializedError);
16
+ }, [serializedError]);
17
+ const errorHandlers = useDi({ token: ERROR_BOUNDARY_TOKEN, optional: true });
18
+ const fallbackFromDi = useDi({ token: ERROR_BOUNDARY_FALLBACK_COMPONENT_TOKEN, optional: true });
19
+ const fallback = pageService.resolveComponentFromConfig('errorBoundary');
20
+ return (jsx(UniversalErrorBoundary, { url: url, error: error, errorHandlers: errorHandlers, fallback: fallback, fallbackFromDi: fallbackFromDi, children: children }));
21
+ };
22
+
23
+ export { PageErrorBoundary };
@@ -0,0 +1,23 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { useDi, ERROR_BOUNDARY_TOKEN, ERROR_BOUNDARY_FALLBACK_COMPONENT_TOKEN, UniversalErrorBoundary } from '@tramvai/react';
4
+ import { useUrl } from '@tramvai/module-router';
5
+ import { PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
6
+ import { useStore } from '@tramvai/state';
7
+ import { PageErrorStore, deserializeError } from '../shared/pageErrorStore.es.js';
8
+
9
+ const PageErrorBoundary = (props) => {
10
+ const { children } = props;
11
+ const pageService = useDi(PAGE_SERVICE_TOKEN);
12
+ const url = useUrl();
13
+ const serializedError = useStore(PageErrorStore);
14
+ const error = useMemo(() => {
15
+ return serializedError && deserializeError(serializedError);
16
+ }, [serializedError]);
17
+ const errorHandlers = useDi({ token: ERROR_BOUNDARY_TOKEN, optional: true });
18
+ const fallbackFromDi = useDi({ token: ERROR_BOUNDARY_FALLBACK_COMPONENT_TOKEN, optional: true });
19
+ const fallback = pageService.resolveComponentFromConfig('errorBoundary');
20
+ return (jsx(UniversalErrorBoundary, { url: url, error: error, errorHandlers: errorHandlers, fallback: fallback, fallbackFromDi: fallbackFromDi, children: children }));
21
+ };
22
+
23
+ export { PageErrorBoundary };
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var react$1 = require('react');
7
+ var react = require('@tramvai/react');
8
+ var moduleRouter = require('@tramvai/module-router');
9
+ var tokensRouter = require('@tramvai/tokens-router');
10
+ var state = require('@tramvai/state');
11
+ var pageErrorStore = require('../shared/pageErrorStore.js');
12
+
13
+ const PageErrorBoundary = (props) => {
14
+ const { children } = props;
15
+ const pageService = react.useDi(tokensRouter.PAGE_SERVICE_TOKEN);
16
+ const url = moduleRouter.useUrl();
17
+ const serializedError = state.useStore(pageErrorStore.PageErrorStore);
18
+ const error = react$1.useMemo(() => {
19
+ return serializedError && pageErrorStore.deserializeError(serializedError);
20
+ }, [serializedError]);
21
+ const errorHandlers = react.useDi({ token: react.ERROR_BOUNDARY_TOKEN, optional: true });
22
+ const fallbackFromDi = react.useDi({ token: react.ERROR_BOUNDARY_FALLBACK_COMPONENT_TOKEN, optional: true });
23
+ const fallback = pageService.resolveComponentFromConfig('errorBoundary');
24
+ return (jsxRuntime.jsx(react.UniversalErrorBoundary, { url: url, error: error, errorHandlers: errorHandlers, fallback: fallback, fallbackFromDi: fallbackFromDi, children: children }));
25
+ };
26
+
27
+ exports.PageErrorBoundary = PageErrorBoundary;
@@ -0,0 +1,58 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { usePageService } from '@tramvai/module-router';
4
+ import { PageErrorBoundary } from './pageErrorBoundary.browser.js';
5
+
6
+ /**
7
+ * Result component structure:
8
+ *
9
+ * <Root>
10
+ * <RootComponent>
11
+ * <LayoutComponent>
12
+ * <NestedLayoutComponent>
13
+ * <ErrorBoundaryComponent>
14
+ * <PageComponent />
15
+ * </ErrorBoundaryComponent>
16
+ * </NestedLayoutComponent>
17
+ * </LayoutComponent>
18
+ * </RootComponent>
19
+ * </Root>
20
+ *
21
+ * All components separated for a few reasons:
22
+ * - Page subtree can be rendered independently when Layout and Nested Layout the same
23
+ * - Nested Layout can be rerendered only on its changes
24
+ * - Layout can be rendered only on its changes
25
+ */
26
+ const LayoutRenderComponent = ({ children }) => {
27
+ const pageService = usePageService();
28
+ const LayoutComponent = pageService.resolveComponentFromConfig('layout');
29
+ const HeaderComponent = pageService.resolveComponentFromConfig('header');
30
+ const FooterComponent = pageService.resolveComponentFromConfig('footer');
31
+ const layout = useMemo(() => (jsx(LayoutComponent, { Header: HeaderComponent, Footer: FooterComponent, children: children })), [LayoutComponent, HeaderComponent, FooterComponent, children]);
32
+ return layout;
33
+ };
34
+ const NestedLayoutRenderComponent = ({ children }) => {
35
+ const pageService = usePageService();
36
+ const NestedLayoutComponent = pageService.resolveComponentFromConfig('nestedLayout');
37
+ const nestedLayout = useMemo(() => jsx(NestedLayoutComponent, { children: children }), [NestedLayoutComponent, children]);
38
+ return nestedLayout;
39
+ };
40
+ const PageRenderComponent = () => {
41
+ const pageService = usePageService();
42
+ const { pageComponent } = pageService.getConfig();
43
+ let PageComponent = pageService.getComponent(pageComponent);
44
+ if (!PageComponent) {
45
+ PageComponent = () => {
46
+ throw new Error(`Page component '${pageComponent}' not found`);
47
+ };
48
+ }
49
+ const page = useMemo(() => (jsx(PageErrorBoundary, { children: jsx(PageComponent, {}) })), [PageComponent]);
50
+ return page;
51
+ };
52
+ const Root = () => {
53
+ const pageRenderComponent = useMemo(() => jsx(PageRenderComponent, {}), []);
54
+ const nestedLayoutRenderComponent = useMemo(() => jsx(NestedLayoutRenderComponent, { children: pageRenderComponent }), [pageRenderComponent]);
55
+ return jsx(LayoutRenderComponent, { children: nestedLayoutRenderComponent });
56
+ };
57
+
58
+ export { Root };
@@ -0,0 +1,58 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { usePageService } from '@tramvai/module-router';
4
+ import { PageErrorBoundary } from './pageErrorBoundary.es.js';
5
+
6
+ /**
7
+ * Result component structure:
8
+ *
9
+ * <Root>
10
+ * <RootComponent>
11
+ * <LayoutComponent>
12
+ * <NestedLayoutComponent>
13
+ * <ErrorBoundaryComponent>
14
+ * <PageComponent />
15
+ * </ErrorBoundaryComponent>
16
+ * </NestedLayoutComponent>
17
+ * </LayoutComponent>
18
+ * </RootComponent>
19
+ * </Root>
20
+ *
21
+ * All components separated for a few reasons:
22
+ * - Page subtree can be rendered independently when Layout and Nested Layout the same
23
+ * - Nested Layout can be rerendered only on its changes
24
+ * - Layout can be rendered only on its changes
25
+ */
26
+ const LayoutRenderComponent = ({ children }) => {
27
+ const pageService = usePageService();
28
+ const LayoutComponent = pageService.resolveComponentFromConfig('layout');
29
+ const HeaderComponent = pageService.resolveComponentFromConfig('header');
30
+ const FooterComponent = pageService.resolveComponentFromConfig('footer');
31
+ const layout = useMemo(() => (jsx(LayoutComponent, { Header: HeaderComponent, Footer: FooterComponent, children: children })), [LayoutComponent, HeaderComponent, FooterComponent, children]);
32
+ return layout;
33
+ };
34
+ const NestedLayoutRenderComponent = ({ children }) => {
35
+ const pageService = usePageService();
36
+ const NestedLayoutComponent = pageService.resolveComponentFromConfig('nestedLayout');
37
+ const nestedLayout = useMemo(() => jsx(NestedLayoutComponent, { children: children }), [NestedLayoutComponent, children]);
38
+ return nestedLayout;
39
+ };
40
+ const PageRenderComponent = () => {
41
+ const pageService = usePageService();
42
+ const { pageComponent } = pageService.getConfig();
43
+ let PageComponent = pageService.getComponent(pageComponent);
44
+ if (!PageComponent) {
45
+ PageComponent = () => {
46
+ throw new Error(`Page component '${pageComponent}' not found`);
47
+ };
48
+ }
49
+ const page = useMemo(() => (jsx(PageErrorBoundary, { children: jsx(PageComponent, {}) })), [PageComponent]);
50
+ return page;
51
+ };
52
+ const Root = () => {
53
+ const pageRenderComponent = useMemo(() => jsx(PageRenderComponent, {}), []);
54
+ const nestedLayoutRenderComponent = useMemo(() => jsx(NestedLayoutRenderComponent, { children: pageRenderComponent }), [pageRenderComponent]);
55
+ return jsx(LayoutRenderComponent, { children: nestedLayoutRenderComponent });
56
+ };
57
+
58
+ export { Root };
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var react = require('react');
7
+ var moduleRouter = require('@tramvai/module-router');
8
+ var pageErrorBoundary = require('./pageErrorBoundary.js');
9
+
10
+ /**
11
+ * Result component structure:
12
+ *
13
+ * <Root>
14
+ * <RootComponent>
15
+ * <LayoutComponent>
16
+ * <NestedLayoutComponent>
17
+ * <ErrorBoundaryComponent>
18
+ * <PageComponent />
19
+ * </ErrorBoundaryComponent>
20
+ * </NestedLayoutComponent>
21
+ * </LayoutComponent>
22
+ * </RootComponent>
23
+ * </Root>
24
+ *
25
+ * All components separated for a few reasons:
26
+ * - Page subtree can be rendered independently when Layout and Nested Layout the same
27
+ * - Nested Layout can be rerendered only on its changes
28
+ * - Layout can be rendered only on its changes
29
+ */
30
+ const LayoutRenderComponent = ({ children }) => {
31
+ const pageService = moduleRouter.usePageService();
32
+ const LayoutComponent = pageService.resolveComponentFromConfig('layout');
33
+ const HeaderComponent = pageService.resolveComponentFromConfig('header');
34
+ const FooterComponent = pageService.resolveComponentFromConfig('footer');
35
+ const layout = react.useMemo(() => (jsxRuntime.jsx(LayoutComponent, { Header: HeaderComponent, Footer: FooterComponent, children: children })), [LayoutComponent, HeaderComponent, FooterComponent, children]);
36
+ return layout;
37
+ };
38
+ const NestedLayoutRenderComponent = ({ children }) => {
39
+ const pageService = moduleRouter.usePageService();
40
+ const NestedLayoutComponent = pageService.resolveComponentFromConfig('nestedLayout');
41
+ const nestedLayout = react.useMemo(() => jsxRuntime.jsx(NestedLayoutComponent, { children: children }), [NestedLayoutComponent, children]);
42
+ return nestedLayout;
43
+ };
44
+ const PageRenderComponent = () => {
45
+ const pageService = moduleRouter.usePageService();
46
+ const { pageComponent } = pageService.getConfig();
47
+ let PageComponent = pageService.getComponent(pageComponent);
48
+ if (!PageComponent) {
49
+ PageComponent = () => {
50
+ throw new Error(`Page component '${pageComponent}' not found`);
51
+ };
52
+ }
53
+ const page = react.useMemo(() => (jsxRuntime.jsx(pageErrorBoundary.PageErrorBoundary, { children: jsxRuntime.jsx(PageComponent, {}) })), [PageComponent]);
54
+ return page;
55
+ };
56
+ const Root = () => {
57
+ const pageRenderComponent = react.useMemo(() => jsxRuntime.jsx(PageRenderComponent, {}), []);
58
+ const nestedLayoutRenderComponent = react.useMemo(() => jsxRuntime.jsx(NestedLayoutRenderComponent, { children: pageRenderComponent }), [pageRenderComponent]);
59
+ return jsxRuntime.jsx(LayoutRenderComponent, { children: nestedLayoutRenderComponent });
60
+ };
61
+
62
+ exports.Root = Root;
@@ -0,0 +1,17 @@
1
+ import fetch from 'node-fetch';
2
+
3
+ const thirtySeconds = 1000 * 30;
4
+ const getFileContentLength = async (url) => {
5
+ const info = await fetch(url, { method: 'HEAD', timeout: thirtySeconds });
6
+ return info.headers.get('content-length');
7
+ };
8
+ const getFile = async (url) => {
9
+ const fileResponse = await fetch(url, { timeout: thirtySeconds });
10
+ if (fileResponse.ok) {
11
+ const file = await fileResponse.text();
12
+ return file;
13
+ }
14
+ return undefined;
15
+ };
16
+
17
+ export { getFile, getFileContentLength };