i18n: Upgrade translations from crowdin (61e08dd5). (pass-desktop)
[ProtonMail-WebClient.git] / packages / pack / webpack.config.ts
blob63246f656584622f1724b102ee3ba23ea9d94fac
1 import path from 'path';
2 import { Configuration } from 'webpack';
3 import 'webpack-dev-server';
4 // @ts-ignore
5 import { parseResource } from 'webpack/lib/util/identifier';
7 import { getEntries } from './webpack/entries';
9 const getCssLoaders = require('./webpack/css.loader');
10 const getAssetsLoaders = require('./webpack/assets.loader');
11 const getPlugins = require('./webpack/plugins');
12 const getOptimizations = require('./webpack/optimization');
14 const getConfig = (env: any): Configuration => {
15     const isProduction = process.env.NODE_ENV === 'production';
16     const isRelease = !!process.env.CI_COMMIT_TAG;
18     // This folder is separate from the assets folder because they are special assets which get served through
19     // a long-term storage
20     const assetsFolder = 'assets/static';
22     const { getJsLoaders } = require(env.webpackOnCaffeine ? './webpack/js.loader.swc' : './webpack/js.loader');
24     const defaultBrowsersList = isProduction
25         ? `> 0.5%, not IE 11, Firefox ESR, Safari 14, iOS 14, Chrome 80`
26         : 'last 1 chrome version, last 1 firefox version, last 1 safari version';
28     const options = {
29         isProduction,
30         isRelease,
31         publicPath: env.publicPath || '/',
32         api: env.api,
33         appMode: env.appMode || 'standalone',
34         webpackOnCaffeine: env.webpackOnCaffeine,
35         featureFlags: env.featureFlags || '',
36         writeSRI: env.writeSri !== 'false',
37         browserslist: env.browserslist ?? defaultBrowsersList,
38         buildData: {
39             version: env.version,
40             commit: env.commit,
41             branch: env.branch,
42             date: env.date,
43             mode: env.appMode,
44         },
45         warningLogs: env.warningLogs || false,
46         errorLogs: env.errorLogs || false,
47         overlayWarnings: env.overlayWarnings || false,
48         overlayErrors: env.overlayErrors || false,
49         overlayRuntimeErrors: env.overlayRuntimeErrors || false,
50         logical: env.logical || false,
51         analyze: env.analyze || false,
52     };
54     const version = options.buildData.version;
56     return {
57         target: `browserslist:${options.browserslist}`,
58         mode: isProduction ? 'production' : 'development',
59         bail: isProduction,
60         devtool: isProduction ? 'source-map' : 'cheap-module-source-map',
61         watchOptions: {
62             ignored: /dist|node_modules|locales|\.(gif|jpeg|jpg|ico|png|svg)/,
63             aggregateTimeout: 600,
64         },
65         resolve: {
66             extensions: ['.js', '.tsx', '.ts'],
67             fallback: {
68                 crypto: false,
69                 buffer: false,
70                 stream: false,
71                 iconv: false,
72                 path: false,
73                 punycode: false,
74             },
75         },
76         experiments: { asyncWebAssembly: true },
77         entry: getEntries(),
78         output: {
79             filename: isProduction
80                 ? `${assetsFolder}/[name].[contenthash:8].js?v=${version}`
81                 : `${assetsFolder}/[name].js?v=${version}`,
82             publicPath: options.publicPath,
83             chunkFilename: (pathData) => {
84                 const result = isProduction
85                     ? `${assetsFolder}/[name].[contenthash:8].chunk.js?v=${version}`
86                     : `${assetsFolder}/[name].chunk.js?v=${version}`;
87                 const chunkName = pathData?.chunk?.name;
88                 if (chunkName && (chunkName.startsWith('date-fns/') || chunkName.startsWith('locales/'))) {
89                     // @ts-ignore
90                     const strippedChunkName = chunkName.replaceAll(/-index-js|-json/g, '');
91                     return result.replace('[name]', strippedChunkName);
92                 }
93                 // Drive need static URL for transpiled SW
94                 if (chunkName && chunkName.startsWith('downloadSW')) {
95                     return `[name].js?v=${version}`;
96                 }
97                 return result;
98             },
99             assetModuleFilename: (data) => {
100                 const { path: file } = parseResource(data?.filename || '');
101                 const ext = path.extname(file);
102                 const base = path.basename(file);
103                 const name = base.slice(0, base.length - ext.length);
104                 if (name.includes('.var')) {
105                     const replacedNamed = name.replace('.var', '-var');
106                     return `${assetsFolder}/${replacedNamed}.[hash][ext]?v=${version}`;
107                 }
108                 return `${assetsFolder}/[name].[hash][ext]?v=${version}`;
109             },
110             crossOriginLoading: 'anonymous',
111         },
112         module: {
113             strictExportPresence: true, // Make missing exports an error instead of warning
114             rules: [...getJsLoaders(options), ...getCssLoaders(options), ...getAssetsLoaders()],
115         },
116         plugins: getPlugins({
117             ...options,
118             cssName: isProduction
119                 ? `${assetsFolder}/[name].[contenthash:8].css?v=${version}`
120                 : `${assetsFolder}/[name].css?v=${version}`,
121         }),
122         optimization: getOptimizations(options),
123         devServer: {
124             hot: !isProduction,
125             devMiddleware: {
126                 stats: 'minimal',
127                 publicPath: options.publicPath,
128             },
129             allowedHosts: 'all',
130             compress: true,
131             historyApiFallback: {
132                 index: options.publicPath,
133             },
134             client: {
135                 webSocketURL: 'auto://0.0.0.0:0/ws',
136                 overlay: {
137                     warnings: options.overlayWarnings,
138                     errors: options.overlayErrors,
139                     runtimeErrors: options.overlayRuntimeErrors,
140                 },
141             },
142             webSocketServer: 'ws',
143             ...(options.api && {
144                 proxy: [
145                     {
146                         context: ['/api', '/internal-api'],
147                         target: options.api,
148                         secure: false,
149                         changeOrigin: true,
150                         onProxyRes: (proxyRes) => {
151                             delete proxyRes.headers['content-security-policy'];
152                             delete proxyRes.headers['x-frame-options'];
153                             proxyRes.headers['set-cookie'] = proxyRes.headers['set-cookie']?.map((cookies) =>
154                                 cookies
155                                     .split('; ')
156                                     .filter((cookie) => {
157                                         return !/(secure$|samesite=|domain=)/i.test(cookie);
158                                     })
159                                     .join('; ')
160                             );
161                         },
162                     },
163                 ],
164             }),
165         },
166     };
169 export default getConfig;