@tramvai/module-render 5.49.1 → 5.53.74
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/lib/browser.js +1 -12
- package/lib/resourcesInliner/resourcesInliner.es.js +71 -67
- package/lib/resourcesInliner/resourcesInliner.js +71 -67
- package/lib/resourcesRegistry/index.es.js +2 -1
- package/lib/resourcesRegistry/index.js +2 -1
- package/lib/server/PageBuilder.d.ts +3 -3
- package/lib/server/PageBuilder.es.js +21 -6
- package/lib/server/PageBuilder.js +21 -6
- package/lib/server/ReactRenderServer.es.js +15 -1
- package/lib/server/ReactRenderServer.js +15 -1
- package/lib/server/blocks/bundleResource/bundleResource.d.ts +2 -2
- package/lib/server/blocks/bundleResource/bundleResource.es.js +42 -7
- package/lib/server/blocks/bundleResource/bundleResource.js +42 -7
- package/lib/server/blocks/polyfill.d.ts +1 -2
- package/lib/server/blocks/polyfill.es.js +18 -6
- package/lib/server/blocks/polyfill.js +18 -6
- package/lib/server/blocks/utils/fetchWebpackRuntime.d.ts +2 -0
- package/lib/server/blocks/utils/fetchWebpackRuntime.es.js +16 -0
- package/lib/server/blocks/utils/fetchWebpackRuntime.js +24 -0
- package/lib/server/blocks/utils/fetchWebpackStats.es.js +13 -17
- package/lib/server/blocks/utils/fetchWebpackStats.js +13 -17
- package/lib/server/blocks/utils/flushFiles.d.ts +2 -1
- package/lib/server/blocks/utils/flushFiles.es.js +5 -2
- package/lib/server/blocks/utils/flushFiles.js +5 -2
- package/lib/server/constants/slots.d.ts +1 -0
- package/lib/server/htmlPageSchema.es.js +2 -1
- package/lib/server/htmlPageSchema.js +2 -1
- package/lib/server.es.js +7 -37
- package/lib/server.js +5 -35
- package/package.json +22 -22
- package/tests.js +3 -1
|
@@ -4,6 +4,7 @@ import { ResourceType, ResourceSlot } from '@tramvai/tokens-render';
|
|
|
4
4
|
import { isFileSystemPageComponent, fileSystemPageToWebpackChunkName } from '@tramvai/experiments';
|
|
5
5
|
import { PRELOAD_JS } from '../../constants/performance.es.js';
|
|
6
6
|
import { flushFiles } from '../utils/flushFiles.es.js';
|
|
7
|
+
import { fetchWebpackRuntime } from '../utils/fetchWebpackRuntime.es.js';
|
|
7
8
|
|
|
8
9
|
const asyncScriptAttrs = {
|
|
9
10
|
defer: null,
|
|
@@ -20,13 +21,13 @@ try {
|
|
|
20
21
|
catch (e) {
|
|
21
22
|
// do nothing
|
|
22
23
|
}
|
|
23
|
-
const bundleResource = async ({ bundle,
|
|
24
|
+
const bundleResource = async ({ bundle, extractor, pageComponent, fetchWebpackStats, inlineWebpackRuntime, renderMode, assetsPrefixFactory, }) => {
|
|
24
25
|
// for file-system pages preload page chunk against bundle chunk
|
|
25
26
|
const chunkNameFromBundle = isFileSystemPageComponent(pageComponent)
|
|
26
27
|
? fileSystemPageToWebpackChunkName(pageComponent)
|
|
27
28
|
: last(bundle.split('/'));
|
|
28
|
-
const webpackStats = await fetchWebpackStats(
|
|
29
|
-
const { publicPath, assetsByChunkName } = webpackStats;
|
|
29
|
+
const webpackStats = await fetchWebpackStats();
|
|
30
|
+
const { publicPath, assetsByChunkName, integrities = {} } = webpackStats;
|
|
30
31
|
const bundles = has('common-chunk', assetsByChunkName)
|
|
31
32
|
? ['common-chunk', chunkNameFromBundle]
|
|
32
33
|
: [chunkNameFromBundle];
|
|
@@ -34,7 +35,9 @@ const bundleResource = async ({ bundle, modern, extractor, pageComponent, fetchW
|
|
|
34
35
|
const { scripts: baseScripts } = flushFiles(['vendor'], webpackStats, {
|
|
35
36
|
ignoreDependencies: true,
|
|
36
37
|
});
|
|
37
|
-
const { scripts, styles } = flushFiles([...bundles, ...lazyChunks, ...criticalChunks, 'platform'], webpackStats
|
|
38
|
+
const { scripts, styles } = flushFiles([...bundles, ...lazyChunks, ...criticalChunks, 'platform'], webpackStats, {
|
|
39
|
+
exclude: ['runtime'],
|
|
40
|
+
});
|
|
38
41
|
const genHref = (href) => `${publicPath}${href}`;
|
|
39
42
|
const result = [];
|
|
40
43
|
const assetsPrefix = assetsPrefixFactory();
|
|
@@ -45,34 +48,66 @@ const bundleResource = async ({ bundle, modern, extractor, pageComponent, fetchW
|
|
|
45
48
|
payload: `window.ap = ${`"${assetsPrefix}"`};`,
|
|
46
49
|
});
|
|
47
50
|
}
|
|
51
|
+
const { scripts: webpackRuntimeScript } = flushFiles(['runtime'], webpackStats);
|
|
52
|
+
// If webpack runtime is presented is always single chunk
|
|
53
|
+
const webpackRuntimeScriptName = webpackRuntimeScript[0];
|
|
54
|
+
if (webpackRuntimeScriptName) {
|
|
55
|
+
if (inlineWebpackRuntime) {
|
|
56
|
+
const webpackRuntime = await fetchWebpackRuntime(genHref(webpackRuntimeScriptName));
|
|
57
|
+
result.push({
|
|
58
|
+
type: ResourceType.inlineScript,
|
|
59
|
+
slot: ResourceSlot.HEAD_WEBPACK_RUNTIME,
|
|
60
|
+
payload: webpackRuntime,
|
|
61
|
+
attrs: {
|
|
62
|
+
id: 'webpack-runtime',
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
result.push({
|
|
68
|
+
type: ResourceType.script,
|
|
69
|
+
slot: ResourceSlot.HEAD_WEBPACK_RUNTIME,
|
|
70
|
+
payload: genHref(webpackRuntimeScriptName),
|
|
71
|
+
attrs: {
|
|
72
|
+
'data-critical': 'true',
|
|
73
|
+
...(integrities[webpackRuntimeScriptName]
|
|
74
|
+
? { integrity: integrities[webpackRuntimeScriptName] }
|
|
75
|
+
: {}),
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
48
80
|
// defer scripts is not suitable for React streaming, we need to ability to run them as early as possible
|
|
49
81
|
// https://github.com/reactwg/react-18/discussions/114
|
|
50
82
|
const scriptTypeAttr = renderMode === 'streaming' ? asyncScriptAttrs : deferScriptAttrs;
|
|
51
|
-
styles.
|
|
83
|
+
styles.forEach((style) => result.push({
|
|
52
84
|
type: ResourceType.style,
|
|
53
85
|
slot: ResourceSlot.HEAD_CORE_STYLES,
|
|
54
86
|
payload: genHref(style),
|
|
55
87
|
attrs: {
|
|
88
|
+
...(integrities[style] ? { integrity: integrities[style] } : {}),
|
|
56
89
|
'data-critical': 'true',
|
|
57
90
|
// looks like we don't need this scripts preload at all, but also it is official recommendation for streaming
|
|
58
91
|
// https://github.com/reactwg/react-18/discussions/114
|
|
59
92
|
onload: renderMode === 'streaming' ? null : `${PRELOAD_JS}()`,
|
|
60
93
|
},
|
|
61
94
|
}));
|
|
62
|
-
baseScripts.
|
|
95
|
+
baseScripts.forEach((script) => result.push({
|
|
63
96
|
type: ResourceType.script,
|
|
64
97
|
slot: ResourceSlot.HEAD_CORE_SCRIPTS,
|
|
65
98
|
payload: genHref(script),
|
|
66
99
|
attrs: {
|
|
100
|
+
...(integrities[script] ? { integrity: integrities[script] } : {}),
|
|
67
101
|
'data-critical': 'true',
|
|
68
102
|
...scriptTypeAttr,
|
|
69
103
|
},
|
|
70
104
|
}));
|
|
71
|
-
scripts.
|
|
105
|
+
scripts.forEach((script) => result.push({
|
|
72
106
|
type: ResourceType.script,
|
|
73
107
|
slot: ResourceSlot.HEAD_CORE_SCRIPTS,
|
|
74
108
|
payload: genHref(script),
|
|
75
109
|
attrs: {
|
|
110
|
+
...(integrities[script] ? { integrity: integrities[script] } : {}),
|
|
76
111
|
'data-critical': 'true',
|
|
77
112
|
...scriptTypeAttr,
|
|
78
113
|
},
|
|
@@ -8,6 +8,7 @@ var tokensRender = require('@tramvai/tokens-render');
|
|
|
8
8
|
var experiments = require('@tramvai/experiments');
|
|
9
9
|
var performance = require('../../constants/performance.js');
|
|
10
10
|
var flushFiles = require('../utils/flushFiles.js');
|
|
11
|
+
var fetchWebpackRuntime = require('../utils/fetchWebpackRuntime.js');
|
|
11
12
|
|
|
12
13
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
13
14
|
|
|
@@ -29,13 +30,13 @@ try {
|
|
|
29
30
|
catch (e) {
|
|
30
31
|
// do nothing
|
|
31
32
|
}
|
|
32
|
-
const bundleResource = async ({ bundle,
|
|
33
|
+
const bundleResource = async ({ bundle, extractor, pageComponent, fetchWebpackStats, inlineWebpackRuntime, renderMode, assetsPrefixFactory, }) => {
|
|
33
34
|
// for file-system pages preload page chunk against bundle chunk
|
|
34
35
|
const chunkNameFromBundle = experiments.isFileSystemPageComponent(pageComponent)
|
|
35
36
|
? experiments.fileSystemPageToWebpackChunkName(pageComponent)
|
|
36
37
|
: last__default["default"](bundle.split('/'));
|
|
37
|
-
const webpackStats = await fetchWebpackStats(
|
|
38
|
-
const { publicPath, assetsByChunkName } = webpackStats;
|
|
38
|
+
const webpackStats = await fetchWebpackStats();
|
|
39
|
+
const { publicPath, assetsByChunkName, integrities = {} } = webpackStats;
|
|
39
40
|
const bundles = has__default["default"]('common-chunk', assetsByChunkName)
|
|
40
41
|
? ['common-chunk', chunkNameFromBundle]
|
|
41
42
|
: [chunkNameFromBundle];
|
|
@@ -43,7 +44,9 @@ const bundleResource = async ({ bundle, modern, extractor, pageComponent, fetchW
|
|
|
43
44
|
const { scripts: baseScripts } = flushFiles.flushFiles(['vendor'], webpackStats, {
|
|
44
45
|
ignoreDependencies: true,
|
|
45
46
|
});
|
|
46
|
-
const { scripts, styles } = flushFiles.flushFiles([...bundles, ...lazyChunks, ...criticalChunks, 'platform'], webpackStats
|
|
47
|
+
const { scripts, styles } = flushFiles.flushFiles([...bundles, ...lazyChunks, ...criticalChunks, 'platform'], webpackStats, {
|
|
48
|
+
exclude: ['runtime'],
|
|
49
|
+
});
|
|
47
50
|
const genHref = (href) => `${publicPath}${href}`;
|
|
48
51
|
const result = [];
|
|
49
52
|
const assetsPrefix = assetsPrefixFactory();
|
|
@@ -54,34 +57,66 @@ const bundleResource = async ({ bundle, modern, extractor, pageComponent, fetchW
|
|
|
54
57
|
payload: `window.ap = ${`"${assetsPrefix}"`};`,
|
|
55
58
|
});
|
|
56
59
|
}
|
|
60
|
+
const { scripts: webpackRuntimeScript } = flushFiles.flushFiles(['runtime'], webpackStats);
|
|
61
|
+
// If webpack runtime is presented is always single chunk
|
|
62
|
+
const webpackRuntimeScriptName = webpackRuntimeScript[0];
|
|
63
|
+
if (webpackRuntimeScriptName) {
|
|
64
|
+
if (inlineWebpackRuntime) {
|
|
65
|
+
const webpackRuntime = await fetchWebpackRuntime.fetchWebpackRuntime(genHref(webpackRuntimeScriptName));
|
|
66
|
+
result.push({
|
|
67
|
+
type: tokensRender.ResourceType.inlineScript,
|
|
68
|
+
slot: tokensRender.ResourceSlot.HEAD_WEBPACK_RUNTIME,
|
|
69
|
+
payload: webpackRuntime,
|
|
70
|
+
attrs: {
|
|
71
|
+
id: 'webpack-runtime',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
result.push({
|
|
77
|
+
type: tokensRender.ResourceType.script,
|
|
78
|
+
slot: tokensRender.ResourceSlot.HEAD_WEBPACK_RUNTIME,
|
|
79
|
+
payload: genHref(webpackRuntimeScriptName),
|
|
80
|
+
attrs: {
|
|
81
|
+
'data-critical': 'true',
|
|
82
|
+
...(integrities[webpackRuntimeScriptName]
|
|
83
|
+
? { integrity: integrities[webpackRuntimeScriptName] }
|
|
84
|
+
: {}),
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
57
89
|
// defer scripts is not suitable for React streaming, we need to ability to run them as early as possible
|
|
58
90
|
// https://github.com/reactwg/react-18/discussions/114
|
|
59
91
|
const scriptTypeAttr = renderMode === 'streaming' ? asyncScriptAttrs : deferScriptAttrs;
|
|
60
|
-
styles.
|
|
92
|
+
styles.forEach((style) => result.push({
|
|
61
93
|
type: tokensRender.ResourceType.style,
|
|
62
94
|
slot: tokensRender.ResourceSlot.HEAD_CORE_STYLES,
|
|
63
95
|
payload: genHref(style),
|
|
64
96
|
attrs: {
|
|
97
|
+
...(integrities[style] ? { integrity: integrities[style] } : {}),
|
|
65
98
|
'data-critical': 'true',
|
|
66
99
|
// looks like we don't need this scripts preload at all, but also it is official recommendation for streaming
|
|
67
100
|
// https://github.com/reactwg/react-18/discussions/114
|
|
68
101
|
onload: renderMode === 'streaming' ? null : `${performance.PRELOAD_JS}()`,
|
|
69
102
|
},
|
|
70
103
|
}));
|
|
71
|
-
baseScripts.
|
|
104
|
+
baseScripts.forEach((script) => result.push({
|
|
72
105
|
type: tokensRender.ResourceType.script,
|
|
73
106
|
slot: tokensRender.ResourceSlot.HEAD_CORE_SCRIPTS,
|
|
74
107
|
payload: genHref(script),
|
|
75
108
|
attrs: {
|
|
109
|
+
...(integrities[script] ? { integrity: integrities[script] } : {}),
|
|
76
110
|
'data-critical': 'true',
|
|
77
111
|
...scriptTypeAttr,
|
|
78
112
|
},
|
|
79
113
|
}));
|
|
80
|
-
scripts.
|
|
114
|
+
scripts.forEach((script) => result.push({
|
|
81
115
|
type: tokensRender.ResourceType.script,
|
|
82
116
|
slot: tokensRender.ResourceSlot.HEAD_CORE_SCRIPTS,
|
|
83
117
|
payload: genHref(script),
|
|
84
118
|
attrs: {
|
|
119
|
+
...(integrities[script] ? { integrity: integrities[script] } : {}),
|
|
85
120
|
'data-critical': 'true',
|
|
86
121
|
...scriptTypeAttr,
|
|
87
122
|
},
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { PageResource, FETCH_WEBPACK_STATS_TOKEN, REACT_SERVER_RENDER_MODE } from '@tramvai/tokens-render';
|
|
2
|
-
export declare const polyfillResources: ({ condition,
|
|
2
|
+
export declare const polyfillResources: ({ condition, fetchWebpackStats, renderMode, }: {
|
|
3
3
|
condition: string;
|
|
4
|
-
modern: boolean;
|
|
5
4
|
fetchWebpackStats: typeof FETCH_WEBPACK_STATS_TOKEN;
|
|
6
5
|
renderMode: typeof REACT_SERVER_RENDER_MODE;
|
|
7
6
|
}) => Promise<PageResource[]>;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { ResourceType, ResourceSlot } from '@tramvai/tokens-render';
|
|
2
2
|
import { flushFiles } from './utils/flushFiles.es.js';
|
|
3
3
|
|
|
4
|
-
const polyfillResources = async ({ condition,
|
|
5
|
-
const webpackStats = await fetchWebpackStats(
|
|
4
|
+
const polyfillResources = async ({ condition, fetchWebpackStats, renderMode, }) => {
|
|
5
|
+
const webpackStats = await fetchWebpackStats();
|
|
6
6
|
const { publicPath, polyfillCondition } = webpackStats;
|
|
7
|
-
// получает файл полифилла из stats.json
|
|
8
|
-
// В зависимости от версии браузера будет использован полифилл из legacy или modern сборки,
|
|
9
|
-
// т.к. полифиллы для них могут отличаться на основании преобразований `@babel/preset-env`
|
|
7
|
+
// получает файл полифилла из stats.json.
|
|
10
8
|
const { scripts: polyfillScripts } = flushFiles(['polyfill'], webpackStats, {
|
|
11
9
|
ignoreDependencies: true,
|
|
12
10
|
});
|
|
11
|
+
const { scripts: modernPolyfillScripts } = flushFiles(['modern.polyfill'], webpackStats, {
|
|
12
|
+
ignoreDependencies: true,
|
|
13
|
+
});
|
|
13
14
|
const genHref = (href) => `${publicPath}${href}`;
|
|
14
15
|
const result = [];
|
|
15
16
|
polyfillScripts.forEach((script) => {
|
|
@@ -30,10 +31,21 @@ const polyfillResources = async ({ condition, modern, fetchWebpackStats, renderM
|
|
|
30
31
|
} catch (e) {
|
|
31
32
|
con = true;
|
|
32
33
|
}
|
|
33
|
-
if (con) {
|
|
34
|
+
if (con) { window.TRAMVAI_POLLYFILL_LOADED = true;
|
|
35
|
+
document.write('<script${renderMode === 'streaming' ? '' : ' defer="defer"'} charset="utf-8" data-critical="true" crossorigin="anonymous" src="${href}"><\\/script>')}
|
|
34
36
|
})()`,
|
|
35
37
|
});
|
|
36
38
|
});
|
|
39
|
+
modernPolyfillScripts.forEach((script) => {
|
|
40
|
+
result.push({
|
|
41
|
+
type: ResourceType.script,
|
|
42
|
+
payload: genHref(script),
|
|
43
|
+
attrs: {
|
|
44
|
+
id: 'modern-polyfills',
|
|
45
|
+
},
|
|
46
|
+
slot: ResourceSlot.HEAD_POLYFILLS,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
37
49
|
return result;
|
|
38
50
|
};
|
|
39
51
|
|
|
@@ -5,15 +5,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var tokensRender = require('@tramvai/tokens-render');
|
|
6
6
|
var flushFiles = require('./utils/flushFiles.js');
|
|
7
7
|
|
|
8
|
-
const polyfillResources = async ({ condition,
|
|
9
|
-
const webpackStats = await fetchWebpackStats(
|
|
8
|
+
const polyfillResources = async ({ condition, fetchWebpackStats, renderMode, }) => {
|
|
9
|
+
const webpackStats = await fetchWebpackStats();
|
|
10
10
|
const { publicPath, polyfillCondition } = webpackStats;
|
|
11
|
-
// получает файл полифилла из stats.json
|
|
12
|
-
// В зависимости от версии браузера будет использован полифилл из legacy или modern сборки,
|
|
13
|
-
// т.к. полифиллы для них могут отличаться на основании преобразований `@babel/preset-env`
|
|
11
|
+
// получает файл полифилла из stats.json.
|
|
14
12
|
const { scripts: polyfillScripts } = flushFiles.flushFiles(['polyfill'], webpackStats, {
|
|
15
13
|
ignoreDependencies: true,
|
|
16
14
|
});
|
|
15
|
+
const { scripts: modernPolyfillScripts } = flushFiles.flushFiles(['modern.polyfill'], webpackStats, {
|
|
16
|
+
ignoreDependencies: true,
|
|
17
|
+
});
|
|
17
18
|
const genHref = (href) => `${publicPath}${href}`;
|
|
18
19
|
const result = [];
|
|
19
20
|
polyfillScripts.forEach((script) => {
|
|
@@ -34,10 +35,21 @@ const polyfillResources = async ({ condition, modern, fetchWebpackStats, renderM
|
|
|
34
35
|
} catch (e) {
|
|
35
36
|
con = true;
|
|
36
37
|
}
|
|
37
|
-
if (con) {
|
|
38
|
+
if (con) { window.TRAMVAI_POLLYFILL_LOADED = true;
|
|
39
|
+
document.write('<script${renderMode === 'streaming' ? '' : ' defer="defer"'} charset="utf-8" data-critical="true" crossorigin="anonymous" src="${href}"><\\/script>')}
|
|
38
40
|
})()`,
|
|
39
41
|
});
|
|
40
42
|
});
|
|
43
|
+
modernPolyfillScripts.forEach((script) => {
|
|
44
|
+
result.push({
|
|
45
|
+
type: tokensRender.ResourceType.script,
|
|
46
|
+
payload: genHref(script),
|
|
47
|
+
attrs: {
|
|
48
|
+
id: 'modern-polyfills',
|
|
49
|
+
},
|
|
50
|
+
slot: tokensRender.ResourceSlot.HEAD_POLYFILLS,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
41
53
|
return result;
|
|
42
54
|
};
|
|
43
55
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
|
|
3
|
+
let runtimeCode;
|
|
4
|
+
const fetchWebpackRuntime = async (webpackRuntimeURL) => {
|
|
5
|
+
if (runtimeCode) {
|
|
6
|
+
return runtimeCode;
|
|
7
|
+
}
|
|
8
|
+
const response = await fetch(webpackRuntimeURL);
|
|
9
|
+
if (!response.ok) {
|
|
10
|
+
throw new Error('Failed to request webpack runtime!');
|
|
11
|
+
}
|
|
12
|
+
runtimeCode = await response.text();
|
|
13
|
+
return runtimeCode;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export { fetchWebpackRuntime };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var fetch = require('node-fetch');
|
|
6
|
+
|
|
7
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
|
+
|
|
9
|
+
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
10
|
+
|
|
11
|
+
let runtimeCode;
|
|
12
|
+
const fetchWebpackRuntime = async (webpackRuntimeURL) => {
|
|
13
|
+
if (runtimeCode) {
|
|
14
|
+
return runtimeCode;
|
|
15
|
+
}
|
|
16
|
+
const response = await fetch__default["default"](webpackRuntimeURL);
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
throw new Error('Failed to request webpack runtime!');
|
|
19
|
+
}
|
|
20
|
+
runtimeCode = await response.text();
|
|
21
|
+
return runtimeCode;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
exports.fetchWebpackRuntime = fetchWebpackRuntime;
|
|
@@ -16,10 +16,10 @@ let fetchStats = async () => {
|
|
|
16
16
|
throw new Error(`Unknown environment`);
|
|
17
17
|
};
|
|
18
18
|
if (process.env.NODE_ENV === 'development') {
|
|
19
|
-
fetchStats = async (
|
|
20
|
-
const {
|
|
19
|
+
fetchStats = async (assetsPrefixFactory) => {
|
|
20
|
+
const { staticHost, staticPort, output, httpProtocol } = appConfig;
|
|
21
21
|
const getUrl = (filename) => `${httpProtocol}://${staticHost}:${staticPort}/${output.client}/${filename}`;
|
|
22
|
-
const request = await fetch(getUrl(
|
|
22
|
+
const request = await fetch(getUrl('stats.json'));
|
|
23
23
|
const stats = await request.json();
|
|
24
24
|
const assetsPrefix = assetsPrefixFactory();
|
|
25
25
|
// static - популярная заглушка в env.development.js файлах, надо игнорировать, как было раньше
|
|
@@ -38,6 +38,7 @@ if (process.env.NODE_ENV === 'test') {
|
|
|
38
38
|
publicPath: 'http://localhost:4000/',
|
|
39
39
|
assetsByChunkName: {},
|
|
40
40
|
entrypoints: {},
|
|
41
|
+
integrities: {},
|
|
41
42
|
});
|
|
42
43
|
};
|
|
43
44
|
}
|
|
@@ -71,37 +72,32 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
71
72
|
};
|
|
72
73
|
};
|
|
73
74
|
const statsCache = {};
|
|
74
|
-
fetchStats = (
|
|
75
|
+
fetchStats = (assetsPrefixFactory) => {
|
|
75
76
|
const assetsPrefix = assetsPrefixFactory();
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
statsCache[legacyStatsKey] = webpackStats('stats.json', assetsPrefix);
|
|
77
|
+
const statsKey = `${assetsPrefix}legacy`;
|
|
78
|
+
if (!statsCache[statsKey]) {
|
|
79
|
+
statsCache[statsKey] = webpackStats('stats.json', assetsPrefix);
|
|
80
80
|
}
|
|
81
|
-
if (!statsCache[
|
|
82
|
-
statsCache[modernStatsKey] =
|
|
83
|
-
webpackStats('stats.modern.json', assetsPrefix) || statsCache[legacyStatsKey];
|
|
84
|
-
}
|
|
85
|
-
if (!statsCache[legacyStatsKey]) {
|
|
81
|
+
if (!statsCache[statsKey]) {
|
|
86
82
|
throw new Error(`Cannot find stats.json.
|
|
87
83
|
It should be placed in one of the next places:
|
|
88
84
|
${SEARCH_PATHS.join('\n\t')}
|
|
89
85
|
In case it happens on deployment:
|
|
90
86
|
- In case you are using two independent jobs for building app
|
|
91
87
|
- Either do not split build command by two independent jobs and use one common job with "tramvai build" command without --buildType
|
|
92
|
-
- Or copy stats.json
|
|
88
|
+
- Or copy stats.json file from client build output to server output by yourself in your CI
|
|
93
89
|
- Otherwise report issue to tramvai team
|
|
94
90
|
In case it happens locally:
|
|
95
91
|
- prefer to use command "tramvai start-prod" to test prod-build locally
|
|
96
92
|
- copy stats.json next to built server.js file
|
|
97
93
|
`);
|
|
98
94
|
}
|
|
99
|
-
const stats =
|
|
95
|
+
const stats = statsCache[statsKey];
|
|
100
96
|
return Promise.resolve(stats);
|
|
101
97
|
};
|
|
102
98
|
}
|
|
103
|
-
const fetchWebpackStats = ({ assetsPrefixFactory }) => async (
|
|
104
|
-
return fetchStats(
|
|
99
|
+
const fetchWebpackStats = ({ assetsPrefixFactory }) => async () => {
|
|
100
|
+
return fetchStats(assetsPrefixFactory);
|
|
105
101
|
};
|
|
106
102
|
|
|
107
103
|
export { fetchWebpackStats };
|
|
@@ -43,10 +43,10 @@ let fetchStats = async () => {
|
|
|
43
43
|
throw new Error(`Unknown environment`);
|
|
44
44
|
};
|
|
45
45
|
if (process.env.NODE_ENV === 'development') {
|
|
46
|
-
fetchStats = async (
|
|
47
|
-
const {
|
|
46
|
+
fetchStats = async (assetsPrefixFactory) => {
|
|
47
|
+
const { staticHost, staticPort, output, httpProtocol } = appConfig;
|
|
48
48
|
const getUrl = (filename) => `${httpProtocol}://${staticHost}:${staticPort}/${output.client}/${filename}`;
|
|
49
|
-
const request = await fetch__default["default"](getUrl(
|
|
49
|
+
const request = await fetch__default["default"](getUrl('stats.json'));
|
|
50
50
|
const stats = await request.json();
|
|
51
51
|
const assetsPrefix = assetsPrefixFactory();
|
|
52
52
|
// static - популярная заглушка в env.development.js файлах, надо игнорировать, как было раньше
|
|
@@ -65,6 +65,7 @@ if (process.env.NODE_ENV === 'test') {
|
|
|
65
65
|
publicPath: 'http://localhost:4000/',
|
|
66
66
|
assetsByChunkName: {},
|
|
67
67
|
entrypoints: {},
|
|
68
|
+
integrities: {},
|
|
68
69
|
});
|
|
69
70
|
};
|
|
70
71
|
}
|
|
@@ -98,37 +99,32 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
98
99
|
};
|
|
99
100
|
};
|
|
100
101
|
const statsCache = {};
|
|
101
|
-
fetchStats = (
|
|
102
|
+
fetchStats = (assetsPrefixFactory) => {
|
|
102
103
|
const assetsPrefix = assetsPrefixFactory();
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
statsCache[legacyStatsKey] = webpackStats('stats.json', assetsPrefix);
|
|
104
|
+
const statsKey = `${assetsPrefix}legacy`;
|
|
105
|
+
if (!statsCache[statsKey]) {
|
|
106
|
+
statsCache[statsKey] = webpackStats('stats.json', assetsPrefix);
|
|
107
107
|
}
|
|
108
|
-
if (!statsCache[
|
|
109
|
-
statsCache[modernStatsKey] =
|
|
110
|
-
webpackStats('stats.modern.json', assetsPrefix) || statsCache[legacyStatsKey];
|
|
111
|
-
}
|
|
112
|
-
if (!statsCache[legacyStatsKey]) {
|
|
108
|
+
if (!statsCache[statsKey]) {
|
|
113
109
|
throw new Error(`Cannot find stats.json.
|
|
114
110
|
It should be placed in one of the next places:
|
|
115
111
|
${SEARCH_PATHS.join('\n\t')}
|
|
116
112
|
In case it happens on deployment:
|
|
117
113
|
- In case you are using two independent jobs for building app
|
|
118
114
|
- Either do not split build command by two independent jobs and use one common job with "tramvai build" command without --buildType
|
|
119
|
-
- Or copy stats.json
|
|
115
|
+
- Or copy stats.json file from client build output to server output by yourself in your CI
|
|
120
116
|
- Otherwise report issue to tramvai team
|
|
121
117
|
In case it happens locally:
|
|
122
118
|
- prefer to use command "tramvai start-prod" to test prod-build locally
|
|
123
119
|
- copy stats.json next to built server.js file
|
|
124
120
|
`);
|
|
125
121
|
}
|
|
126
|
-
const stats =
|
|
122
|
+
const stats = statsCache[statsKey];
|
|
127
123
|
return Promise.resolve(stats);
|
|
128
124
|
};
|
|
129
125
|
}
|
|
130
|
-
const fetchWebpackStats = ({ assetsPrefixFactory }) => async (
|
|
131
|
-
return fetchStats(
|
|
126
|
+
const fetchWebpackStats = ({ assetsPrefixFactory }) => async () => {
|
|
127
|
+
return fetchStats(assetsPrefixFactory);
|
|
132
128
|
};
|
|
133
129
|
|
|
134
130
|
exports.fetchWebpackStats = fetchWebpackStats;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { WebpackStats } from '@tramvai/tokens-render';
|
|
2
2
|
export declare const isJs: (file: string) => boolean;
|
|
3
3
|
export declare const isCss: (file: string) => boolean;
|
|
4
|
-
export declare const flushFiles: (chunks: string[], webpackStats: WebpackStats, { ignoreDependencies, }?: {
|
|
4
|
+
export declare const flushFiles: (chunks: string[], webpackStats: WebpackStats, { ignoreDependencies, exclude, }?: {
|
|
5
5
|
ignoreDependencies?: boolean;
|
|
6
|
+
exclude?: string[];
|
|
6
7
|
}) => {
|
|
7
8
|
scripts: string[];
|
|
8
9
|
styles: string[];
|
|
@@ -11,7 +11,7 @@ const getFilesByType = (files) => {
|
|
|
11
11
|
styles,
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
|
-
const flushFiles = (chunks, webpackStats, { ignoreDependencies = false, } = {}) => {
|
|
14
|
+
const flushFiles = (chunks, webpackStats, { ignoreDependencies = false, exclude, } = {}) => {
|
|
15
15
|
// при использовании namedChunkGroups во все entry-файлы как зависимость попадает runtimeChunk
|
|
16
16
|
// что при повторных вызовах flushChunks вызовет дублирование подключения manifest.js
|
|
17
17
|
// из-за чего приложение может запускаться несколько раз
|
|
@@ -26,7 +26,10 @@ const flushFiles = (chunks, webpackStats, { ignoreDependencies = false, } = {})
|
|
|
26
26
|
resolvedChunks.push(chunk);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
const
|
|
29
|
+
const filteredChunks = exclude
|
|
30
|
+
? resolvedChunks.filter((chunk) => !exclude.includes(chunk))
|
|
31
|
+
: resolvedChunks;
|
|
32
|
+
const files = flatten(uniq(filteredChunks).map((chunk) => {
|
|
30
33
|
let assetFiles = assetsByChunkName[chunk];
|
|
31
34
|
if (!assetFiles && allChunks) {
|
|
32
35
|
const chunkById = allChunks.find((c) => c.id === chunk);
|
|
@@ -20,7 +20,7 @@ const getFilesByType = (files) => {
|
|
|
20
20
|
styles,
|
|
21
21
|
};
|
|
22
22
|
};
|
|
23
|
-
const flushFiles = (chunks, webpackStats, { ignoreDependencies = false, } = {}) => {
|
|
23
|
+
const flushFiles = (chunks, webpackStats, { ignoreDependencies = false, exclude, } = {}) => {
|
|
24
24
|
// при использовании namedChunkGroups во все entry-файлы как зависимость попадает runtimeChunk
|
|
25
25
|
// что при повторных вызовах flushChunks вызовет дублирование подключения manifest.js
|
|
26
26
|
// из-за чего приложение может запускаться несколько раз
|
|
@@ -35,7 +35,10 @@ const flushFiles = (chunks, webpackStats, { ignoreDependencies = false, } = {})
|
|
|
35
35
|
resolvedChunks.push(chunk);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
const
|
|
38
|
+
const filteredChunks = exclude
|
|
39
|
+
? resolvedChunks.filter((chunk) => !exclude.includes(chunk))
|
|
40
|
+
: resolvedChunks;
|
|
41
|
+
const files = flatten__default["default"](uniq__default["default"](filteredChunks).map((chunk) => {
|
|
39
42
|
let assetFiles = assetsByChunkName[chunk];
|
|
40
43
|
if (!assetFiles && allChunks) {
|
|
41
44
|
const chunkById = allChunks.find((c) => c.id === chunk);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const REACT_RENDER = "react:render";
|
|
2
2
|
export declare const HEAD_PERFORMANCE = "head:performance";
|
|
3
3
|
export declare const HEAD_META = "head:meta";
|
|
4
|
+
export declare const HEAD_WEBPACK_RUNTIME = "head:webpack-runtime";
|
|
4
5
|
export declare const HEAD_POLYFILLS = "head:polyfills";
|
|
5
6
|
export declare const HEAD_CORE_STYLES = "head:core-styles";
|
|
6
7
|
export declare const HEAD_CORE_SCRIPTS = "head:core-scripts";
|
|
@@ -2,7 +2,7 @@ import { staticRender, dynamicRender } from '@tinkoff/htmlpagebuilder';
|
|
|
2
2
|
import { ResourceSlot } from '@tramvai/tokens-render';
|
|
3
3
|
import { formatAttributes } from './utils.es.js';
|
|
4
4
|
|
|
5
|
-
const { REACT_RENDER, HEAD_CORE_SCRIPTS, HEAD_DYNAMIC_SCRIPTS, HEAD_META, HEAD_POLYFILLS, HEAD_CORE_STYLES, HEAD_PERFORMANCE, HEAD_ANALYTICS, BODY_START, BODY_END, HEAD_ICONS, BODY_TAIL_ANALYTICS, BODY_TAIL, } = ResourceSlot;
|
|
5
|
+
const { REACT_RENDER, HEAD_CORE_SCRIPTS, HEAD_DYNAMIC_SCRIPTS, HEAD_META, HEAD_POLYFILLS, HEAD_WEBPACK_RUNTIME, HEAD_CORE_STYLES, HEAD_PERFORMANCE, HEAD_ANALYTICS, BODY_START, BODY_END, HEAD_ICONS, BODY_TAIL_ANALYTICS, BODY_TAIL, } = ResourceSlot;
|
|
6
6
|
const htmlPageSchemaFactory = ({ htmlAttrs, }) => {
|
|
7
7
|
return [
|
|
8
8
|
staticRender('<!DOCTYPE html>'),
|
|
@@ -12,6 +12,7 @@ const htmlPageSchemaFactory = ({ htmlAttrs, }) => {
|
|
|
12
12
|
dynamicRender(HEAD_META),
|
|
13
13
|
dynamicRender(HEAD_PERFORMANCE),
|
|
14
14
|
dynamicRender(HEAD_CORE_STYLES),
|
|
15
|
+
dynamicRender(HEAD_WEBPACK_RUNTIME),
|
|
15
16
|
dynamicRender(HEAD_POLYFILLS),
|
|
16
17
|
dynamicRender(HEAD_DYNAMIC_SCRIPTS),
|
|
17
18
|
dynamicRender(HEAD_CORE_SCRIPTS),
|
|
@@ -6,7 +6,7 @@ var htmlpagebuilder = require('@tinkoff/htmlpagebuilder');
|
|
|
6
6
|
var tokensRender = require('@tramvai/tokens-render');
|
|
7
7
|
var utils = require('./utils.js');
|
|
8
8
|
|
|
9
|
-
const { REACT_RENDER, HEAD_CORE_SCRIPTS, HEAD_DYNAMIC_SCRIPTS, HEAD_META, HEAD_POLYFILLS, HEAD_CORE_STYLES, HEAD_PERFORMANCE, HEAD_ANALYTICS, BODY_START, BODY_END, HEAD_ICONS, BODY_TAIL_ANALYTICS, BODY_TAIL, } = tokensRender.ResourceSlot;
|
|
9
|
+
const { REACT_RENDER, HEAD_CORE_SCRIPTS, HEAD_DYNAMIC_SCRIPTS, HEAD_META, HEAD_POLYFILLS, HEAD_WEBPACK_RUNTIME, HEAD_CORE_STYLES, HEAD_PERFORMANCE, HEAD_ANALYTICS, BODY_START, BODY_END, HEAD_ICONS, BODY_TAIL_ANALYTICS, BODY_TAIL, } = tokensRender.ResourceSlot;
|
|
10
10
|
const htmlPageSchemaFactory = ({ htmlAttrs, }) => {
|
|
11
11
|
return [
|
|
12
12
|
htmlpagebuilder.staticRender('<!DOCTYPE html>'),
|
|
@@ -16,6 +16,7 @@ const htmlPageSchemaFactory = ({ htmlAttrs, }) => {
|
|
|
16
16
|
htmlpagebuilder.dynamicRender(HEAD_META),
|
|
17
17
|
htmlpagebuilder.dynamicRender(HEAD_PERFORMANCE),
|
|
18
18
|
htmlpagebuilder.dynamicRender(HEAD_CORE_STYLES),
|
|
19
|
+
htmlpagebuilder.dynamicRender(HEAD_WEBPACK_RUNTIME),
|
|
19
20
|
htmlpagebuilder.dynamicRender(HEAD_POLYFILLS),
|
|
20
21
|
htmlpagebuilder.dynamicRender(HEAD_DYNAMIC_SCRIPTS),
|
|
21
22
|
htmlpagebuilder.dynamicRender(HEAD_CORE_SCRIPTS),
|