@tramvai/module-render 5.49.1 → 6.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +15 -17
  21. package/lib/server/blocks/utils/fetchWebpackStats.js +15 -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 +23 -23
  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;
@@ -4,6 +4,8 @@ import { requireFunc } from './requireFunc.es.js';
4
4
 
5
5
  let appConfig;
6
6
  try {
7
+ // appConfig = require('virtual:tramvai/config').appConfig;
8
+ // eslint-disable-next-line import/extensions, import/no-unresolved
7
9
  appConfig = require('@tramvai/cli/lib/external/config').appConfig;
8
10
  }
9
11
  catch (e) {
@@ -16,10 +18,10 @@ let fetchStats = async () => {
16
18
  throw new Error(`Unknown environment`);
17
19
  };
18
20
  if (process.env.NODE_ENV === 'development') {
19
- fetchStats = async (_, assetsPrefixFactory) => {
20
- const { modern: configModern, staticHost, staticPort, output, httpProtocol } = appConfig;
21
+ fetchStats = async (assetsPrefixFactory) => {
22
+ const { staticHost, staticPort, output, httpProtocol } = appConfig;
21
23
  const getUrl = (filename) => `${httpProtocol}://${staticHost}:${staticPort}/${output.client}/${filename}`;
22
- const request = await fetch(getUrl(configModern ? 'stats.modern.json' : 'stats.json'));
24
+ const request = await fetch(getUrl('stats.json'));
23
25
  const stats = await request.json();
24
26
  const assetsPrefix = assetsPrefixFactory();
25
27
  // static - популярная заглушка в env.development.js файлах, надо игнорировать, как было раньше
@@ -38,6 +40,7 @@ if (process.env.NODE_ENV === 'test') {
38
40
  publicPath: 'http://localhost:4000/',
39
41
  assetsByChunkName: {},
40
42
  entrypoints: {},
43
+ integrities: {},
41
44
  });
42
45
  };
43
46
  }
@@ -71,37 +74,32 @@ if (process.env.NODE_ENV === 'production') {
71
74
  };
72
75
  };
73
76
  const statsCache = {};
74
- fetchStats = (modern, assetsPrefixFactory) => {
77
+ fetchStats = (assetsPrefixFactory) => {
75
78
  const assetsPrefix = assetsPrefixFactory();
76
- const legacyStatsKey = `${assetsPrefix}legacy`;
77
- const modernStatsKey = `${assetsPrefix}modern`;
78
- if (!statsCache[legacyStatsKey]) {
79
- statsCache[legacyStatsKey] = webpackStats('stats.json', assetsPrefix);
79
+ const statsKey = `${assetsPrefix}legacy`;
80
+ if (!statsCache[statsKey]) {
81
+ statsCache[statsKey] = webpackStats('stats.json', assetsPrefix);
80
82
  }
81
- if (!statsCache[modernStatsKey]) {
82
- statsCache[modernStatsKey] =
83
- webpackStats('stats.modern.json', assetsPrefix) || statsCache[legacyStatsKey];
84
- }
85
- if (!statsCache[legacyStatsKey]) {
83
+ if (!statsCache[statsKey]) {
86
84
  throw new Error(`Cannot find stats.json.
87
85
  It should be placed in one of the next places:
88
86
  ${SEARCH_PATHS.join('\n\t')}
89
87
  In case it happens on deployment:
90
88
  - In case you are using two independent jobs for building app
91
89
  - 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
90
+ - Or copy stats.json file from client build output to server output by yourself in your CI
93
91
  - Otherwise report issue to tramvai team
94
92
  In case it happens locally:
95
93
  - prefer to use command "tramvai start-prod" to test prod-build locally
96
94
  - copy stats.json next to built server.js file
97
95
  `);
98
96
  }
99
- const stats = modern ? statsCache[modernStatsKey] : statsCache[legacyStatsKey];
97
+ const stats = statsCache[statsKey];
100
98
  return Promise.resolve(stats);
101
99
  };
102
100
  }
103
- const fetchWebpackStats = ({ assetsPrefixFactory }) => async ({ modern } = {}) => {
104
- return fetchStats(modern, assetsPrefixFactory);
101
+ const fetchWebpackStats = ({ assetsPrefixFactory }) => async () => {
102
+ return fetchStats(assetsPrefixFactory);
105
103
  };
106
104
 
107
105
  export { fetchWebpackStats };
@@ -31,6 +31,8 @@ var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
31
31
 
32
32
  let appConfig;
33
33
  try {
34
+ // appConfig = require('virtual:tramvai/config').appConfig;
35
+ // eslint-disable-next-line import/extensions, import/no-unresolved
34
36
  appConfig = require('@tramvai/cli/lib/external/config').appConfig;
35
37
  }
36
38
  catch (e) {
@@ -43,10 +45,10 @@ let fetchStats = async () => {
43
45
  throw new Error(`Unknown environment`);
44
46
  };
45
47
  if (process.env.NODE_ENV === 'development') {
46
- fetchStats = async (_, assetsPrefixFactory) => {
47
- const { modern: configModern, staticHost, staticPort, output, httpProtocol } = appConfig;
48
+ fetchStats = async (assetsPrefixFactory) => {
49
+ const { staticHost, staticPort, output, httpProtocol } = appConfig;
48
50
  const getUrl = (filename) => `${httpProtocol}://${staticHost}:${staticPort}/${output.client}/${filename}`;
49
- const request = await fetch__default["default"](getUrl(configModern ? 'stats.modern.json' : 'stats.json'));
51
+ const request = await fetch__default["default"](getUrl('stats.json'));
50
52
  const stats = await request.json();
51
53
  const assetsPrefix = assetsPrefixFactory();
52
54
  // static - популярная заглушка в env.development.js файлах, надо игнорировать, как было раньше
@@ -65,6 +67,7 @@ if (process.env.NODE_ENV === 'test') {
65
67
  publicPath: 'http://localhost:4000/',
66
68
  assetsByChunkName: {},
67
69
  entrypoints: {},
70
+ integrities: {},
68
71
  });
69
72
  };
70
73
  }
@@ -98,37 +101,32 @@ if (process.env.NODE_ENV === 'production') {
98
101
  };
99
102
  };
100
103
  const statsCache = {};
101
- fetchStats = (modern, assetsPrefixFactory) => {
104
+ fetchStats = (assetsPrefixFactory) => {
102
105
  const assetsPrefix = assetsPrefixFactory();
103
- const legacyStatsKey = `${assetsPrefix}legacy`;
104
- const modernStatsKey = `${assetsPrefix}modern`;
105
- if (!statsCache[legacyStatsKey]) {
106
- statsCache[legacyStatsKey] = webpackStats('stats.json', assetsPrefix);
106
+ const statsKey = `${assetsPrefix}legacy`;
107
+ if (!statsCache[statsKey]) {
108
+ statsCache[statsKey] = webpackStats('stats.json', assetsPrefix);
107
109
  }
108
- if (!statsCache[modernStatsKey]) {
109
- statsCache[modernStatsKey] =
110
- webpackStats('stats.modern.json', assetsPrefix) || statsCache[legacyStatsKey];
111
- }
112
- if (!statsCache[legacyStatsKey]) {
110
+ if (!statsCache[statsKey]) {
113
111
  throw new Error(`Cannot find stats.json.
114
112
  It should be placed in one of the next places:
115
113
  ${SEARCH_PATHS.join('\n\t')}
116
114
  In case it happens on deployment:
117
115
  - In case you are using two independent jobs for building app
118
116
  - 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
117
+ - Or copy stats.json file from client build output to server output by yourself in your CI
120
118
  - Otherwise report issue to tramvai team
121
119
  In case it happens locally:
122
120
  - prefer to use command "tramvai start-prod" to test prod-build locally
123
121
  - copy stats.json next to built server.js file
124
122
  `);
125
123
  }
126
- const stats = modern ? statsCache[modernStatsKey] : statsCache[legacyStatsKey];
124
+ const stats = statsCache[statsKey];
127
125
  return Promise.resolve(stats);
128
126
  };
129
127
  }
130
- const fetchWebpackStats = ({ assetsPrefixFactory }) => async ({ modern } = {}) => {
131
- return fetchStats(modern, assetsPrefixFactory);
128
+ const fetchWebpackStats = ({ assetsPrefixFactory }) => async () => {
129
+ return fetchStats(assetsPrefixFactory);
132
130
  };
133
131
 
134
132
  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),