@tramvai/module-render 2.40.0 → 2.44.2
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 +69 -5
- package/lib/browser.js +56 -24
- package/lib/client/index.d.ts +1 -3
- package/lib/client/types.d.ts +1 -1
- package/lib/react/index.d.ts +1 -2
- package/lib/react/pageErrorBoundary.d.ts +1 -4
- package/lib/react/root.d.ts +1 -4
- package/lib/resourcesInliner/tokens.d.ts +1 -1
- package/lib/resourcesRegistry/index.d.ts +2 -2
- package/lib/server/ReactRenderServer.d.ts +1 -4
- package/lib/server/blocks/utils/fetchWebpackStats.d.ts +1 -1
- package/lib/server.es.js +55 -24
- package/lib/server.js +53 -22
- package/lib/shared/pageErrorStore.d.ts +2 -2
- package/lib/shared/types.d.ts +1 -1
- package/lib/test/unit/resources.d.ts +1 -1
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -143,13 +143,13 @@ To speed up data loading, we've added a preloading system for resources and asyn
|
|
|
143
143
|
- Next we add all the CSS to the **preload** tag and add onload event on them. We need to load the blocking resources as quickly as possible.
|
|
144
144
|
- When loading any CSS file, onload event will be fired (only once time) and add all **preload** tags to the necessary JS files
|
|
145
145
|
|
|
146
|
-
###
|
|
146
|
+
### Root layout
|
|
147
147
|
|
|
148
|
-
The `RenderModule` has a default
|
|
148
|
+
The `RenderModule` has a default root layout that supports different ways of extending and adding functionality
|
|
149
149
|
|
|
150
150
|
[Read more about layout on the library page](references/libs/tinkoff-layout.md)
|
|
151
151
|
|
|
152
|
-
#### Adding a
|
|
152
|
+
#### Adding a default header and footer
|
|
153
153
|
|
|
154
154
|
The module allows you to add header and footer components, which will be rendered by default for all pages
|
|
155
155
|
|
|
@@ -191,7 +191,7 @@ createBundle({
|
|
|
191
191
|
|
|
192
192
|
#### Adding components and wrappers
|
|
193
193
|
|
|
194
|
-
You can add custom components and wrappers for layout via the token `LAYOUT_OPTIONS
|
|
194
|
+
You can add custom components and wrappers for layout via the token `LAYOUT_OPTIONS`, this wrappers will be applied on every application page:
|
|
195
195
|
|
|
196
196
|
```tsx
|
|
197
197
|
import { provide } from '@tramvai/core';
|
|
@@ -226,7 +226,13 @@ export class MyLayoutModule {}
|
|
|
226
226
|
|
|
227
227
|
More details about the `components` and `wrappers` options can be found in [@tinkoff/layout-factory](references/libs/tinkoff-layout.md)
|
|
228
228
|
|
|
229
|
-
#### Replacing the
|
|
229
|
+
#### Replacing the root layout
|
|
230
|
+
|
|
231
|
+
:::warning
|
|
232
|
+
|
|
233
|
+
Not recommended, because a lot of dependant features can be broken!
|
|
234
|
+
|
|
235
|
+
:::
|
|
230
236
|
|
|
231
237
|
If the basic layout doesn't work for you, you can replace it with any other React component.
|
|
232
238
|
In doing so, you need to implement all the wrappers yourself and plug in global components if you need them.
|
|
@@ -268,6 +274,64 @@ createBundle({
|
|
|
268
274
|
});
|
|
269
275
|
```
|
|
270
276
|
|
|
277
|
+
### Nested layout
|
|
278
|
+
|
|
279
|
+
For every page, nested layout can be applied. It is useful when you need to wrap group of pages in the same block, or add the same actions.
|
|
280
|
+
|
|
281
|
+
:::note
|
|
282
|
+
|
|
283
|
+
For now, only one level of layout nesting supported, and simplified component structure will look like this:
|
|
284
|
+
|
|
285
|
+
```tsx
|
|
286
|
+
<RootLayout>
|
|
287
|
+
<NestedLayout>
|
|
288
|
+
<Page />
|
|
289
|
+
</NestedLayout>
|
|
290
|
+
</RootLayout>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
:::
|
|
294
|
+
|
|
295
|
+
Nested layout it is a simple React component with `children` property, and static properties `actions` and `reducers` are supported:
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
import type { NestedLayoutComponent } from '@tramvai/react';
|
|
299
|
+
|
|
300
|
+
const Layout: NestedLayoutComponent = ({ children }) => {
|
|
301
|
+
return <div>{children}</div>;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
Layout.actions = [actionFoo, actionBar];
|
|
305
|
+
|
|
306
|
+
Layout.reducers = [StoreBaz];
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Actions will be registered as current page component actions and reducers will be registered in global store.
|
|
310
|
+
|
|
311
|
+
#### Adding a nested layout
|
|
312
|
+
|
|
313
|
+
You can add a `nestedLayoutComponent` property to route `config` property and register component in `bundle`.
|
|
314
|
+
This layout will be rendered when you go to the corresponding route.
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
createBundle({
|
|
318
|
+
name: 'common-bundle',
|
|
319
|
+
components: {
|
|
320
|
+
myNestedLayout: NestedLayout,
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const route = {
|
|
325
|
+
name: 'main',
|
|
326
|
+
path: '/',
|
|
327
|
+
config: {
|
|
328
|
+
nestedLayoutComponent: 'myNestedLayout',
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Also, this feature available for [File-System Pages and Routes](features/routing/file-system-pages.md)
|
|
334
|
+
|
|
271
335
|
## How to
|
|
272
336
|
|
|
273
337
|
### How to add assets loading to a page
|
package/lib/browser.js
CHANGED
|
@@ -3,36 +3,64 @@ import { Module, provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core'
|
|
|
3
3
|
import { COMBINE_REDUCERS, STORE_TOKEN, LOGGER_TOKEN, CONTEXT_TOKEN } from '@tramvai/tokens-common';
|
|
4
4
|
import { DEFAULT_LAYOUT_COMPONENT, LAYOUT_OPTIONS, DEFAULT_FOOTER_COMPONENT, DEFAULT_HEADER_COMPONENT, TRAMVAI_RENDER_MODE, 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 {
|
|
6
|
+
import { PAGE_SERVICE_TOKEN, ROUTER_TOKEN } from '@tramvai/tokens-router';
|
|
7
7
|
import each from '@tinkoff/utils/array/each';
|
|
8
|
-
import {
|
|
8
|
+
import { useMemo, createElement, StrictMode } from 'react';
|
|
9
9
|
import { jsx } from 'react/jsx-runtime';
|
|
10
10
|
import { createEvent, createReducer, useStore, Provider } from '@tramvai/state';
|
|
11
11
|
import { useDi, ERROR_BOUNDARY_TOKEN, ERROR_BOUNDARY_FALLBACK_COMPONENT_TOKEN, UniversalErrorBoundary, DIContext } from '@tramvai/react';
|
|
12
|
-
import {
|
|
12
|
+
import { usePageService, useUrl } from '@tramvai/module-router';
|
|
13
13
|
import { useIsomorphicLayoutEffect } from '@tinkoff/react-hooks';
|
|
14
14
|
import { composeLayoutOptions, createLayout } from '@tinkoff/layout-factory';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Result component structure:
|
|
18
|
+
*
|
|
19
|
+
* <Root>
|
|
20
|
+
* <RootComponent>
|
|
21
|
+
* <LayoutComponent>
|
|
22
|
+
* <NestedLayoutComponent>
|
|
23
|
+
* <PageComponent />
|
|
24
|
+
* </NestedLayoutComponent>
|
|
25
|
+
* </LayoutComponent>
|
|
26
|
+
* </RootComponent>
|
|
27
|
+
* </Root>
|
|
28
|
+
*
|
|
29
|
+
* All components separated for a few reasons:
|
|
30
|
+
* - Page subtree can be rendered independently when Layout and Nested Layout the same
|
|
31
|
+
* - Nested Layout can be rerendered only on its changes
|
|
32
|
+
* - Layout can be rendered only on its changes
|
|
33
|
+
*/
|
|
34
|
+
const LayoutRenderComponent = ({ children }) => {
|
|
35
|
+
const pageService = usePageService();
|
|
36
|
+
const LayoutComponent = pageService.resolveComponentFromConfig('layout');
|
|
37
|
+
const HeaderComponent = pageService.resolveComponentFromConfig('header');
|
|
38
|
+
const FooterComponent = pageService.resolveComponentFromConfig('footer');
|
|
39
|
+
const layout = useMemo(() => (jsx(LayoutComponent, { Header: HeaderComponent, Footer: FooterComponent, children: children })), [LayoutComponent, HeaderComponent, FooterComponent, children]);
|
|
40
|
+
return layout;
|
|
41
|
+
};
|
|
42
|
+
const NestedLayoutRenderComponent = ({ children }) => {
|
|
43
|
+
const pageService = usePageService();
|
|
44
|
+
const NestedLayoutComponent = pageService.resolveComponentFromConfig('nestedLayout');
|
|
45
|
+
const nestedLayout = useMemo(() => jsx(NestedLayoutComponent, { children: children }), [NestedLayoutComponent, children]);
|
|
46
|
+
return nestedLayout;
|
|
47
|
+
};
|
|
48
|
+
const PageRenderComponent = () => {
|
|
49
|
+
const pageService = usePageService();
|
|
50
|
+
const { pageComponent } = pageService.getConfig();
|
|
25
51
|
let PageComponent = pageService.getComponent(pageComponent);
|
|
26
52
|
if (!PageComponent) {
|
|
27
53
|
PageComponent = () => {
|
|
28
54
|
throw new Error(`Page component '${pageComponent}' not found`);
|
|
29
55
|
};
|
|
30
56
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
57
|
+
const page = useMemo(() => jsx(PageComponent, {}), [PageComponent]);
|
|
58
|
+
return page;
|
|
59
|
+
};
|
|
60
|
+
const Root = () => {
|
|
61
|
+
const pageRenderComponent = useMemo(() => jsx(PageRenderComponent, {}), []);
|
|
62
|
+
const nestedLayoutRenderComponent = useMemo(() => jsx(NestedLayoutRenderComponent, { children: pageRenderComponent }), [pageRenderComponent]);
|
|
63
|
+
return jsx(LayoutRenderComponent, { children: nestedLayoutRenderComponent });
|
|
36
64
|
};
|
|
37
65
|
|
|
38
66
|
function serializeError(error) {
|
|
@@ -52,7 +80,8 @@ const initialState = null;
|
|
|
52
80
|
const PageErrorStore = createReducer('pageError', initialState).on(setPageErrorEvent, (state, error) => error && serializeError(error));
|
|
53
81
|
|
|
54
82
|
const PageErrorBoundary = (props) => {
|
|
55
|
-
const { children
|
|
83
|
+
const { children } = props;
|
|
84
|
+
const pageService = useDi(PAGE_SERVICE_TOKEN);
|
|
56
85
|
const url = useUrl();
|
|
57
86
|
const serializedError = useStore(PageErrorStore);
|
|
58
87
|
const error = useMemo(() => {
|
|
@@ -64,9 +93,9 @@ const PageErrorBoundary = (props) => {
|
|
|
64
93
|
return (jsx(UniversalErrorBoundary, { url: url, error: error, errorHandlers: errorHandlers, fallback: fallback, fallbackFromDi: fallbackFromDi, children: children }));
|
|
65
94
|
};
|
|
66
95
|
|
|
67
|
-
function renderReact({
|
|
96
|
+
function renderReact({ di }, context) {
|
|
68
97
|
const serverState = typeof window !== 'undefined' ? context.getState() : undefined;
|
|
69
|
-
return (jsx(Provider, { context: context, serverState: serverState, children: jsx(DIContext.Provider, { value: di, children: jsx(PageErrorBoundary, {
|
|
98
|
+
return (jsx(Provider, { context: context, serverState: serverState, children: jsx(DIContext.Provider, { value: di, children: jsx(PageErrorBoundary, { children: jsx(Root, {}) }) }) }));
|
|
70
99
|
}
|
|
71
100
|
|
|
72
101
|
let hydrateRoot;
|
|
@@ -98,10 +127,10 @@ const renderer = ({ element, container, callback, log }) => {
|
|
|
98
127
|
return hydrate(element, container, callback);
|
|
99
128
|
};
|
|
100
129
|
|
|
101
|
-
function rendering({
|
|
130
|
+
function rendering({ logger, consumerContext, customRender, extendRender, di, useStrictMode, rendererCallback, }) {
|
|
102
131
|
const log = logger('module-render');
|
|
103
132
|
return new Promise((resolve, reject) => {
|
|
104
|
-
let renderResult = renderReact({
|
|
133
|
+
let renderResult = renderReact({ di }, consumerContext);
|
|
105
134
|
if (extendRender) {
|
|
106
135
|
each((render) => {
|
|
107
136
|
renderResult = render(renderResult);
|
|
@@ -140,6 +169,7 @@ function rendering({ pageService, logger, consumerContext, customRender, extendR
|
|
|
140
169
|
});
|
|
141
170
|
}
|
|
142
171
|
|
|
172
|
+
const RenderChildrenComponent = ({ children }) => children;
|
|
143
173
|
let LayoutModule = class LayoutModule {
|
|
144
174
|
};
|
|
145
175
|
LayoutModule = __decorate([
|
|
@@ -158,7 +188,10 @@ LayoutModule = __decorate([
|
|
|
158
188
|
{
|
|
159
189
|
provide: 'componentDefaultList',
|
|
160
190
|
multi: true,
|
|
161
|
-
useFactory: (components) =>
|
|
191
|
+
useFactory: (components) => ({
|
|
192
|
+
...components,
|
|
193
|
+
nestedLayoutDefault: RenderChildrenComponent,
|
|
194
|
+
}),
|
|
162
195
|
deps: {
|
|
163
196
|
layoutDefault: DEFAULT_LAYOUT_COMPONENT,
|
|
164
197
|
footerDefault: { token: DEFAULT_FOOTER_COMPONENT, optional: true },
|
|
@@ -247,7 +280,6 @@ RenderModule = RenderModule_1 = __decorate([
|
|
|
247
280
|
};
|
|
248
281
|
},
|
|
249
282
|
deps: {
|
|
250
|
-
pageService: PAGE_SERVICE_TOKEN,
|
|
251
283
|
logger: LOGGER_TOKEN,
|
|
252
284
|
customRender: { token: CUSTOM_RENDER, optional: true },
|
|
253
285
|
extendRender: { token: EXTEND_RENDER, optional: true },
|
package/lib/client/index.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { EXTEND_RENDER, RENDERER_CALLBACK, USE_REACT_STRICT_MODE } from '@tramvai/tokens-render';
|
|
2
|
-
import type { PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
|
|
3
2
|
import type { ExtractDependencyType } from '@tinkoff/dippy';
|
|
4
|
-
export declare function rendering({
|
|
5
|
-
pageService: ExtractDependencyType<typeof PAGE_SERVICE_TOKEN>;
|
|
3
|
+
export declare function rendering({ logger, consumerContext, customRender, extendRender, di, useStrictMode, rendererCallback, }: {
|
|
6
4
|
logger: any;
|
|
7
5
|
consumerContext: any;
|
|
8
6
|
extendRender?: ExtractDependencyType<typeof EXTEND_RENDER>;
|
package/lib/client/types.d.ts
CHANGED
package/lib/react/index.d.ts
CHANGED
|
@@ -1,5 +1,2 @@
|
|
|
1
1
|
import type { PropsWithChildren } from 'react';
|
|
2
|
-
|
|
3
|
-
export declare const PageErrorBoundary: (props: PropsWithChildren<{
|
|
4
|
-
pageService: typeof PAGE_SERVICE_TOKEN;
|
|
5
|
-
}>) => JSX.Element;
|
|
2
|
+
export declare const PageErrorBoundary: (props: PropsWithChildren) => JSX.Element;
|
package/lib/react/root.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { ResourcesInlinerType } from './resourcesInliner';
|
|
|
6
6
|
* в итоговую html-страницу в виде ссылки на файл или заинлайнеными полностью
|
|
7
7
|
*/
|
|
8
8
|
export declare const RESOURCE_INLINER: import("@tinkoff/dippy").BaseTokenInterface<ResourcesInlinerType>;
|
|
9
|
-
export
|
|
9
|
+
export type ResourcesRegistryCache = {
|
|
10
10
|
filesCache: Cache;
|
|
11
11
|
sizeCache: Cache;
|
|
12
12
|
disabledUrlsCache: Cache;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RESOURCES_REGISTRY } from '@tramvai/tokens-render';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
type ResourceRegistry = typeof RESOURCES_REGISTRY;
|
|
3
|
+
type PageResource = ReturnType<ResourceRegistry['getPageResources']>[0];
|
|
4
4
|
export declare class ResourcesRegistry implements ResourceRegistry {
|
|
5
5
|
private resources;
|
|
6
6
|
private resourceInliner;
|
|
@@ -2,18 +2,15 @@ import type { ExtractDependencyType } from '@tinkoff/dippy';
|
|
|
2
2
|
import type { DI_TOKEN } from '@tramvai/core';
|
|
3
3
|
import type { CONTEXT_TOKEN, LOGGER_TOKEN } from '@tramvai/module-common';
|
|
4
4
|
import type { EXTEND_RENDER, CUSTOM_RENDER, REACT_SERVER_RENDER_MODE } from '@tramvai/tokens-render';
|
|
5
|
-
import type { PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
|
|
6
5
|
import type { ChunkExtractor } from '@loadable/server';
|
|
7
6
|
export declare class ReactRenderServer {
|
|
8
7
|
customRender: typeof CUSTOM_RENDER;
|
|
9
8
|
extendRender: ExtractDependencyType<typeof EXTEND_RENDER>;
|
|
10
9
|
context: typeof CONTEXT_TOKEN;
|
|
11
|
-
pageService: typeof PAGE_SERVICE_TOKEN;
|
|
12
10
|
di: typeof DI_TOKEN;
|
|
13
11
|
log: ReturnType<typeof LOGGER_TOKEN>;
|
|
14
12
|
renderMode: typeof REACT_SERVER_RENDER_MODE;
|
|
15
|
-
constructor({
|
|
16
|
-
pageService: any;
|
|
13
|
+
constructor({ context, customRender, extendRender, di, renderMode, logger }: {
|
|
17
14
|
context: any;
|
|
18
15
|
customRender: any;
|
|
19
16
|
extendRender: any;
|
package/lib/server.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __decorate } from 'tslib';
|
|
2
|
-
import {
|
|
2
|
+
import { useMemo, createElement } from 'react';
|
|
3
3
|
import { renderToString } from 'react-dom/server';
|
|
4
4
|
import { Module, provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core';
|
|
5
5
|
import { COMBINE_REDUCERS, CREATE_CACHE_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, RESPONSE_MANAGER_TOKEN, CONTEXT_TOKEN } from '@tramvai/tokens-common';
|
|
@@ -32,7 +32,7 @@ import { o as onload } from './server_inline.inline.es.js';
|
|
|
32
32
|
import { Writable } from 'stream';
|
|
33
33
|
import { jsx } from 'react/jsx-runtime';
|
|
34
34
|
import { createEvent, createReducer, useStore, Provider } from '@tramvai/state';
|
|
35
|
-
import {
|
|
35
|
+
import { usePageService, useUrl } from '@tramvai/module-router';
|
|
36
36
|
import { composeLayoutOptions, createLayout } from '@tinkoff/layout-factory';
|
|
37
37
|
|
|
38
38
|
const thirtySeconds = 1000 * 30;
|
|
@@ -647,26 +647,54 @@ const htmlPageSchemaFactory = ({ htmlAttrs, }) => {
|
|
|
647
647
|
];
|
|
648
648
|
};
|
|
649
649
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
650
|
+
/**
|
|
651
|
+
* Result component structure:
|
|
652
|
+
*
|
|
653
|
+
* <Root>
|
|
654
|
+
* <RootComponent>
|
|
655
|
+
* <LayoutComponent>
|
|
656
|
+
* <NestedLayoutComponent>
|
|
657
|
+
* <PageComponent />
|
|
658
|
+
* </NestedLayoutComponent>
|
|
659
|
+
* </LayoutComponent>
|
|
660
|
+
* </RootComponent>
|
|
661
|
+
* </Root>
|
|
662
|
+
*
|
|
663
|
+
* All components separated for a few reasons:
|
|
664
|
+
* - Page subtree can be rendered independently when Layout and Nested Layout the same
|
|
665
|
+
* - Nested Layout can be rerendered only on its changes
|
|
666
|
+
* - Layout can be rendered only on its changes
|
|
667
|
+
*/
|
|
668
|
+
const LayoutRenderComponent = ({ children }) => {
|
|
669
|
+
const pageService = usePageService();
|
|
670
|
+
const LayoutComponent = pageService.resolveComponentFromConfig('layout');
|
|
671
|
+
const HeaderComponent = pageService.resolveComponentFromConfig('header');
|
|
672
|
+
const FooterComponent = pageService.resolveComponentFromConfig('footer');
|
|
673
|
+
const layout = useMemo(() => (jsx(LayoutComponent, { Header: HeaderComponent, Footer: FooterComponent, children: children })), [LayoutComponent, HeaderComponent, FooterComponent, children]);
|
|
674
|
+
return layout;
|
|
675
|
+
};
|
|
676
|
+
const NestedLayoutRenderComponent = ({ children }) => {
|
|
677
|
+
const pageService = usePageService();
|
|
678
|
+
const NestedLayoutComponent = pageService.resolveComponentFromConfig('nestedLayout');
|
|
679
|
+
const nestedLayout = useMemo(() => jsx(NestedLayoutComponent, { children: children }), [NestedLayoutComponent, children]);
|
|
680
|
+
return nestedLayout;
|
|
681
|
+
};
|
|
682
|
+
const PageRenderComponent = () => {
|
|
683
|
+
const pageService = usePageService();
|
|
684
|
+
const { pageComponent } = pageService.getConfig();
|
|
659
685
|
let PageComponent = pageService.getComponent(pageComponent);
|
|
660
686
|
if (!PageComponent) {
|
|
661
687
|
PageComponent = () => {
|
|
662
688
|
throw new Error(`Page component '${pageComponent}' not found`);
|
|
663
689
|
};
|
|
664
690
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
691
|
+
const page = useMemo(() => jsx(PageComponent, {}), [PageComponent]);
|
|
692
|
+
return page;
|
|
693
|
+
};
|
|
694
|
+
const Root = () => {
|
|
695
|
+
const pageRenderComponent = useMemo(() => jsx(PageRenderComponent, {}), []);
|
|
696
|
+
const nestedLayoutRenderComponent = useMemo(() => jsx(NestedLayoutRenderComponent, { children: pageRenderComponent }), [pageRenderComponent]);
|
|
697
|
+
return jsx(LayoutRenderComponent, { children: nestedLayoutRenderComponent });
|
|
670
698
|
};
|
|
671
699
|
|
|
672
700
|
function serializeError(error) {
|
|
@@ -686,7 +714,8 @@ const initialState = null;
|
|
|
686
714
|
const PageErrorStore = createReducer('pageError', initialState).on(setPageErrorEvent, (state, error) => error && serializeError(error));
|
|
687
715
|
|
|
688
716
|
const PageErrorBoundary = (props) => {
|
|
689
|
-
const { children
|
|
717
|
+
const { children } = props;
|
|
718
|
+
const pageService = useDi(PAGE_SERVICE_TOKEN);
|
|
690
719
|
const url = useUrl();
|
|
691
720
|
const serializedError = useStore(PageErrorStore);
|
|
692
721
|
const error = useMemo(() => {
|
|
@@ -698,9 +727,9 @@ const PageErrorBoundary = (props) => {
|
|
|
698
727
|
return (jsx(UniversalErrorBoundary, { url: url, error: error, errorHandlers: errorHandlers, fallback: fallback, fallbackFromDi: fallbackFromDi, children: children }));
|
|
699
728
|
};
|
|
700
729
|
|
|
701
|
-
function renderReact({
|
|
730
|
+
function renderReact({ di }, context) {
|
|
702
731
|
const serverState = typeof window !== 'undefined' ? context.getState() : undefined;
|
|
703
|
-
return (jsx(Provider, { context: context, serverState: serverState, children: jsx(DIContext.Provider, { value: di, children: jsx(PageErrorBoundary, {
|
|
732
|
+
return (jsx(Provider, { context: context, serverState: serverState, children: jsx(DIContext.Provider, { value: di, children: jsx(PageErrorBoundary, { children: jsx(Root, {}) }) }) }));
|
|
704
733
|
}
|
|
705
734
|
|
|
706
735
|
const RENDER_TIMEOUT = 500;
|
|
@@ -724,8 +753,7 @@ class HtmlWritable extends Writable {
|
|
|
724
753
|
}
|
|
725
754
|
class ReactRenderServer {
|
|
726
755
|
// eslint-disable-next-line sort-class-members/sort-class-members
|
|
727
|
-
constructor({
|
|
728
|
-
this.pageService = pageService;
|
|
756
|
+
constructor({ context, customRender, extendRender, di, renderMode, logger }) {
|
|
729
757
|
this.context = context;
|
|
730
758
|
this.customRender = customRender;
|
|
731
759
|
this.extendRender = extendRender;
|
|
@@ -735,7 +763,7 @@ class ReactRenderServer {
|
|
|
735
763
|
}
|
|
736
764
|
render(extractor) {
|
|
737
765
|
var _a;
|
|
738
|
-
let renderResult = renderReact({
|
|
766
|
+
let renderResult = renderReact({ di: this.di }, this.context);
|
|
739
767
|
each((render) => {
|
|
740
768
|
renderResult = render(renderResult);
|
|
741
769
|
}, (_a = this.extendRender) !== null && _a !== void 0 ? _a : []);
|
|
@@ -787,6 +815,7 @@ class ReactRenderServer {
|
|
|
787
815
|
}
|
|
788
816
|
}
|
|
789
817
|
|
|
818
|
+
const RenderChildrenComponent = ({ children }) => children;
|
|
790
819
|
let LayoutModule = class LayoutModule {
|
|
791
820
|
};
|
|
792
821
|
LayoutModule = __decorate([
|
|
@@ -805,7 +834,10 @@ LayoutModule = __decorate([
|
|
|
805
834
|
{
|
|
806
835
|
provide: 'componentDefaultList',
|
|
807
836
|
multi: true,
|
|
808
|
-
useFactory: (components) =>
|
|
837
|
+
useFactory: (components) => ({
|
|
838
|
+
...components,
|
|
839
|
+
nestedLayoutDefault: RenderChildrenComponent,
|
|
840
|
+
}),
|
|
809
841
|
deps: {
|
|
810
842
|
layoutDefault: DEFAULT_LAYOUT_COMPONENT,
|
|
811
843
|
footerDefault: { token: DEFAULT_FOOTER_COMPONENT, optional: true },
|
|
@@ -965,7 +997,6 @@ RenderModule = RenderModule_1 = __decorate([
|
|
|
965
997
|
useClass: ReactRenderServer,
|
|
966
998
|
deps: {
|
|
967
999
|
context: CONTEXT_TOKEN,
|
|
968
|
-
pageService: PAGE_SERVICE_TOKEN,
|
|
969
1000
|
customRender: { token: CUSTOM_RENDER, optional: true },
|
|
970
1001
|
extendRender: { token: EXTEND_RENDER, optional: true },
|
|
971
1002
|
di: DI_TOKEN,
|
package/lib/server.js
CHANGED
|
@@ -683,26 +683,54 @@ const htmlPageSchemaFactory = ({ htmlAttrs, }) => {
|
|
|
683
683
|
];
|
|
684
684
|
};
|
|
685
685
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
686
|
+
/**
|
|
687
|
+
* Result component structure:
|
|
688
|
+
*
|
|
689
|
+
* <Root>
|
|
690
|
+
* <RootComponent>
|
|
691
|
+
* <LayoutComponent>
|
|
692
|
+
* <NestedLayoutComponent>
|
|
693
|
+
* <PageComponent />
|
|
694
|
+
* </NestedLayoutComponent>
|
|
695
|
+
* </LayoutComponent>
|
|
696
|
+
* </RootComponent>
|
|
697
|
+
* </Root>
|
|
698
|
+
*
|
|
699
|
+
* All components separated for a few reasons:
|
|
700
|
+
* - Page subtree can be rendered independently when Layout and Nested Layout the same
|
|
701
|
+
* - Nested Layout can be rerendered only on its changes
|
|
702
|
+
* - Layout can be rendered only on its changes
|
|
703
|
+
*/
|
|
704
|
+
const LayoutRenderComponent = ({ children }) => {
|
|
705
|
+
const pageService = moduleRouter.usePageService();
|
|
706
|
+
const LayoutComponent = pageService.resolveComponentFromConfig('layout');
|
|
707
|
+
const HeaderComponent = pageService.resolveComponentFromConfig('header');
|
|
708
|
+
const FooterComponent = pageService.resolveComponentFromConfig('footer');
|
|
709
|
+
const layout = react.useMemo(() => (jsxRuntime.jsx(LayoutComponent, { Header: HeaderComponent, Footer: FooterComponent, children: children })), [LayoutComponent, HeaderComponent, FooterComponent, children]);
|
|
710
|
+
return layout;
|
|
711
|
+
};
|
|
712
|
+
const NestedLayoutRenderComponent = ({ children }) => {
|
|
713
|
+
const pageService = moduleRouter.usePageService();
|
|
714
|
+
const NestedLayoutComponent = pageService.resolveComponentFromConfig('nestedLayout');
|
|
715
|
+
const nestedLayout = react.useMemo(() => jsxRuntime.jsx(NestedLayoutComponent, { children: children }), [NestedLayoutComponent, children]);
|
|
716
|
+
return nestedLayout;
|
|
717
|
+
};
|
|
718
|
+
const PageRenderComponent = () => {
|
|
719
|
+
const pageService = moduleRouter.usePageService();
|
|
720
|
+
const { pageComponent } = pageService.getConfig();
|
|
695
721
|
let PageComponent = pageService.getComponent(pageComponent);
|
|
696
722
|
if (!PageComponent) {
|
|
697
723
|
PageComponent = () => {
|
|
698
724
|
throw new Error(`Page component '${pageComponent}' not found`);
|
|
699
725
|
};
|
|
700
726
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
727
|
+
const page = react.useMemo(() => jsxRuntime.jsx(PageComponent, {}), [PageComponent]);
|
|
728
|
+
return page;
|
|
729
|
+
};
|
|
730
|
+
const Root = () => {
|
|
731
|
+
const pageRenderComponent = react.useMemo(() => jsxRuntime.jsx(PageRenderComponent, {}), []);
|
|
732
|
+
const nestedLayoutRenderComponent = react.useMemo(() => jsxRuntime.jsx(NestedLayoutRenderComponent, { children: pageRenderComponent }), [pageRenderComponent]);
|
|
733
|
+
return jsxRuntime.jsx(LayoutRenderComponent, { children: nestedLayoutRenderComponent });
|
|
706
734
|
};
|
|
707
735
|
|
|
708
736
|
function serializeError(error) {
|
|
@@ -722,7 +750,8 @@ const initialState = null;
|
|
|
722
750
|
const PageErrorStore = state.createReducer('pageError', initialState).on(setPageErrorEvent, (state, error) => error && serializeError(error));
|
|
723
751
|
|
|
724
752
|
const PageErrorBoundary = (props) => {
|
|
725
|
-
const { children
|
|
753
|
+
const { children } = props;
|
|
754
|
+
const pageService = react$1.useDi(tokensRouter.PAGE_SERVICE_TOKEN);
|
|
726
755
|
const url = moduleRouter.useUrl();
|
|
727
756
|
const serializedError = state.useStore(PageErrorStore);
|
|
728
757
|
const error = react.useMemo(() => {
|
|
@@ -734,9 +763,9 @@ const PageErrorBoundary = (props) => {
|
|
|
734
763
|
return (jsxRuntime.jsx(react$1.UniversalErrorBoundary, { url: url, error: error, errorHandlers: errorHandlers, fallback: fallback, fallbackFromDi: fallbackFromDi, children: children }));
|
|
735
764
|
};
|
|
736
765
|
|
|
737
|
-
function renderReact({
|
|
766
|
+
function renderReact({ di }, context) {
|
|
738
767
|
const serverState = typeof window !== 'undefined' ? context.getState() : undefined;
|
|
739
|
-
return (jsxRuntime.jsx(state.Provider, { context: context, serverState: serverState, children: jsxRuntime.jsx(react$1.DIContext.Provider, { value: di, children: jsxRuntime.jsx(PageErrorBoundary, {
|
|
768
|
+
return (jsxRuntime.jsx(state.Provider, { context: context, serverState: serverState, children: jsxRuntime.jsx(react$1.DIContext.Provider, { value: di, children: jsxRuntime.jsx(PageErrorBoundary, { children: jsxRuntime.jsx(Root, {}) }) }) }));
|
|
740
769
|
}
|
|
741
770
|
|
|
742
771
|
const RENDER_TIMEOUT = 500;
|
|
@@ -760,8 +789,7 @@ class HtmlWritable extends stream.Writable {
|
|
|
760
789
|
}
|
|
761
790
|
class ReactRenderServer {
|
|
762
791
|
// eslint-disable-next-line sort-class-members/sort-class-members
|
|
763
|
-
constructor({
|
|
764
|
-
this.pageService = pageService;
|
|
792
|
+
constructor({ context, customRender, extendRender, di, renderMode, logger }) {
|
|
765
793
|
this.context = context;
|
|
766
794
|
this.customRender = customRender;
|
|
767
795
|
this.extendRender = extendRender;
|
|
@@ -771,7 +799,7 @@ class ReactRenderServer {
|
|
|
771
799
|
}
|
|
772
800
|
render(extractor) {
|
|
773
801
|
var _a;
|
|
774
|
-
let renderResult = renderReact({
|
|
802
|
+
let renderResult = renderReact({ di: this.di }, this.context);
|
|
775
803
|
each__default["default"]((render) => {
|
|
776
804
|
renderResult = render(renderResult);
|
|
777
805
|
}, (_a = this.extendRender) !== null && _a !== void 0 ? _a : []);
|
|
@@ -823,6 +851,7 @@ class ReactRenderServer {
|
|
|
823
851
|
}
|
|
824
852
|
}
|
|
825
853
|
|
|
854
|
+
const RenderChildrenComponent = ({ children }) => children;
|
|
826
855
|
let LayoutModule = class LayoutModule {
|
|
827
856
|
};
|
|
828
857
|
LayoutModule = tslib.__decorate([
|
|
@@ -841,7 +870,10 @@ LayoutModule = tslib.__decorate([
|
|
|
841
870
|
{
|
|
842
871
|
provide: 'componentDefaultList',
|
|
843
872
|
multi: true,
|
|
844
|
-
useFactory: (components) =>
|
|
873
|
+
useFactory: (components) => ({
|
|
874
|
+
...components,
|
|
875
|
+
nestedLayoutDefault: RenderChildrenComponent,
|
|
876
|
+
}),
|
|
845
877
|
deps: {
|
|
846
878
|
layoutDefault: tokensRender.DEFAULT_LAYOUT_COMPONENT,
|
|
847
879
|
footerDefault: { token: tokensRender.DEFAULT_FOOTER_COMPONENT, optional: true },
|
|
@@ -1001,7 +1033,6 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
|
|
|
1001
1033
|
useClass: ReactRenderServer,
|
|
1002
1034
|
deps: {
|
|
1003
1035
|
context: tokensCommon.CONTEXT_TOKEN,
|
|
1004
|
-
pageService: tokensRouter.PAGE_SERVICE_TOKEN,
|
|
1005
1036
|
customRender: { token: tokensRender.CUSTOM_RENDER, optional: true },
|
|
1006
1037
|
extendRender: { token: tokensRender.EXTEND_RENDER, optional: true },
|
|
1007
1038
|
di: core.DI_TOKEN,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type AnyError = Error & {
|
|
2
2
|
[key: string]: any;
|
|
3
3
|
};
|
|
4
4
|
export interface SerializedError {
|
|
@@ -8,7 +8,7 @@ export interface SerializedError {
|
|
|
8
8
|
}
|
|
9
9
|
export declare function serializeError(error: AnyError): SerializedError;
|
|
10
10
|
export declare function deserializeError(serializedError: SerializedError): AnyError;
|
|
11
|
-
export
|
|
11
|
+
export type IPageErrorStore = SerializedError | null;
|
|
12
12
|
export declare const setPageErrorEvent: import("@tramvai/types-actions-state-context").EventCreator1<AnyError, AnyError>;
|
|
13
13
|
export declare const PageErrorStore: import("@tramvai/state").Reducer<SerializedError, "pageError">;
|
|
14
14
|
export {};
|
package/lib/shared/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getDiWrapper } from '@tramvai/test-helpers';
|
|
2
|
-
|
|
2
|
+
type Options = Parameters<typeof getDiWrapper>[0];
|
|
3
3
|
export declare const testPageResources: (options: Options) => {
|
|
4
4
|
render: () => {
|
|
5
5
|
parsed: import("node-html-parser").HTMLElement;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-render",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.44.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"browser": "lib/browser.js",
|
|
6
6
|
"main": "lib/server.js",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"@tinkoff/htmlpagebuilder": "0.5.5",
|
|
27
27
|
"@tinkoff/layout-factory": "0.3.4",
|
|
28
28
|
"@tinkoff/url": "0.8.4",
|
|
29
|
-
"@tinkoff/user-agent": "0.4.
|
|
30
|
-
"@tramvai/module-client-hints": "2.
|
|
31
|
-
"@tramvai/module-router": "2.
|
|
32
|
-
"@tramvai/react": "2.
|
|
29
|
+
"@tinkoff/user-agent": "0.4.99",
|
|
30
|
+
"@tramvai/module-client-hints": "2.44.2",
|
|
31
|
+
"@tramvai/module-router": "2.44.2",
|
|
32
|
+
"@tramvai/react": "2.44.2",
|
|
33
33
|
"@tramvai/safe-strings": "0.5.5",
|
|
34
|
-
"@tramvai/tokens-render": "2.
|
|
35
|
-
"@tramvai/experiments": "2.
|
|
34
|
+
"@tramvai/tokens-render": "2.44.2",
|
|
35
|
+
"@tramvai/experiments": "2.44.2",
|
|
36
36
|
"@types/loadable__server": "^5.12.6",
|
|
37
37
|
"node-fetch": "^2.6.1"
|
|
38
38
|
},
|
|
@@ -40,14 +40,14 @@
|
|
|
40
40
|
"@tinkoff/dippy": "0.8.9",
|
|
41
41
|
"@tinkoff/utils": "^2.1.2",
|
|
42
42
|
"@tinkoff/react-hooks": "0.1.4",
|
|
43
|
-
"@tramvai/cli": "2.
|
|
44
|
-
"@tramvai/core": "2.
|
|
45
|
-
"@tramvai/module-common": "2.
|
|
46
|
-
"@tramvai/state": "2.
|
|
47
|
-
"@tramvai/test-helpers": "2.
|
|
48
|
-
"@tramvai/tokens-common": "2.
|
|
49
|
-
"@tramvai/tokens-router": "2.
|
|
50
|
-
"@tramvai/tokens-server-private": "2.
|
|
43
|
+
"@tramvai/cli": "2.44.2",
|
|
44
|
+
"@tramvai/core": "2.44.2",
|
|
45
|
+
"@tramvai/module-common": "2.44.2",
|
|
46
|
+
"@tramvai/state": "2.44.2",
|
|
47
|
+
"@tramvai/test-helpers": "2.44.2",
|
|
48
|
+
"@tramvai/tokens-common": "2.44.2",
|
|
49
|
+
"@tramvai/tokens-router": "2.44.2",
|
|
50
|
+
"@tramvai/tokens-server-private": "2.44.2",
|
|
51
51
|
"express": "^4.17.1",
|
|
52
52
|
"prop-types": "^15.6.2",
|
|
53
53
|
"react": ">=16.14.0",
|