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
) => {
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('--webpackOnCaffeine', '', false)
35 .option('--logical', '', false)
37 '--publicPath <publicPath>',
40 if (publicPath
&& (!publicPath
.startsWith('/') || !publicPath
.endsWith('/'))) {
41 throw new Error('--publicPath must start and end with a forward slash');
43 return publicPath
|| '/';
49 const getWebpackArgs
= (options
, env
, { appData
, buildData
}) => {
51 api
: appData
.api
=== '/api' ? undefined : appData
.api
,
53 appMode
: options
.appMode
,
54 publicPath
: options
.publicPath
=== '/' ? undefined : options
.publicPath
,
55 featureFlags
: options
.featureFlags
,
56 writeSri
: options
.sri
? undefined : options
.sri
,
57 warningLogs
: options
.warningLogs
,
58 errorLogs
: options
.errorLogs
,
59 overlayWarnings
: options
.overlayWarnings
,
60 overlayErrors
: options
.overlayErrors
,
61 overlayRuntimeErrors
: options
.overlayRuntimeErrors
,
62 logical
: Boolean(options
.logical
),
63 webpackOnCaffeine
: Boolean(options
.webpackOnCaffeine
),
64 analyze
: options
.analyze
,
67 const extraWebpackArgs
= env
.args
.join(' ');
68 const webpackEnvArgs
= Object
.entries(envArgs
)
69 .filter(([, value
]) => value
!== undefined && value
!== '')
70 .reduce((acc
, [key
, value
]) => {
71 if (typeof value
=== 'boolean') {
73 return `${acc} --env ${key}`;
79 return `${acc} --env ${key}=${value.replace(/ /g, '\\ ')}`;
82 return `${webpackEnvArgs} ${extraWebpackArgs}`;
85 const commandWithLog
= (...args
) => {
86 console
.log(chalk
.cyan(args
[0]), `\n`);
87 return execa
.command(...args
);
90 addGlobalOptions(program
.command('build').description('create an optimized production build'))
91 .option('--no-sri', 'disable sri')
92 .action(async (options
, env
) => {
93 console
.log(chalk
.magenta('Creating a production build...\n'));
95 const configData
= getConfigData(options
);
96 await
writeConfig(getConfigFile(configData
));
98 const webpackArgs
= getWebpackArgs(options
, env
, configData
);
99 const outputPath
= path
.resolve('./dist');
100 await
commandWithLog(`rm -rf ${outputPath}`);
101 await
commandWithLog(
102 `${require.resolve('webpack-cli/bin/cli.js')} --progress --output-path=${outputPath} ${webpackArgs}`,
107 await
commandWithLog(`${path.resolve(__dirname, `../scripts/validate.sh`)} ${outputPath}`, {
110 const dotFiles
= await Promise
.all(
111 ['.htaccess', '.well-known'].map(async (file
) => {
113 await fs
.access(`${outputPath}/${file}`);
118 await
commandWithLog(`tar -czvf ../webapp-bundle.tar.gz * ${dotFiles.filter(Boolean).join(' ')} 2> /dev/null`, {
125 addGlobalOptions(program
.command('dev-server').description('run locally'))
126 .option('--port <port>', '')
127 .option('--warning-logs', 'emit typescript and eslint warnings')
128 .option('--no-error-logs', 'do not emit typescript and eslint errors')
129 .option('--overlay-warnings', 'show a full screen overlay when there are compiler warnings')
130 .option('--overlay-runtime-errors', 'show a full screen overlay when there are runtime errors')
131 .option('--overlay-errors', 'show a full screen overlay when there are compiler errors')
132 .action(async (options
, env
) => {
133 console
.log(chalk
.magenta('Starting development server...\n'));
135 const configData
= getConfigData(options
);
136 await
writeConfig(getConfigFile(configData
));
138 const port
= await
getPort(options
.port
|| 8080);
140 const webpackArgs
= getWebpackArgs(options
, env
, configData
);
141 await
commandWithLog(
142 `${require.resolve('webpack-cli/bin/cli.js')} serve --progress --port=${port} ${webpackArgs}`,
149 addGlobalOptions(program
.command('config').description('write config'))
150 .option('--version <version>', 'override the default (based on the tag) version number')
151 .action(async (options
) => {
152 await
writeConfig(getConfigFile(getConfigData(options
)));
155 program
.parse(process
.argv
);