@tramvai/cli 2.120.0 → 2.122.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 (34) hide show
  1. package/lib/builder/webpack/devServer/server.js +10 -0
  2. package/lib/builder/webpack/devServer/server.js.map +1 -1
  3. package/lib/config/configManager.js +12 -13
  4. package/lib/config/configManager.js.map +1 -1
  5. package/lib/library/webpack/application/client/dev.js +6 -14
  6. package/lib/library/webpack/application/client/dev.js.map +1 -1
  7. package/lib/library/webpack/application/client/prod/optimization/splitChunks.js +14 -1
  8. package/lib/library/webpack/application/client/prod/optimization/splitChunks.js.map +1 -1
  9. package/lib/library/webpack/blocks/pwa/client.js +24 -2
  10. package/lib/library/webpack/blocks/pwa/client.js.map +1 -1
  11. package/lib/library/webpack/common/main.js +3 -1
  12. package/lib/library/webpack/common/main.js.map +1 -1
  13. package/lib/library/webpack/plugins/PwaIconsPlugin.d.ts +3 -1
  14. package/lib/library/webpack/plugins/PwaIconsPlugin.js +7 -6
  15. package/lib/library/webpack/plugins/PwaIconsPlugin.js.map +1 -1
  16. package/lib/library/webpack/plugins/WebManifestPlugin.d.ts +6 -2
  17. package/lib/library/webpack/plugins/WebManifestPlugin.js +23 -3
  18. package/lib/library/webpack/plugins/WebManifestPlugin.js.map +1 -1
  19. package/lib/schema/autogeneratedSchema.json +16 -16
  20. package/lib/typings/pwa/index.d.ts +1 -1
  21. package/package.json +5 -5
  22. package/schema.json +16 -16
  23. package/src/api/start/__integration__/__fixtures__/app/server.tsx +1 -0
  24. package/src/api/start/__integration__/start.test.ts +2 -0
  25. package/src/builder/webpack/devServer/server.ts +13 -0
  26. package/src/config/configManager.ts +10 -13
  27. package/src/library/webpack/application/client/dev.ts +9 -17
  28. package/src/library/webpack/application/client/prod/optimization/splitChunks.ts +18 -1
  29. package/src/library/webpack/blocks/pwa/client.ts +36 -2
  30. package/src/library/webpack/common/main.ts +3 -1
  31. package/src/library/webpack/plugins/PwaIconsPlugin.ts +14 -7
  32. package/src/library/webpack/plugins/WebManifestPlugin.ts +35 -5
  33. package/src/schema/autogeneratedSchema.json +16 -16
  34. package/src/typings/pwa/index.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/cli",
3
- "version": "2.120.0",
3
+ "version": "2.122.0",
4
4
  "description": "Cli инструмент для сборки и запуска приложений",
5
5
  "files": [
6
6
  "src",
@@ -63,7 +63,7 @@
63
63
  "@tinkoff/browserslist-config": "0.2.5",
64
64
  "@tinkoff/dippy": "0.8.15",
65
65
  "@tinkoff/is-modern-lib": "2.0.10",
66
- "@tinkoff/logger": "0.10.63",
66
+ "@tinkoff/logger": "0.10.64",
67
67
  "@tinkoff/minicss-class-generator": "0.2.5",
68
68
  "@tinkoff/package-manager-wrapper": "0.1.9",
69
69
  "@tinkoff/request-core": "^0.9.2",
@@ -71,9 +71,9 @@
71
71
  "@tinkoff/utils": "^2.1.3",
72
72
  "@tinkoff/webpack-dedupe-plugin": "1.0.5",
73
73
  "@tramvai/build": "3.1.3",
74
- "@tramvai/react": "2.120.0",
75
- "@tramvai/tools-check-versions": "0.4.14",
76
- "@tramvai/tools-migrate": "0.6.18",
74
+ "@tramvai/react": "2.122.0",
75
+ "@tramvai/tools-check-versions": "0.4.15",
76
+ "@tramvai/tools-migrate": "0.6.19",
77
77
  "ajv": "^6.12.6",
78
78
  "ansi-escapes": "^4.3.2",
79
79
  "autoprefixer": "^10.4.8",
package/schema.json CHANGED
@@ -673,7 +673,7 @@
673
673
  "type": "boolean"
674
674
  },
675
675
  "dest": {
676
- "title": "Name of generated manifest file (will be placed in \"output.client\" directory). You can use `[hash]` placeholder for manifest cache busting",
676
+ "title": "Name of generated manifest file (will be placed in \"output.client\" directory). You can use `[hash]` placeholder for manifest cache busting in production mode",
677
677
  "default": "/manifest.[hash].json",
678
678
  "type": "string"
679
679
  },
@@ -1143,23 +1143,23 @@
1143
1143
  "dotAll": {
1144
1144
  "type": "boolean"
1145
1145
  },
1146
- "__@match@8168": {
1146
+ "__@match@8191": {
1147
1147
  "type": "object",
1148
1148
  "additionalProperties": false
1149
1149
  },
1150
- "__@replace@8170": {
1150
+ "__@replace@8193": {
1151
1151
  "type": "object",
1152
1152
  "additionalProperties": false
1153
1153
  },
1154
- "__@search@8173": {
1154
+ "__@search@8196": {
1155
1155
  "type": "object",
1156
1156
  "additionalProperties": false
1157
1157
  },
1158
- "__@split@8175": {
1158
+ "__@split@8198": {
1159
1159
  "type": "object",
1160
1160
  "additionalProperties": false
1161
1161
  },
1162
- "__@matchAll@8177": {
1162
+ "__@matchAll@8200": {
1163
1163
  "type": "object",
1164
1164
  "additionalProperties": false
1165
1165
  }
@@ -1793,23 +1793,23 @@
1793
1793
  "dotAll": {
1794
1794
  "type": "boolean"
1795
1795
  },
1796
- "__@match@8168": {
1796
+ "__@match@8191": {
1797
1797
  "type": "object",
1798
1798
  "additionalProperties": false
1799
1799
  },
1800
- "__@replace@8170": {
1800
+ "__@replace@8193": {
1801
1801
  "type": "object",
1802
1802
  "additionalProperties": false
1803
1803
  },
1804
- "__@search@8173": {
1804
+ "__@search@8196": {
1805
1805
  "type": "object",
1806
1806
  "additionalProperties": false
1807
1807
  },
1808
- "__@split@8175": {
1808
+ "__@split@8198": {
1809
1809
  "type": "object",
1810
1810
  "additionalProperties": false
1811
1811
  },
1812
- "__@matchAll@8177": {
1812
+ "__@matchAll@8200": {
1813
1813
  "type": "object",
1814
1814
  "additionalProperties": false
1815
1815
  }
@@ -2443,23 +2443,23 @@
2443
2443
  "dotAll": {
2444
2444
  "type": "boolean"
2445
2445
  },
2446
- "__@match@8168": {
2446
+ "__@match@8191": {
2447
2447
  "type": "object",
2448
2448
  "additionalProperties": false
2449
2449
  },
2450
- "__@replace@8170": {
2450
+ "__@replace@8193": {
2451
2451
  "type": "object",
2452
2452
  "additionalProperties": false
2453
2453
  },
2454
- "__@search@8173": {
2454
+ "__@search@8196": {
2455
2455
  "type": "object",
2456
2456
  "additionalProperties": false
2457
2457
  },
2458
- "__@split@8175": {
2458
+ "__@split@8198": {
2459
2459
  "type": "object",
2460
2460
  "additionalProperties": false
2461
2461
  },
2462
- "__@matchAll@8177": {
2462
+ "__@matchAll@8200": {
2463
2463
  "type": "object",
2464
2464
  "additionalProperties": false
2465
2465
  }
@@ -50,6 +50,7 @@ app.get<{ Querystring: Querystring }>(
50
50
  return `<html>
51
51
  <head>
52
52
  <link rel="stylesheet" href="http://localhost:${appConfig.staticPort}/dist/client/platform.css">
53
+ <script src="http://localhost:${appConfig.staticPort}/dist/client/react.js" defer></script>
53
54
  <script src="http://localhost:${appConfig.staticPort}/dist/client/hmr.js" defer></script>
54
55
  <script src="http://localhost:${appConfig.staticPort}/dist/client/platform.js" defer></script>
55
56
  </head>
@@ -79,6 +79,7 @@ describe('@tramvai/cli start command', () => {
79
79
 
80
80
  expect(responseServer.text)
81
81
  .toMatch(`<link rel="stylesheet" href="http://localhost:${staticServerPort}/dist/client/platform.css">
82
+ <script src="http://localhost:${staticServerPort}/dist/client/react.js" defer></script>
82
83
  <script src="http://localhost:${staticServerPort}/dist/client/hmr.js" defer></script>
83
84
  <script src="http://localhost:${staticServerPort}/dist/client/platform.js" defer></script>`);
84
85
  expect(responseServer.text).toMatch(`this is App`);
@@ -188,6 +189,7 @@ describe('@tramvai/cli start command', () => {
188
189
 
189
190
  expect(responseServer.text)
190
191
  .toMatch(`<link rel="stylesheet" href="http://localhost:${staticServerPort}/dist/client/platform.css">
192
+ <script src="http://localhost:${staticServerPort}/dist/client/react.js" defer></script>
191
193
  <script src="http://localhost:${staticServerPort}/dist/client/hmr.js" defer></script>
192
194
  <script src="http://localhost:${staticServerPort}/dist/client/platform.js" defer></script>`);
193
195
  expect(responseServer.text).toMatch(`this is App`);
@@ -86,6 +86,7 @@ export const serverRunner = ({
86
86
  let resolveWorkerPort: () => void | null;
87
87
  let workerPortPromise: Promise<void> | null;
88
88
  let hasExitedUnexpectedly = false;
89
+ let proxyErrorCount = 0;
89
90
 
90
91
  const proxy = createProxyServer({
91
92
  // указываем, что сами обработаем ответ
@@ -157,6 +158,8 @@ export const serverRunner = ({
157
158
  // делаем запросы к дочернему процессу и пытаемся полностью получить его ответ
158
159
  // если всё ок, то просто отправляем полученные данные клиенту
159
160
  proxy.on('proxyRes', (proxyRes, req, res) => {
161
+ proxyErrorCount--;
162
+
160
163
  if (!res.headersSent) {
161
164
  // дублируем всю логику прокси и отправляем ответ
162
165
  // немного костыль по мотивам https://github.com/http-party/node-http-proxy/issues/1263#issuecomment-394758768
@@ -197,7 +200,17 @@ export const serverRunner = ({
197
200
  return;
198
201
  }
199
202
 
203
+ if (proxyErrorCount > 10) {
204
+ console.error('[dev-server-error] looping of request proxying to worker', err);
205
+ // @ts-ignore
206
+ res.statusCode = 500;
207
+ res.end(EXITED_UNEXPECTEDLY);
208
+ return;
209
+ }
210
+
200
211
  if (pool.state !== PoolState.CLOSED) {
212
+ proxyErrorCount++;
213
+
201
214
  await waitWorkerPort();
202
215
 
203
216
  proxy.web(req, res as any, { target: `http://localhost:${workerPort}` });
@@ -197,13 +197,6 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
197
197
 
198
198
  if (pwa.webmanifest?.enabled) {
199
199
  pwa.webmanifest = {
200
- icons: pwa.icon?.src
201
- ? pwa.icon.sizes.map((size) => ({
202
- src: `${config.assetsPrefix}${pwa.icon.dest}/${size}x${size}.png`,
203
- sizes: `${size}x${size}`,
204
- type: 'image/png',
205
- }))
206
- : [],
207
200
  ...pwa.webmanifest,
208
201
  scope: pwa.webmanifest.scope ?? pwa.sw?.scope,
209
202
  name: pwa.webmanifest.name ?? config.name,
@@ -212,12 +205,16 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
212
205
  };
213
206
 
214
207
  if (pwa.webmanifest.dest.includes('[hash]')) {
215
- const crypto = require('crypto');
216
- const hashSum = crypto.createHash('sha256');
217
- hashSum.update(JSON.stringify(pwa.webmanifest));
218
- const currentHash = hashSum.digest('hex');
219
-
220
- pwa.webmanifest.dest = pwa.webmanifest.dest.replace('[hash]', currentHash.substr(0, 8));
208
+ if (env === 'production') {
209
+ const crypto = require('crypto');
210
+ const hashSum = crypto.createHash('sha256');
211
+ hashSum.update(JSON.stringify(pwa.webmanifest));
212
+ const currentHash = hashSum.digest('hex');
213
+
214
+ pwa.webmanifest.dest = pwa.webmanifest.dest.replace('[hash]', currentHash.substr(0, 8));
215
+ } else {
216
+ pwa.webmanifest.dest = pwa.webmanifest.dest.replace('.[hash]', '').replace('[hash].', '');
217
+ }
221
218
  }
222
219
  }
223
220
  } else if (isChildApp(config)) {
@@ -21,7 +21,7 @@ import sourcemaps from '../../blocks/sourcemaps';
21
21
  import FancyReporter from '../../plugins/WebpackBar/reporters/fancy';
22
22
  import { extendEntry } from '../../utils/extendEntry';
23
23
  import { extractCssPluginFactory } from '../../blocks/extractCssPlugin';
24
- import type { SplitChunksOptions } from '../../types/webpack';
24
+ import { splitChunksConfig } from './prod/optimization/splitChunks';
25
25
 
26
26
  // eslint-disable-next-line max-statements
27
27
  export const webpackClientConfig = ({
@@ -78,7 +78,12 @@ export const webpackClientConfig = ({
78
78
 
79
79
  config.optimization.set('emitOnErrors', false);
80
80
 
81
- config.batch(extractCssPluginFactory(configManager));
81
+ config.batch(
82
+ extractCssPluginFactory(configManager, {
83
+ filename: '[name].css',
84
+ chunkFilename: '[name].chunk.css',
85
+ })
86
+ );
82
87
 
83
88
  if (showProgress) {
84
89
  config.plugin('progress').use(WebpackBar, [
@@ -136,24 +141,11 @@ export const webpackClientConfig = ({
136
141
  },
137
142
  ]);
138
143
 
144
+ config.batch(splitChunksConfig(configManager));
145
+
139
146
  const { hotRefresh } = configManager;
140
147
 
141
148
  if (hotRefresh?.enabled) {
142
- const splitChunks: SplitChunksOptions = {
143
- cacheGroups: {
144
- default: false,
145
- defaultVendors: false,
146
- commons: {
147
- name: 'hmr',
148
- enforce: true,
149
- test: /[\\/]node_modules[\\/](react-refresh|webpack-hot-middleware|@pmmmwh[\\/]react-refresh-webpack-plugin)[\\/]/,
150
- chunks: 'all',
151
- },
152
- },
153
- };
154
-
155
- config.optimization.splitChunks(splitChunks).set('chunkIds', 'named');
156
-
157
149
  extendEntry(config.entry('platform'), {
158
150
  import: [
159
151
  'webpack-hot-middleware/client?name=client&dynamicPublicPath=true&path=__webpack_hmr',
@@ -73,7 +73,10 @@ export const splitChunksConfig =
73
73
  return false;
74
74
  }
75
75
 
76
- return topLevelFrameworkPaths.some((packagePath) => resource.startsWith(packagePath));
76
+ return (
77
+ !resource.startsWith('react-refresh') &&
78
+ topLevelFrameworkPaths.some((packagePath) => resource.startsWith(packagePath))
79
+ );
77
80
  },
78
81
  priority: 40,
79
82
  // Don't let webpack eliminate this chunk (prevents this chunk from becoming a part of the commons chunk)
@@ -96,6 +99,7 @@ export const splitChunksConfig =
96
99
  minChunks: splitChunks.granularChunksSplitNumber,
97
100
  minSize: splitChunks.granularChunksMinSize,
98
101
  reuseExistingChunk: true,
102
+ priority: 30,
99
103
  name(module: any, chunks: webpack.Chunk[]) {
100
104
  return crypto
101
105
  .createHash('sha1')
@@ -119,12 +123,25 @@ export const splitChunksConfig =
119
123
  commons: {
120
124
  name: 'common-chunk',
121
125
  minChunks: splitChunks.commonChunkSplitNumber,
126
+ priority: 30,
122
127
  },
123
128
  },
124
129
  };
125
130
  break;
126
131
  }
127
132
 
133
+ const { hotRefresh } = configManager;
134
+
135
+ if (hotRefresh?.enabled && webpackSplitChunks) {
136
+ webpackSplitChunks.cacheGroups.hmr = {
137
+ name: 'hmr',
138
+ enforce: true,
139
+ test: /[\\/]node_modules[\\/](react-refresh|webpack-hot-middleware|@pmmmwh[\\/]react-refresh-webpack-plugin)[\\/]/,
140
+ chunks: 'all',
141
+ priority: 20,
142
+ };
143
+ }
144
+
128
145
  config.optimization
129
146
  .splitChunks(webpackSplitChunks)
130
147
  // namedChunks должно быть включено, чтобы webpack-flush-chunks смог определить имена чанков от которых зависит чанк бандла после обработчки через splitChunks
@@ -54,6 +54,25 @@ export const pwaBlock =
54
54
  // @todo CSR fallback or all static pages?
55
55
  // do not forget about revision and possible conflict with modifyURLPrefix
56
56
  ],
57
+ manifestTransforms: [
58
+ (manifest, compilation: any) => {
59
+ return {
60
+ // we need to have a relative webmanifest url for precaching
61
+ manifest: manifest.map((asset) => {
62
+ const assetName = asset.url.replace(assetsPrefix, '');
63
+ const assetInfo = compilation.assetsInfo.get(asset.url.replace(assetsPrefix, ''));
64
+
65
+ if (assetInfo._webmanifestFilename) {
66
+ return {
67
+ ...asset,
68
+ url: `${pwa.sw.scope}${assetName}`,
69
+ };
70
+ }
71
+ return asset;
72
+ }),
73
+ };
74
+ },
75
+ ],
57
76
  };
58
77
 
59
78
  if (pwa.workbox.include) {
@@ -104,18 +123,33 @@ export const pwaBlock =
104
123
  config.output.set('devtoolNamespace', 'tramvai');
105
124
  }
106
125
 
126
+ config.plugin('define').tap((args) => [
127
+ {
128
+ ...args[0],
129
+ 'process.env.ASSETS_PREFIX': JSON.stringify(assetsPrefix),
130
+ },
131
+ ]);
132
+
107
133
  config.plugin('workbox').use(workboxPlugin);
108
134
  }
109
135
 
110
136
  if (pwa.webmanifest?.enabled) {
111
- const webmanifestPlugin = new WebManifestPlugin(pwa.webmanifest);
137
+ const webmanifestPlugin = new WebManifestPlugin({
138
+ manifest: pwa.webmanifest,
139
+ icon: pwa.icon,
140
+ assetsPrefix,
141
+ });
112
142
 
113
143
  config.plugin('webmanifest').use(webmanifestPlugin);
114
144
  }
115
145
 
116
146
  if (pwa.icon?.src) {
117
147
  const iconSrc = path.join(rootDir, root, pwa.icon.src);
118
- const pwaIconsPlugin = new PwaIconsPlugin({ ...pwa.icon, src: iconSrc });
148
+ const pwaIconsPlugin = new PwaIconsPlugin({
149
+ ...pwa.icon,
150
+ src: iconSrc,
151
+ mode: configManager.env,
152
+ });
119
153
 
120
154
  config.plugin('pwa-icons').use(pwaIconsPlugin);
121
155
  }
@@ -99,7 +99,9 @@ export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config
99
99
  ...configManager.define[configManager.env],
100
100
 
101
101
  'process.env.APP_ID': JSON.stringify(configManager.name || 'common'),
102
- 'process.env.APP_VERSION': JSON.stringify(process.env.APP_VERSION),
102
+ 'process.env.APP_VERSION': process.env.APP_VERSION
103
+ ? JSON.stringify(process.env.APP_VERSION)
104
+ : undefined,
103
105
 
104
106
  'process.env.ENABLE_DEVTOOLS':
105
107
  process.env.ENABLE_DEVTOOLS || configManager.env === 'development',
@@ -8,7 +8,11 @@ const pluginName = 'PwaIconsPlugin';
8
8
  export class PwaIconsPlugin implements webpack.WebpackPluginInstance {
9
9
  private hash: string;
10
10
 
11
- constructor(private options: PwaIconOptions) {
11
+ constructor(
12
+ private options: PwaIconOptions & {
13
+ mode: 'production' | 'development';
14
+ }
15
+ ) {
12
16
  this.options = options;
13
17
  }
14
18
 
@@ -16,7 +20,7 @@ export class PwaIconsPlugin implements webpack.WebpackPluginInstance {
16
20
  const { webpack } = compiler;
17
21
  const { Compilation } = webpack;
18
22
  const { RawSource } = webpack.sources;
19
- const { src, dest, sizes } = this.options;
23
+ const { src, dest, sizes, mode } = this.options;
20
24
 
21
25
  compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
22
26
  // watch icon source file
@@ -27,7 +31,8 @@ export class PwaIconsPlugin implements webpack.WebpackPluginInstance {
27
31
  compilation.hooks.processAssets.tapPromise(
28
32
  {
29
33
  name: pluginName,
30
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
34
+ // to work before WebManifestPlugin
35
+ stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS - 1,
31
36
  },
32
37
  async () => {
33
38
  try {
@@ -59,21 +64,23 @@ export class PwaIconsPlugin implements webpack.WebpackPluginInstance {
59
64
  })
60
65
  .toBuffer()
61
66
  .then((content) => ({
62
- filename: `${dest}/${size}x${size}.png`,
67
+ filename: `${dest}/${size}x${size}${
68
+ mode === 'development' ? '' : `.${this.hash}`
69
+ }.png`,
63
70
  content,
64
71
  }));
65
72
  });
66
73
 
67
74
  const results = await Promise.all(promises);
68
75
 
69
- results.forEach(({ filename, content }) => {
76
+ results.forEach(({ filename, content }, i) => {
70
77
  const source = new RawSource(content);
71
78
  const asset = compilation.getAsset(filename);
72
79
 
73
80
  if (asset) {
74
- compilation.updateAsset(filename, source);
81
+ compilation.updateAsset(filename, source, { _pwaIconSize: sizes[i] });
75
82
  } else {
76
- compilation.emitAsset(filename, source);
83
+ compilation.emitAsset(filename, source, { _pwaIconSize: sizes[i] });
77
84
  }
78
85
  });
79
86
  } catch (e) {
@@ -1,11 +1,13 @@
1
1
  import type webpack from 'webpack';
2
2
  import type { Compiler } from 'webpack';
3
- import type { WebManifestOptions } from '../../../typings/pwa';
3
+ import type { PwaIconOptions, WebManifestOptions } from '../../../typings/pwa';
4
4
 
5
5
  const pluginName = 'WebManifestPlugin';
6
6
 
7
7
  export class WebManifestPlugin implements webpack.WebpackPluginInstance {
8
- constructor(private options: WebManifestOptions) {
8
+ constructor(
9
+ private options: { manifest: WebManifestOptions; icon: PwaIconOptions; assetsPrefix: string }
10
+ ) {
9
11
  this.options = options;
10
12
  }
11
13
 
@@ -13,7 +15,11 @@ export class WebManifestPlugin implements webpack.WebpackPluginInstance {
13
15
  const { webpack } = compiler;
14
16
  const { Compilation } = webpack;
15
17
  const { RawSource } = webpack.sources;
16
- const { dest, enabled, ...content } = this.options;
18
+ const {
19
+ manifest: { dest, enabled, ...content },
20
+ icon,
21
+ assetsPrefix,
22
+ } = this.options;
17
23
 
18
24
  compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
19
25
  compilation.hooks.processAssets.tap(
@@ -21,10 +27,34 @@ export class WebManifestPlugin implements webpack.WebpackPluginInstance {
21
27
  name: pluginName,
22
28
  stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
23
29
  },
24
- () => {
30
+ (compilationAssets) => {
31
+ if (!content.icons && icon?.src) {
32
+ const assets = Object.keys(compilationAssets);
33
+
34
+ assets.forEach((asset) => {
35
+ const assetInfo = compilation.assetsInfo.get(asset);
36
+ // asset info `_pwaIconSize` added in PwaIconsPlugin
37
+ const size = assetInfo._pwaIconSize;
38
+
39
+ if (size) {
40
+ if (!content.icons) {
41
+ content.icons = [];
42
+ }
43
+
44
+ content.icons.push({
45
+ src: `${assetsPrefix}${asset}`,
46
+ sizes: `${size}x${size}`,
47
+ type: 'image/png',
48
+ });
49
+ }
50
+ });
51
+ }
52
+
25
53
  const manifestFilename = dest.replace(/^\//, '');
26
54
 
27
- compilation.emitAsset(manifestFilename, new RawSource(JSON.stringify(content, null, 2)));
55
+ compilation.emitAsset(manifestFilename, new RawSource(JSON.stringify(content, null, 2)), {
56
+ _webmanifestFilename: manifestFilename,
57
+ });
28
58
  }
29
59
  );
30
60
  });
@@ -652,7 +652,7 @@
652
652
  "type": "boolean"
653
653
  },
654
654
  "dest": {
655
- "title": "Name of generated manifest file (will be placed in \"output.client\" directory). You can use `[hash]` placeholder for manifest cache busting",
655
+ "title": "Name of generated manifest file (will be placed in \"output.client\" directory). You can use `[hash]` placeholder for manifest cache busting in production mode",
656
656
  "default": "/manifest.[hash].json",
657
657
  "type": "string"
658
658
  },
@@ -1122,23 +1122,23 @@
1122
1122
  "dotAll": {
1123
1123
  "type": "boolean"
1124
1124
  },
1125
- "__@match@8168": {
1125
+ "__@match@8191": {
1126
1126
  "type": "object",
1127
1127
  "additionalProperties": false
1128
1128
  },
1129
- "__@replace@8170": {
1129
+ "__@replace@8193": {
1130
1130
  "type": "object",
1131
1131
  "additionalProperties": false
1132
1132
  },
1133
- "__@search@8173": {
1133
+ "__@search@8196": {
1134
1134
  "type": "object",
1135
1135
  "additionalProperties": false
1136
1136
  },
1137
- "__@split@8175": {
1137
+ "__@split@8198": {
1138
1138
  "type": "object",
1139
1139
  "additionalProperties": false
1140
1140
  },
1141
- "__@matchAll@8177": {
1141
+ "__@matchAll@8200": {
1142
1142
  "type": "object",
1143
1143
  "additionalProperties": false
1144
1144
  }
@@ -1764,23 +1764,23 @@
1764
1764
  "dotAll": {
1765
1765
  "type": "boolean"
1766
1766
  },
1767
- "__@match@8168": {
1767
+ "__@match@8191": {
1768
1768
  "type": "object",
1769
1769
  "additionalProperties": false
1770
1770
  },
1771
- "__@replace@8170": {
1771
+ "__@replace@8193": {
1772
1772
  "type": "object",
1773
1773
  "additionalProperties": false
1774
1774
  },
1775
- "__@search@8173": {
1775
+ "__@search@8196": {
1776
1776
  "type": "object",
1777
1777
  "additionalProperties": false
1778
1778
  },
1779
- "__@split@8175": {
1779
+ "__@split@8198": {
1780
1780
  "type": "object",
1781
1781
  "additionalProperties": false
1782
1782
  },
1783
- "__@matchAll@8177": {
1783
+ "__@matchAll@8200": {
1784
1784
  "type": "object",
1785
1785
  "additionalProperties": false
1786
1786
  }
@@ -2406,23 +2406,23 @@
2406
2406
  "dotAll": {
2407
2407
  "type": "boolean"
2408
2408
  },
2409
- "__@match@8168": {
2409
+ "__@match@8191": {
2410
2410
  "type": "object",
2411
2411
  "additionalProperties": false
2412
2412
  },
2413
- "__@replace@8170": {
2413
+ "__@replace@8193": {
2414
2414
  "type": "object",
2415
2415
  "additionalProperties": false
2416
2416
  },
2417
- "__@search@8173": {
2417
+ "__@search@8196": {
2418
2418
  "type": "object",
2419
2419
  "additionalProperties": false
2420
2420
  },
2421
- "__@split@8175": {
2421
+ "__@split@8198": {
2422
2422
  "type": "object",
2423
2423
  "additionalProperties": false
2424
2424
  },
2425
- "__@matchAll@8177": {
2425
+ "__@matchAll@8200": {
2426
2426
  "type": "object",
2427
2427
  "additionalProperties": false
2428
2428
  }
@@ -5,7 +5,7 @@ export type WebManifestOptions = {
5
5
  */
6
6
  enabled?: boolean;
7
7
  /**
8
- * @title Name of generated manifest file (will be placed in "output.client" directory). You can use `[hash]` placeholder for manifest cache busting
8
+ * @title Name of generated manifest file (will be placed in "output.client" directory). You can use `[hash]` placeholder for manifest cache busting in production mode
9
9
  * @default "/manifest.[hash].json"
10
10
  */
11
11
  dest?: string;