From 44689f640ae2200a6b0ec0bccbf66342e23a9417 Mon Sep 17 00:00:00 2001 From: Edie Lemoine Date: Mon, 16 Oct 2023 14:27:03 +0200 Subject: [PATCH] feat(app-builder): allow passing custom root command via command line --- .../app-builder/src/commands/upgrade/types.ts | 5 ++- apps/app-builder/src/run.ts | 39 ++++++++++++------- apps/app-builder/src/types/command.ts | 2 - apps/app-builder/src/types/config.ts | 5 +++ apps/app-builder/src/utils/createCommand.ts | 9 +++++ .../app-builder/src/utils/createWithConfig.ts | 2 +- apps/app-builder/src/utils/executeCommand.ts | 11 ++++-- apps/app-builder/src/utils/index.ts | 1 + .../src/utils/mergeDefaultConfig.ts | 1 + apps/app-builder/src/utils/registerCommand.ts | 2 +- apps/app-builder/src/utils/resolveConfig.ts | 19 ++++++--- 11 files changed, 70 insertions(+), 26 deletions(-) create mode 100644 apps/app-builder/src/utils/createCommand.ts diff --git a/apps/app-builder/src/commands/upgrade/types.ts b/apps/app-builder/src/commands/upgrade/types.ts index cb4282a1c..c7757f9c7 100644 --- a/apps/app-builder/src/commands/upgrade/types.ts +++ b/apps/app-builder/src/commands/upgrade/types.ts @@ -20,7 +20,10 @@ export type UpgradeCommandArgs = CommandArgs & { lockfile?: string; }; -export type InputUpgradeCommandArgs = MakeOptional; +export type InputUpgradeCommandArgs = MakeOptional< + UpgradeCommandArgs, + 'composerCommand' | 'yarnCommand' | 'rootCommand' +>; export interface UpgradeSubContext { args: UpgradeCommandArgs; diff --git a/apps/app-builder/src/run.ts b/apps/app-builder/src/run.ts index b566d7b86..979915802 100644 --- a/apps/app-builder/src/run.ts +++ b/apps/app-builder/src/run.ts @@ -132,6 +132,10 @@ const ALL_BULK_COMMANDS = [ [COMMAND_BUILD_NAME, BUILD_COMMANDS], ] as const; +const BULK_COMMAND_OPTIONS = [OPTION_DRY_RUN, OPTION_PARALLEL, OPTION_VERBOSITY, OPTION_QUIET, OPTION_VERSION] as const; + +const CONFIG_OPTIONS = [['--root-command ', 'Root command', '']] as const; + // eslint-disable-next-line max-lines-per-function export const run = (env: LiftoffEnv, argv: string[]): void => { const withContext = createWithContext(env, argv); @@ -141,22 +145,30 @@ export const run = (env: LiftoffEnv, argv: string[]): void => { WITHOUT_CONFIG_COMMANDS.forEach((definition) => registerCommand(definition, withContext)); - CONFIG_COMMANDS.forEach((definition) => registerCommand(definition, withConfig)); + CONFIG_COMMANDS.forEach((definition) => { + registerCommand( + { + ...definition, + options: [...CONFIG_OPTIONS, ...(definition.options ?? [])], + }, + withConfig, + ); + }); ALL_BULK_COMMANDS.forEach(([commandName, commands]) => { - program + const command = program .command(commandName) - .description(`Run ${commands.map(({name}) => name).join(', ')} in sequence. ${REQUIRES_CONFIG_FILE}`) - .option(...OPTION_DRY_RUN) - .option(...OPTION_PARALLEL) - .option(...OPTION_VERBOSITY) - .option(...OPTION_QUIET) - .option(...OPTION_VERSION) - .action(async (...args) => { - for (const command of commands) { - await withConfig(command)(...args); - } - }); + .description(`Run ${commands.map(({name}) => name).join(', ')} in sequence. ${REQUIRES_CONFIG_FILE}`); + + // @ts-expect-error todo + BULK_COMMAND_OPTIONS.forEach((option) => command.option(...option)); + CONFIG_OPTIONS.forEach((option) => command.option(...option)); + + command.action(async (...args) => { + for (const command of commands) { + await withConfig(command)(...args); + } + }); }); void (async () => { @@ -166,6 +178,7 @@ export const run = (env: LiftoffEnv, argv: string[]): void => { config.additionalCommands?.forEach((definition) => { const resolvedDefinition = { ...definition, + options: [...CONFIG_OPTIONS, ...(definition.options ?? [])], action: () => Promise.resolve({default: definition.action}), }; diff --git a/apps/app-builder/src/types/command.ts b/apps/app-builder/src/types/command.ts index 71b7a0002..f9d8c511e 100644 --- a/apps/app-builder/src/types/command.ts +++ b/apps/app-builder/src/types/command.ts @@ -40,8 +40,6 @@ export type WithContextParams = ( context: Omit, 'config'>, ) => Promise | void; -export type WithConfigParams = (context: PdkBuilderContext) => Promise | void; - export type CommandCb = (...args: CommandArguments) => void | Promise; export type CreateHook = ( diff --git a/apps/app-builder/src/types/config.ts b/apps/app-builder/src/types/config.ts index 7fa5f721a..e29086993 100644 --- a/apps/app-builder/src/types/config.ts +++ b/apps/app-builder/src/types/config.ts @@ -103,6 +103,11 @@ export type PdkBuilderConfig = { */ composerCommand?: OneOrMore; + /** + * Command to prepend to all commands. + */ + rootCommand?: OneOrMore; + /** * Translations configuration. */ diff --git a/apps/app-builder/src/utils/createCommand.ts b/apps/app-builder/src/utils/createCommand.ts new file mode 100644 index 000000000..51d069809 --- /dev/null +++ b/apps/app-builder/src/utils/createCommand.ts @@ -0,0 +1,9 @@ +import {type OneOrMore, toArray} from '@myparcel/ts-utils'; +import {type PdkBuilderConfig} from '../types'; + +export const createCommand = (config: PdkBuilderConfig, command: OneOrMore): string => { + const resolvedRootCommand = toArray(config.rootCommand ?? []); + const resolvedCommand = toArray(command ?? []); + + return [...resolvedRootCommand, ...resolvedCommand].filter(Boolean).join(' '); +}; diff --git a/apps/app-builder/src/utils/createWithConfig.ts b/apps/app-builder/src/utils/createWithConfig.ts index 9fdd81442..91582b2c1 100644 --- a/apps/app-builder/src/utils/createWithConfig.ts +++ b/apps/app-builder/src/utils/createWithConfig.ts @@ -7,8 +7,8 @@ import {mergeDefaultConfig} from './mergeDefaultConfig'; export const createWithConfig: CreateHook = (env) => { return (definition) => { return async (...args) => { - const config = await resolveConfig(env); const {command, context} = await parseCommandInput(definition, args, env); + const config = await resolveConfig(env, context.args); const commandName = definition.name; diff --git a/apps/app-builder/src/utils/executeCommand.ts b/apps/app-builder/src/utils/executeCommand.ts index 27f1c5c65..c0b0a8d7d 100644 --- a/apps/app-builder/src/utils/executeCommand.ts +++ b/apps/app-builder/src/utils/executeCommand.ts @@ -1,7 +1,8 @@ import {spawnSync, type SpawnSyncOptions, type SpawnSyncOptionsWithStringEncoding} from 'child_process'; -import {type OneOrMore, toArray} from '@myparcel/ts-utils'; -import {type ExecuteCommandContext} from '../types'; +import {isOfType, type OneOrMore, toArray} from '@myparcel/ts-utils'; +import {type ExecuteCommandContext, type PdkBuilderContext} from '../types'; import {VerbosityLevel} from '../constants'; +import {createCommand} from './createCommand'; export const executeCommand = async ( context: ExecuteCommandContext, @@ -16,7 +17,11 @@ export const executeCommand = async ( }; return new Promise((resolve, reject) => { - const splitCommand = toArray(command).reduce((acc, command) => [...acc, ...command.split(' ')], [] as string[]); + const resolvedCommand = isOfType(context, 'config') + ? toArray(createCommand(context.config, command)) + : toArray(command); + + const splitCommand = resolvedCommand.reduce((acc, command) => [...acc, ...command.split(' ')], [] as string[]); const allArgs = [...splitCommand, ...(args ?? [])]; const [commandName, ...commandArgs] = allArgs; diff --git a/apps/app-builder/src/utils/index.ts b/apps/app-builder/src/utils/index.ts index de97ad47d..6ee33fa92 100644 --- a/apps/app-builder/src/utils/index.ts +++ b/apps/app-builder/src/utils/index.ts @@ -1,4 +1,5 @@ export * from './createArchive'; +export * from './createCommand'; export * from './createDebugger'; export * from './createWithConfig'; export * from './createWithContext'; diff --git a/apps/app-builder/src/utils/mergeDefaultConfig.ts b/apps/app-builder/src/utils/mergeDefaultConfig.ts index 3e25fbe40..993e0a1d6 100644 --- a/apps/app-builder/src/utils/mergeDefaultConfig.ts +++ b/apps/app-builder/src/utils/mergeDefaultConfig.ts @@ -13,6 +13,7 @@ export const mergeDefaultConfig = (config: PdkBuilderConfig): ResolvedPdkBuilder nodePackageManagerCommand: undefined, outDir: 'dist', platformFolderName: '{{platform}}-{{name}}', + rootCommand: '', version: '0.0.0', yarnCommand: undefined, ...config, diff --git a/apps/app-builder/src/utils/registerCommand.ts b/apps/app-builder/src/utils/registerCommand.ts index 3a0c0eb94..12ce9e53d 100644 --- a/apps/app-builder/src/utils/registerCommand.ts +++ b/apps/app-builder/src/utils/registerCommand.ts @@ -4,7 +4,7 @@ import {type CommandCb, type CommandDefinition} from '../types'; export const registerCommand = ( definition: CommandDefinition, callback: (definition: CommandDefinition) => CommandCb, -) => { +): void => { const command = program.command(definition.name).description(definition.description); if (definition.options) { diff --git a/apps/app-builder/src/utils/resolveConfig.ts b/apps/app-builder/src/utils/resolveConfig.ts index 525637b6f..9994bd5c4 100644 --- a/apps/app-builder/src/utils/resolveConfig.ts +++ b/apps/app-builder/src/utils/resolveConfig.ts @@ -1,20 +1,29 @@ import {type LiftoffEnv} from 'liftoff'; -import {type PdkBuilderConfig, type ResolvedPdkBuilderConfig} from '../types'; +import {type AnyCommandArgs, type PdkBuilderConfig, type ResolvedPdkBuilderConfig} from '../types'; +import {type ParsedCommand} from './parseCommand'; const configCache = new Map(); -export async function resolveConfig(env: LiftoffEnv): Promise { +export async function resolveConfig( + env: LiftoffEnv, + args?: ParsedCommand, +): Promise { if (!env.configPath) { throw new Error('No config file found.'); } if (configCache.has(env.configPath)) { - return configCache.get(env.configPath) as ResolvedPdkBuilderConfig; + return {...configCache.get(env.configPath), ...args} as ResolvedPdkBuilderConfig; } const imported = await import(env.configPath); - configCache.set(env.configPath, imported.default); + const resolvedConfig = { + ...imported.default, + ...args, + }; - return imported.default; + configCache.set(env.configPath, resolvedConfig); + + return resolvedConfig; }