Merge branch 'renovate/all-minor-patch' into 'main'
[ProtonMail-WebClient.git] / packages / pack / bin / protonPack.js
blob3c5ddf419086b0ed5ce12a327194acac2d4b83ba
1 #!/usr/bin/env node
2 const fs = require('fs').promises;
3 const execa = require('execa');
4 const path = require('path');
5 const { Command } = require('commander');
6 const portfinder = require('portfinder');
7 const chalk = require('chalk');
9 const program = new Command();
11 const { getConfigData, getApi, getConfigFile } = require('../lib/config');
13 const getPort = (basePort) => {
14 portfinder.basePort = basePort;
15 return portfinder.getPortPromise();
18 const writeConfig = async (configFile) => {
19 const configPath = path.resolve('./src/app/config.ts');
20 console.log(`writing file ${configPath}`);
22 await fs.mkdir(path.dirname(configPath), { recursive: true });
23 await fs.writeFile(configPath, configFile);
26 const addGlobalOptions = (program) => {
27 return program
28 .option('--appMode <appMode>', '')
29 .option('--analyze', '')
30 .option('--featureFlags <featureFlags>', '')
31 .option('--api <api>', '', (api) => getApi(api), getApi(''))
32 .option('--sso <sso>', '')
33 .option('--no-api-proxy', '')
34 .option('--inline-icons', false)
35 .option('--webpackOnCaffeine', '', false)
36 .option('--logical', '', false)
37 .option(
38 '--publicPath <publicPath>',
39 '',
40 (publicPath) => {
41 if (publicPath && (!publicPath.startsWith('/') || !publicPath.endsWith('/'))) {
42 throw new Error('--publicPath must start and end with a forward slash');
44 return publicPath || '/';
46 '/'
50 const getWebpackArgs = (options, env, { appData, buildData }) => {
51 const envArgs = {
52 api: appData.api === '/api' ? undefined : appData.api,
53 sso: appData.sso,
54 appMode: options.appMode,
55 publicPath: options.publicPath === '/' ? undefined : options.publicPath,
56 featureFlags: options.featureFlags,
57 writeSri: options.sri ? undefined : options.sri,
58 inlineIcons: options.inlineIcons,
59 warningLogs: options.warningLogs,
60 errorLogs: options.errorLogs,
61 overlayWarnings: options.overlayWarnings,
62 overlayErrors: options.overlayErrors,
63 overlayRuntimeErrors: options.overlayRuntimeErrors,
64 logical: Boolean(options.logical),
65 webpackOnCaffeine: Boolean(options.webpackOnCaffeine),
66 analyze: options.analyze,
67 ...buildData,
69 const extraWebpackArgs = env.args.join(' ');
70 const webpackEnvArgs = Object.entries(envArgs)
71 .filter(([, value]) => value !== undefined && value !== '')
72 .reduce((acc, [key, value]) => {
73 if (typeof value === 'boolean') {
74 if (value) {
75 return `${acc} --env ${key}`;
76 } else {
77 return acc;
81 return `${acc} --env ${key}=${value.replace(/ /g, '\\ ')}`;
82 }, '');
84 return `${webpackEnvArgs} ${extraWebpackArgs}`;
87 const commandWithLog = (...args) => {
88 console.log(chalk.cyan(args[0]), `\n`);
89 return execa.command(...args);
92 addGlobalOptions(program.command('build').description('create an optimized production build'))
93 .option('--no-sri', 'disable sri')
94 .action(async (options, env) => {
95 console.log(chalk.magenta('Creating a production build...\n'));
97 const configData = getConfigData(options);
98 await writeConfig(getConfigFile(configData));
100 const webpackArgs = getWebpackArgs(options, env, configData);
101 const outputPath = path.resolve('./dist');
102 await commandWithLog(`rm -rf ${outputPath}`);
103 await commandWithLog(
104 `${require.resolve('webpack-cli/bin/cli.js')} --progress --output-path=${outputPath} ${webpackArgs}`,
106 stdio: 'inherit',
109 await commandWithLog(`${path.resolve(__dirname, `../scripts/validate.sh`)} ${outputPath}`, {
110 stdio: 'inherit',
112 const dotFiles = await Promise.all(
113 ['.htaccess', '.well-known'].map(async (file) => {
114 try {
115 await fs.access(`${outputPath}/${file}`);
116 return file;
117 } catch {}
120 await commandWithLog(`tar -czvf ../webapp-bundle.tar.gz * ${dotFiles.filter(Boolean).join(' ')} 2> /dev/null`, {
121 stdio: 'inherit',
122 cwd: outputPath,
123 shell: true,
127 addGlobalOptions(program.command('dev-server').description('run locally'))
128 .option('--port <port>', '')
129 .option('--warning-logs', 'emit typescript and eslint warnings')
130 .option('--no-error-logs', 'do not emit typescript and eslint errors')
131 .option('--overlay-warnings', 'show a full screen overlay when there are compiler warnings')
132 .option('--overlay-runtime-errors', 'show a full screen overlay when there are runtime errors')
133 .option('--overlay-errors', 'show a full screen overlay when there are compiler errors')
134 .action(async (options, env) => {
135 console.log(chalk.magenta('Starting development server...\n'));
137 const configData = getConfigData(options);
138 await writeConfig(getConfigFile(configData));
140 const port = await getPort(options.port || 8080);
142 const webpackArgs = getWebpackArgs(options, env, configData);
143 await commandWithLog(
144 `${require.resolve('webpack-cli/bin/cli.js')} serve --progress --port=${port} ${webpackArgs}`,
146 stdio: 'inherit',
151 addGlobalOptions(program.command('config').description('write config'))
152 .option('--version <version>', 'override the default (based on the tag) version number')
153 .action(async (options) => {
154 await writeConfig(getConfigFile(getConfigData(options)));
157 program.parse(process.argv);