i18n: Upgrade translations from crowdin (a80a6511). (vpn-settings)
[ProtonMail-WebClient.git] / packages / pack / webpack / plugins.js
blob255e5ddb20a67d34845b5da775d4a5a8f52e465e
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');
23 module.exports = ({
24 isProduction,
25 isRelease,
26 publicPath,
27 appMode,
28 buildData,
29 featureFlags,
30 writeSRI,
31 warningLogs,
32 errorLogs,
33 logical,
34 cssName,
35 analyze,
36 }) => {
37 return [
38 ...(isProduction
39 ? []
40 : [
41 new ReactRefreshWebpackPlugin({
42 overlay: false,
43 }),
44 (warningLogs || errorLogs) &&
45 new ESLintPlugin({
46 extensions: ['js', 'ts', 'tsx'],
47 eslintPath: require.resolve('eslint'),
48 context: path.resolve('.'),
49 emitWarning: warningLogs,
50 emitError: errorLogs,
51 // ESLint class options
52 resolvePluginsRelativeTo: __dirname,
53 cwd: path.resolve('.'),
54 cache: true,
55 }),
56 (warningLogs || errorLogs) &&
57 new ForkTsCheckerWebpackPlugin({
58 typescript: {
59 memoryLimit: 4096,
61 async: true,
62 formatter: 'basic',
63 issue: {
64 include: (issue) => {
65 if (warningLogs && issue.severity === 'warning') {
66 return true;
68 if (errorLogs && issue.severity === 'error') {
69 return true;
71 return false;
74 }),
75 ]),
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)
84 * */
85 isRelease &&
86 new SentryCliPlugin({
87 include: './dist',
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,
95 }),
97 new CopyWebpackPlugin({
98 patterns: [
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({
121 patterns: [
122 { from: 'src/.htaccess' },
123 // Fix max file limit if the folder does not exist
124 fs.existsSync('public') && { from: 'public', noErrorOnMissing: true },
125 ].filter(Boolean),
128 new MiniCssExtractPlugin({
129 filename: cssName,
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,
142 lang: 'en-US',
144 inject: 'body',
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,
155 minifyJS: true,
156 minifyCSS: true,
157 minifyURLs: true,
161 new FaviconsWebpackPlugin({
162 logo: path.resolve(faviconConfig.logo),
163 logoMaskable: path.resolve(faviconConfig.logoMaskable),
164 cache: path.resolve('./node_modules/.cache'),
165 favicons: {
166 version: buildData.version,
167 ...defaultFaviconConfig,
168 ...faviconConfig.favicons,
172 ...(writeSRI
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;
186 return tag;
189 : []),
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')) {
197 return null;
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';
206 return tag;
209 new RetryChunkLoadPlugin({
210 cacheBust: `function() {
211 return Date.now();
213 retryDelay: 5000,
214 maxRetries: 3,
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(),
226 ].filter(Boolean);