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('--inline-icons', false)
35 .option('--webpackOnCaffeine', '', false)
36 .option('--logical', '', false)
38 '--publicPath <publicPath>',
41 if (publicPath
&& (!publicPath
.startsWith('/') || !publicPath
.endsWith('/'))) {
42 throw new Error('--publicPath must start and end with a forward slash');
44 return publicPath
|| '/';
50 const getWebpackArgs
= (options
, env
, { appData
, buildData
}) => {
52 api
: appData
.api
=== '/api' ? undefined : appData
.api
,
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
,
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') {
75 return `${acc} --env ${key}`;
81 return `${acc} --env ${key}=${value.replace(/ /g, '\\ ')}`;
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}`,
109 await
commandWithLog(`${path.resolve(__dirname, `../scripts/validate.sh`)} ${outputPath}`, {
112 const dotFiles
= await Promise
.all(
113 ['.htaccess', '.well-known'].map(async (file
) => {
115 await fs
.access(`${outputPath}/${file}`);
120 await
commandWithLog(`tar -czvf ../webapp-bundle.tar.gz * ${dotFiles.filter(Boolean).join(' ')} 2> /dev/null`, {
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}`,
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
);