siegel
Version:
Web application development ecosystem
257 lines (256 loc) • 10.3 kB
JavaScript
//TODO?: console output: checkboxes, progress, timings
import path from 'path';
import { LOC_NAMES, PATHS } from '../core/constants.js';
import getConfig from '../core/get_config.js';
import siegel, { nodeUtils, utils } from '../core/index.js';
import initProject from './init_project.js';
import initMiniProject from './init_minimal.js';
import createSSLCerts from './create_SSL.js';
const { globalNodeModulesPath, requireJSON, parseCommandLineArgs } = nodeUtils;
const getColored = (color, str) => `\x1b[${color}m${str}\x1b[0m`;
const getColoredCommandStr = getColored.bind(null, 36);
const getColoredCommandArgumentStr = getColored.bind(null, 32);
const getColoredHighlightText = getColored.bind(null, 33);
const resolvePath = (_path) => path.isAbsolute(_path) ? _path : `${PATHS.cwd}/${_path}`;
const DEFAULT_CONFIG = getConfig();
const COMMANDS_TREE = {
run: {
description: 'Builds client and runs dev server with client watch mode enabled.',
example: (command, { client, server, port }) => (`siegel ${command} ${client.flagLong} app.ts ${server.flagLong} server.ts ${port.flagLong} 4000`),
prepareResult: () => ({
config: DEFAULT_CONFIG
}),
commandAction({ result }) {
siegel(result.config);
},
params: [
{
flagLong: '--production',
flag: '-p',
description: 'Production mode.',
defaultValue: false,
paramAction({ result }) {
result.config.runMode.isProd = true;
}
},
{
flagLong: '--build-only',
flag: '-b',
description: 'Builds client with no static server enabled.',
defaultValue: false,
paramAction({ result }) {
result.config.runMode.isServer = false;
}
},
{
flagLong: '--serv-only',
flag: '-s',
description: 'Serves built client.',
defaultValue: false,
paramAction({ result }) {
result.config.runMode.isBuild = false;
}
},
{
flagLong: '--config',
description: 'Path to Siegel config.',
async paramAction({ value, result }) {
const resolvedPath = resolvePath(value);
const config = value.endsWith('.json')
? requireJSON(resolvedPath)
: (await import(resolvedPath)).default;
result.config = getConfig(config);
}
},
{
flagLong: '--eslint',
flag: '-l',
description: 'Enables lintng with ESLint.',
defaultValue: DEFAULT_CONFIG.build.eslint,
paramAction({ result }) {
result.config.build.eslint = true;
}
},
{
flagLong: '--resolve-globals',
flag: '-g',
description: 'Enable resolve global node modules imports.',
defaultValue: false,
paramAction({ result }) {
result.config.build.postProcessWebpackConfig = webpackConfig => {
webpackConfig.resolve.modules.push(globalNodeModulesPath());
return webpackConfig;
};
}
},
{
flagLong: '--client',
flag: '-C',
description: 'Path to client app entrypoint.',
defaultValue: DEFAULT_CONFIG.build.input.js,
paramAction({ value, result }) {
result.config.build.input.js = resolvePath(value);
}
},
{
flagLong: '--server',
flag: '-S',
description: 'Path to server app entrypoint.',
async paramAction({ value, result }) {
const appServer = await import(resolvePath(value));
result.config.server.appServer = appServer.default;
}
},
{
flagLong: '--port',
description: 'Dev server port.',
defaultValue: DEFAULT_CONFIG.server.port,
paramAction({ value, result }) {
result.config.server.port = +value;
}
},
{
flagLong: '--host',
description: 'Dev server host.',
defaultValue: DEFAULT_CONFIG.server.host,
paramAction({ value, result }) {
result.config.server.host = value;
}
}
]
},
init: {
description: `Creates production ready project with predefined folder structure including already configured Siegel.
\r\tModifies existing ${LOC_NAMES.PACKAGE_JSON} or creates new one.
\r\tMore about demo project read here: ${getColoredHighlightText(`https://github.com/CyberCookie/siegel/tree/master/${LOC_NAMES.DEMO_APP_DIR_NAME}`)}`,
example: true,
commandAction({ result }) {
const { isGlobal, isMini, isMiniServ } = result;
isMini
? initMiniProject(isMiniServ)
: initProject(isGlobal);
},
prepareResult: () => ({
isGlobal: false,
isMini: false,
isMiniServ: false
}),
params: [
{
flagLong: '--global',
flag: '-g',
defaultValue: false,
description: 'Updates Siegel related paths to global.',
paramAction({ result }) {
result.isGlobal = true;
}
},
{
flagLong: '--mini',
flag: '-m',
defaultValue: false,
description: 'Creates mini zero-config react TS project',
paramAction({ result }) {
result.isMini = true;
}
},
{
flagLong: '--mini-serv',
flag: '-s',
defaultValue: false,
description: 'Creates mini zero-config react TS project with preconfigured TS express server',
paramAction({ result }) {
result.isMini = true;
result.isMiniServ = true;
}
}
]
},
'create-ssl': {
example: true,
description: `Creates localhost ssl certificate to be used with NodeJS server;
\r\tCreates authority certificate to be imported in a web browser for testing purposes.`,
commandAction() {
createSSLCerts();
}
},
version: {
description: 'Prints current Siegel version.',
commandAction() {
console.log(requireJSON(PATHS.packageJSON).version);
}
}
};
const CLI_ARGS = process.argv.slice(2);
const COMMAND = CLI_ARGS.shift();
const commandConfig = COMMANDS_TREE[COMMAND];
if (commandConfig) {
const { commandAction } = commandConfig;
const { params, prepareResult } = commandConfig;
const result = prepareResult?.();
const parseResult = parseCommandLineArgs(CLI_ARGS);
const { CLIParamsValues } = parseResult;
let { unresolvedParamsCount } = parseResult;
for await (const param of params) {
const { flag, flagLong, paramAction } = param;
if (paramAction) {
const paramValueData = CLIParamsValues[flagLong] || CLIParamsValues[flag];
if (paramValueData) {
paramValueData.resolved = true;
unresolvedParamsCount--;
await paramAction({
result: result,
CLIParamsValues,
value: paramValueData.value
});
}
}
}
if (unresolvedParamsCount) {
const notSupportedParams = [];
Object.entries(CLIParamsValues)
.forEach(([CLIParam, CLIParamValue]) => {
CLIParamValue.resolved || notSupportedParams.push(CLIParam);
});
if (notSupportedParams.length) {
throw Error(`
\rCommand ${getColoredCommandStr(COMMAND)} doesn't support following arguments: ${getColoredCommandArgumentStr(notSupportedParams.join(' '))}
`);
}
}
commandAction({ CLIParamsValues, result });
}
else {
COMMAND && console.log(`Command ${getColoredCommandStr(COMMAND)} doesn't exist.\n`);
Object.entries(COMMANDS_TREE)
.forEach(([commandConfigKey, COMMAND]) => {
const { description, example } = COMMAND;
const { params } = COMMAND;
console.log(`\n ${getColoredCommandStr(commandConfigKey)} - ${description}`);
const flagsMap = {};
params?.forEach(paramConfg => {
const { description, defaultValue, flag, flagLong } = paramConfg;
let logString = '\n\t';
flag && (logString += getColoredCommandArgumentStr(flag));
flag && flagLong && (logString += ' ');
flagLong && (logString += getColoredCommandArgumentStr(flagLong));
logString += ` - ${description}`;
if (utils.isExists(defaultValue)) {
logString += `\n\r\t\t${getColoredHighlightText(` Default value: ${defaultValue}`)}`;
}
console.log(logString);
flagsMap[flagLong.substring(2)] = { flag, flagLong };
});
if (example) {
const exampleType = typeof example;
const logString = exampleType == 'function'
? example(commandConfigKey, flagsMap)
: exampleType == 'string'
? example
: `siegel ${commandConfigKey}`;
console.log(`\n\tExample: ${getColoredHighlightText(logString)}\n`);
}
});
console.log('\n');
}