@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.
- package/lib/builder/webpack/devServer/server.js +10 -0
- package/lib/builder/webpack/devServer/server.js.map +1 -1
- package/lib/config/configManager.js +12 -13
- package/lib/config/configManager.js.map +1 -1
- package/lib/library/webpack/application/client/dev.js +6 -14
- package/lib/library/webpack/application/client/dev.js.map +1 -1
- package/lib/library/webpack/application/client/prod/optimization/splitChunks.js +14 -1
- package/lib/library/webpack/application/client/prod/optimization/splitChunks.js.map +1 -1
- package/lib/library/webpack/blocks/pwa/client.js +24 -2
- package/lib/library/webpack/blocks/pwa/client.js.map +1 -1
- package/lib/library/webpack/common/main.js +3 -1
- package/lib/library/webpack/common/main.js.map +1 -1
- package/lib/library/webpack/plugins/PwaIconsPlugin.d.ts +3 -1
- package/lib/library/webpack/plugins/PwaIconsPlugin.js +7 -6
- package/lib/library/webpack/plugins/PwaIconsPlugin.js.map +1 -1
- package/lib/library/webpack/plugins/WebManifestPlugin.d.ts +6 -2
- package/lib/library/webpack/plugins/WebManifestPlugin.js +23 -3
- package/lib/library/webpack/plugins/WebManifestPlugin.js.map +1 -1
- package/lib/schema/autogeneratedSchema.json +16 -16
- package/lib/typings/pwa/index.d.ts +1 -1
- package/package.json +5 -5
- package/schema.json +16 -16
- package/src/api/start/__integration__/__fixtures__/app/server.tsx +1 -0
- package/src/api/start/__integration__/start.test.ts +2 -0
- package/src/builder/webpack/devServer/server.ts +13 -0
- package/src/config/configManager.ts +10 -13
- package/src/library/webpack/application/client/dev.ts +9 -17
- package/src/library/webpack/application/client/prod/optimization/splitChunks.ts +18 -1
- package/src/library/webpack/blocks/pwa/client.ts +36 -2
- package/src/library/webpack/common/main.ts +3 -1
- package/src/library/webpack/plugins/PwaIconsPlugin.ts +14 -7
- package/src/library/webpack/plugins/WebManifestPlugin.ts +35 -5
- package/src/schema/autogeneratedSchema.json +16 -16
- 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.
|
|
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.
|
|
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.
|
|
75
|
-
"@tramvai/tools-check-versions": "0.4.
|
|
76
|
-
"@tramvai/tools-migrate": "0.6.
|
|
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@
|
|
1146
|
+
"__@match@8191": {
|
|
1147
1147
|
"type": "object",
|
|
1148
1148
|
"additionalProperties": false
|
|
1149
1149
|
},
|
|
1150
|
-
"__@replace@
|
|
1150
|
+
"__@replace@8193": {
|
|
1151
1151
|
"type": "object",
|
|
1152
1152
|
"additionalProperties": false
|
|
1153
1153
|
},
|
|
1154
|
-
"__@search@
|
|
1154
|
+
"__@search@8196": {
|
|
1155
1155
|
"type": "object",
|
|
1156
1156
|
"additionalProperties": false
|
|
1157
1157
|
},
|
|
1158
|
-
"__@split@
|
|
1158
|
+
"__@split@8198": {
|
|
1159
1159
|
"type": "object",
|
|
1160
1160
|
"additionalProperties": false
|
|
1161
1161
|
},
|
|
1162
|
-
"__@matchAll@
|
|
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@
|
|
1796
|
+
"__@match@8191": {
|
|
1797
1797
|
"type": "object",
|
|
1798
1798
|
"additionalProperties": false
|
|
1799
1799
|
},
|
|
1800
|
-
"__@replace@
|
|
1800
|
+
"__@replace@8193": {
|
|
1801
1801
|
"type": "object",
|
|
1802
1802
|
"additionalProperties": false
|
|
1803
1803
|
},
|
|
1804
|
-
"__@search@
|
|
1804
|
+
"__@search@8196": {
|
|
1805
1805
|
"type": "object",
|
|
1806
1806
|
"additionalProperties": false
|
|
1807
1807
|
},
|
|
1808
|
-
"__@split@
|
|
1808
|
+
"__@split@8198": {
|
|
1809
1809
|
"type": "object",
|
|
1810
1810
|
"additionalProperties": false
|
|
1811
1811
|
},
|
|
1812
|
-
"__@matchAll@
|
|
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@
|
|
2446
|
+
"__@match@8191": {
|
|
2447
2447
|
"type": "object",
|
|
2448
2448
|
"additionalProperties": false
|
|
2449
2449
|
},
|
|
2450
|
-
"__@replace@
|
|
2450
|
+
"__@replace@8193": {
|
|
2451
2451
|
"type": "object",
|
|
2452
2452
|
"additionalProperties": false
|
|
2453
2453
|
},
|
|
2454
|
-
"__@search@
|
|
2454
|
+
"__@search@8196": {
|
|
2455
2455
|
"type": "object",
|
|
2456
2456
|
"additionalProperties": false
|
|
2457
2457
|
},
|
|
2458
|
-
"__@split@
|
|
2458
|
+
"__@split@8198": {
|
|
2459
2459
|
"type": "object",
|
|
2460
2460
|
"additionalProperties": false
|
|
2461
2461
|
},
|
|
2462
|
-
"__@matchAll@
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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({
|
|
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':
|
|
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(
|
|
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
|
-
|
|
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}
|
|
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(
|
|
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 {
|
|
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@
|
|
1125
|
+
"__@match@8191": {
|
|
1126
1126
|
"type": "object",
|
|
1127
1127
|
"additionalProperties": false
|
|
1128
1128
|
},
|
|
1129
|
-
"__@replace@
|
|
1129
|
+
"__@replace@8193": {
|
|
1130
1130
|
"type": "object",
|
|
1131
1131
|
"additionalProperties": false
|
|
1132
1132
|
},
|
|
1133
|
-
"__@search@
|
|
1133
|
+
"__@search@8196": {
|
|
1134
1134
|
"type": "object",
|
|
1135
1135
|
"additionalProperties": false
|
|
1136
1136
|
},
|
|
1137
|
-
"__@split@
|
|
1137
|
+
"__@split@8198": {
|
|
1138
1138
|
"type": "object",
|
|
1139
1139
|
"additionalProperties": false
|
|
1140
1140
|
},
|
|
1141
|
-
"__@matchAll@
|
|
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@
|
|
1767
|
+
"__@match@8191": {
|
|
1768
1768
|
"type": "object",
|
|
1769
1769
|
"additionalProperties": false
|
|
1770
1770
|
},
|
|
1771
|
-
"__@replace@
|
|
1771
|
+
"__@replace@8193": {
|
|
1772
1772
|
"type": "object",
|
|
1773
1773
|
"additionalProperties": false
|
|
1774
1774
|
},
|
|
1775
|
-
"__@search@
|
|
1775
|
+
"__@search@8196": {
|
|
1776
1776
|
"type": "object",
|
|
1777
1777
|
"additionalProperties": false
|
|
1778
1778
|
},
|
|
1779
|
-
"__@split@
|
|
1779
|
+
"__@split@8198": {
|
|
1780
1780
|
"type": "object",
|
|
1781
1781
|
"additionalProperties": false
|
|
1782
1782
|
},
|
|
1783
|
-
"__@matchAll@
|
|
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@
|
|
2409
|
+
"__@match@8191": {
|
|
2410
2410
|
"type": "object",
|
|
2411
2411
|
"additionalProperties": false
|
|
2412
2412
|
},
|
|
2413
|
-
"__@replace@
|
|
2413
|
+
"__@replace@8193": {
|
|
2414
2414
|
"type": "object",
|
|
2415
2415
|
"additionalProperties": false
|
|
2416
2416
|
},
|
|
2417
|
-
"__@search@
|
|
2417
|
+
"__@search@8196": {
|
|
2418
2418
|
"type": "object",
|
|
2419
2419
|
"additionalProperties": false
|
|
2420
2420
|
},
|
|
2421
|
-
"__@split@
|
|
2421
|
+
"__@split@8198": {
|
|
2422
2422
|
"type": "object",
|
|
2423
2423
|
"additionalProperties": false
|
|
2424
2424
|
},
|
|
2425
|
-
"__@matchAll@
|
|
2425
|
+
"__@matchAll@8200": {
|
|
2426
2426
|
"type": "object",
|
|
2427
2427
|
"additionalProperties": false
|
|
2428
2428
|
}
|
package/src/typings/pwa/index.ts
CHANGED
|
@@ -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;
|