@tramvai/module-render 5.50.0 → 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.
Files changed (31) hide show
  1. package/lib/browser.js +1 -12
  2. package/lib/resourcesInliner/resourcesInliner.es.js +71 -67
  3. package/lib/resourcesInliner/resourcesInliner.js +71 -67
  4. package/lib/resourcesRegistry/index.es.js +2 -1
  5. package/lib/resourcesRegistry/index.js +2 -1
  6. package/lib/server/PageBuilder.d.ts +3 -3
  7. package/lib/server/PageBuilder.es.js +21 -6
  8. package/lib/server/PageBuilder.js +21 -6
  9. package/lib/server/ReactRenderServer.es.js +15 -1
  10. package/lib/server/ReactRenderServer.js +15 -1
  11. package/lib/server/blocks/bundleResource/bundleResource.d.ts +2 -2
  12. package/lib/server/blocks/bundleResource/bundleResource.es.js +42 -7
  13. package/lib/server/blocks/bundleResource/bundleResource.js +42 -7
  14. package/lib/server/blocks/polyfill.d.ts +1 -2
  15. package/lib/server/blocks/polyfill.es.js +18 -6
  16. package/lib/server/blocks/polyfill.js +18 -6
  17. package/lib/server/blocks/utils/fetchWebpackRuntime.d.ts +2 -0
  18. package/lib/server/blocks/utils/fetchWebpackRuntime.es.js +16 -0
  19. package/lib/server/blocks/utils/fetchWebpackRuntime.js +24 -0
  20. package/lib/server/blocks/utils/fetchWebpackStats.es.js +13 -17
  21. package/lib/server/blocks/utils/fetchWebpackStats.js +13 -17
  22. package/lib/server/blocks/utils/flushFiles.d.ts +2 -1
  23. package/lib/server/blocks/utils/flushFiles.es.js +5 -2
  24. package/lib/server/blocks/utils/flushFiles.js +5 -2
  25. package/lib/server/constants/slots.d.ts +1 -0
  26. package/lib/server/htmlPageSchema.es.js +2 -1
  27. package/lib/server/htmlPageSchema.js +2 -1
  28. package/lib/server.es.js +7 -37
  29. package/lib/server.js +5 -35
  30. package/package.json +20 -20
  31. 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, modern, extractor, pageComponent, fetchWebpackStats, renderMode, assetsPrefixFactory, }) => {
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({ modern });
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.map((style) => result.push({
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.map((script) => result.push({
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.map((script) => result.push({
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, modern, extractor, pageComponent, fetchWebpackStats, renderMode, assetsPrefixFactory, }) => {
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({ modern });
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.map((style) => result.push({
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.map((script) => result.push({
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.map((script) => result.push({
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, modern, fetchWebpackStats, renderMode, }: {
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, modern, fetchWebpackStats, renderMode, }) => {
5
- const webpackStats = await fetchWebpackStats({ modern });
4
+ const polyfillResources = async ({ condition, fetchWebpackStats, renderMode, }) => {
5
+ const webpackStats = await fetchWebpackStats();
6
6
  const { publicPath, polyfillCondition } = webpackStats;
7
- // получает файл полифилла из stats.json\stats.modern.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) { document.write('<script${renderMode === 'streaming' ? '' : ' defer="defer"'} charset="utf-8" data-critical="true" crossorigin="anonymous" src="${href}"><\\/script>')}
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, modern, fetchWebpackStats, renderMode, }) => {
9
- const webpackStats = await fetchWebpackStats({ modern });
8
+ const polyfillResources = async ({ condition, fetchWebpackStats, renderMode, }) => {
9
+ const webpackStats = await fetchWebpackStats();
10
10
  const { publicPath, polyfillCondition } = webpackStats;
11
- // получает файл полифилла из stats.json\stats.modern.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) { document.write('<script${renderMode === 'streaming' ? '' : ' defer="defer"'} charset="utf-8" data-critical="true" crossorigin="anonymous" src="${href}"><\\/script>')}
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,2 @@
1
+ export declare const fetchWebpackRuntime: (webpackRuntimeURL: string) => Promise<any>;
2
+ //# sourceMappingURL=fetchWebpackRuntime.d.ts.map
@@ -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 (_, assetsPrefixFactory) => {
20
- const { modern: configModern, staticHost, staticPort, output, httpProtocol } = appConfig;
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(configModern ? 'stats.modern.json' : 'stats.json'));
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 = (modern, assetsPrefixFactory) => {
75
+ fetchStats = (assetsPrefixFactory) => {
75
76
  const assetsPrefix = assetsPrefixFactory();
76
- const legacyStatsKey = `${assetsPrefix}legacy`;
77
- const modernStatsKey = `${assetsPrefix}modern`;
78
- if (!statsCache[legacyStatsKey]) {
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[modernStatsKey]) {
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 (and stats.modern.json if present) file from client build output to server output by yourself in your CI
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 = modern ? statsCache[modernStatsKey] : statsCache[legacyStatsKey];
95
+ const stats = statsCache[statsKey];
100
96
  return Promise.resolve(stats);
101
97
  };
102
98
  }
103
- const fetchWebpackStats = ({ assetsPrefixFactory }) => async ({ modern } = {}) => {
104
- return fetchStats(modern, assetsPrefixFactory);
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 (_, assetsPrefixFactory) => {
47
- const { modern: configModern, staticHost, staticPort, output, httpProtocol } = appConfig;
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(configModern ? 'stats.modern.json' : 'stats.json'));
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 = (modern, assetsPrefixFactory) => {
102
+ fetchStats = (assetsPrefixFactory) => {
102
103
  const assetsPrefix = assetsPrefixFactory();
103
- const legacyStatsKey = `${assetsPrefix}legacy`;
104
- const modernStatsKey = `${assetsPrefix}modern`;
105
- if (!statsCache[legacyStatsKey]) {
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[modernStatsKey]) {
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 (and stats.modern.json if present) file from client build output to server output by yourself in your CI
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 = modern ? statsCache[modernStatsKey] : statsCache[legacyStatsKey];
122
+ const stats = statsCache[statsKey];
127
123
  return Promise.resolve(stats);
128
124
  };
129
125
  }
130
- const fetchWebpackStats = ({ assetsPrefixFactory }) => async ({ modern } = {}) => {
131
- return fetchStats(modern, assetsPrefixFactory);
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 files = flatten(uniq(resolvedChunks).map((chunk) => {
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 files = flatten__default["default"](uniq__default["default"](resolvedChunks).map((chunk) => {
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),