1 const fs
= require('fs');
2 const path
= require('path');
3 const webpack
= require('webpack');
4 const HtmlWebpackPlugin
= require('html-webpack-plugin');
5 const CopyWebpackPlugin
= require('copy-webpack-plugin');
6 const MiniCssExtractPlugin
= require('mini-css-extract-plugin');
7 const FaviconsWebpackPlugin
= require('favicons-webpack-plugin');
8 const ReactRefreshWebpackPlugin
= require('@pmmmwh/react-refresh-webpack-plugin');
9 const ESLintPlugin
= require('eslint-webpack-plugin');
10 const ForkTsCheckerWebpackPlugin
= require('fork-ts-checker-webpack-plugin');
11 const { SubresourceIntegrityPlugin
} = require('webpack-subresource-integrity');
12 const { RetryChunkLoadPlugin
} = require('webpack-retry-chunk-load-plugin');
13 const SentryCliPlugin
= require('@sentry/webpack-plugin');
14 const PostCssLogicalWebpackPlugin
= require('./postcss-logical-webpack-plugin').default;
15 const WriteWebpackPlugin
= require('./write-webpack-plugin').default;
16 const HtmlEditWebpackPlugin
= require('./html-edit-webpack-plugin').default;
17 const BundleAnalyzerPlugin
= require('webpack-bundle-analyzer').BundleAnalyzerPlugin
;
19 const defaultFaviconConfig
= require('./favicon.config');
20 const faviconConfig
= require(path
.resolve('./favicon.config.js'));
21 const { getIndexChunks
} = require('../webpack/entries');
41 new ReactRefreshWebpackPlugin({
44 (warningLogs
|| errorLogs
) &&
46 extensions
: ['js', 'ts', 'tsx'],
47 eslintPath
: require
.resolve('eslint'),
48 context
: path
.resolve('.'),
49 emitWarning
: warningLogs
,
51 // ESLint class options
52 resolvePluginsRelativeTo
: __dirname
,
53 cwd
: path
.resolve('.'),
56 (warningLogs
|| errorLogs
) &&
57 new ForkTsCheckerWebpackPlugin({
65 if (warningLogs
&& issue
.severity
=== 'warning') {
68 if (errorLogs
&& issue
.severity
=== 'error') {
78 * Sentry webpack plugin is only run on tag creation (IS_RELEASE_BUNDLE)
79 * Needed values for source-maps upload
80 * project: defined in sentry.properties of each app
81 * org: defined in SENTRY_ORG (gitlab env)
82 * url: defined in SENTRY_URL (gitlab env)
83 * authToken: defined in SENTRY_AUTH_TOKEN (gitlab env)
88 ignore
: ['node_modules', 'webpack.config.js'],
89 configFile
: 'sentry.properties',
90 // This prevents build to fail if any issue happened
91 errorHandler
: (err
, invokeErr
, compilation
) => {
92 compilation
.warnings
.push('Sentry CLI Plugin: ' + err
.message
);
94 release
: buildData
.version
,
97 new CopyWebpackPlugin({
100 from: `${path.dirname(require.resolve('push.js'))}/serviceWorker.min.js`,
101 to
: 'assets/serviceWorker.min.js',
106 new WriteWebpackPlugin([
108 name
: 'assets/version.json',
109 data
: Buffer
.from(JSON
.stringify(buildData
, null, 2)),
113 new WriteWebpackPlugin([
115 name
: 'assets/host.png',
116 data
: Buffer
.from('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', 'base64'),
120 new CopyWebpackPlugin({
122 { from: 'src/.htaccess' },
123 // Fix max file limit if the folder does not exist
124 fs
.existsSync('public') && { from: 'public', noErrorOnMissing
: true },
128 new MiniCssExtractPlugin({
130 chunkFilename
: cssName
,
133 new HtmlWebpackPlugin({
134 template
: path
.resolve('./src/app.ejs'),
135 templateParameters
: {
136 appName
: faviconConfig
.favicons
.appName
,
137 title
: faviconConfig
.favicons
.appName
,
138 description
: faviconConfig
.favicons
.appDescription
,
139 url
: faviconConfig
.url
,
140 locales
: faviconConfig
.locales
,
141 ogImage
: faviconConfig
.ogImage
,
145 scriptLoading
: 'defer',
146 chunks
: getIndexChunks('index'),
147 minify
: isProduction
&& {
148 removeComments
: true,
149 collapseWhitespace
: true,
150 removeRedundantAttributes
: true,
151 useShortDoctype
: true,
152 removeEmptyAttributes
: true,
153 removeStyleLinkTypeAttributes
: true,
154 keepClosingSlash
: true,
161 new FaviconsWebpackPlugin({
162 logo
: path
.resolve(faviconConfig
.logo
),
163 logoMaskable
: path
.resolve(faviconConfig
.logoMaskable
),
164 cache
: path
.resolve('./node_modules/.cache'),
166 version
: buildData
.version
,
167 ...defaultFaviconConfig
,
168 ...faviconConfig
.favicons
,
174 new SubresourceIntegrityPlugin(),
175 new HtmlEditWebpackPlugin((tag
) => {
176 const src
= tag
.attributes
.href
|| tag
.attributes
.src
;
177 // Remove the integrity and crossorigin attributes for these files because we don't
178 // want to validate them since we may override the server response on these assets
179 // for certain scenarios.
180 if (/\.(css|png|svg|ico|json)(?:\?.+)?$/.test(src
)) {
181 if (tag
.attributes
.integrity
|| tag
.attributes
.crossorigin
) {
182 delete tag
.attributes
.integrity
;
183 delete tag
.attributes
.crossorigin
;
191 new HtmlEditWebpackPlugin((tag
) => {
192 // Remove the favicon.ico tag that the FaviconsWebpackPlugin generates because:
193 // 1) We want it to be listed before the .svg icon that we manually inject
194 // 2) We want it to have the sizes="any" attribute because of this chrome bug
195 // https://twitter.com/subzey/status/1417099064949235712
196 if (tag
.tagName
=== 'link' && tag
.attributes
.href
.endsWith('favicon.ico')) {
199 // With enabling the loadManifestWithCredentials option in the FaviconsWebpackPlugin the
200 // crossorigin attribute for the manifest.json link is added correctly, however the SRI
201 // plugin removes it and replaces it with SRI attributes. This plugin adds back the use-credentials
202 // crossorigin attribute for the manifest link.
203 if (tag
.tagName
=== 'link' && tag
.attributes
.href
.endsWith('manifest.webmanifest')) {
204 tag
.attributes
.crossorigin
= 'use-credentials';
209 new RetryChunkLoadPlugin({
210 cacheBust
: `function() {
217 new webpack
.DefinePlugin({
218 WEBPACK_APP_MODE
: JSON
.stringify(appMode
),
219 WEBPACK_PUBLIC_PATH
: JSON
.stringify(publicPath
),
220 WEBPACK_FEATURE_FLAGS
: JSON
.stringify(featureFlags
),
223 logical
&& new PostCssLogicalWebpackPlugin(),
225 analyze
&& new BundleAnalyzerPlugin(),