Skip to content

Commit

Permalink
feat(app-builder): improve error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
EdieLemoine committed Nov 7, 2024
1 parent 340f12a commit c5bd584
Show file tree
Hide file tree
Showing 20 changed files with 214 additions and 125 deletions.
4 changes: 2 additions & 2 deletions apps/app-builder/src/__tests__/createTestContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import {type RecursivePartial} from '@myparcel/ts-utils';
import {mergeDefaultConfig} from '../utils/mergeDefaultConfig';
import {createDebugger} from '../utils/command/createDebugger';
import {type PdkBuilderConfig} from '../types/config.types';
import {type CommandArgs, type PdkBuilderContext} from '../types/command.types';
import {type DefaultCommandArgs, type PdkBuilderContext} from '../types/command.types';
import {PdkPlatformName} from '../constants';
import {MOCK_ROOT_DIR} from './constants';

export const createTestContext = <Context extends PdkBuilderContext = PdkBuilderContext>(
context?: RecursivePartial<Context>,
): Context => {
const args: CommandArgs = {
const args: DefaultCommandArgs = {
arguments: [],
dryRun: false,
parallel: false,
Expand Down
6 changes: 3 additions & 3 deletions apps/app-builder/src/commands/increment/increment.types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {type Debugger} from 'debug';
import {type PdkBuilderConfig} from '../../types/config.types';
import {type CommandArgs} from '../../types/command.types';
import {type DefaultCommandArgs} from '../../types/command.types';

export type IncrementCommandArgs = CommandArgs & {
export type IncrementCommandArgs = DefaultCommandArgs & {
version?: string;
};

Expand Down Expand Up @@ -42,7 +42,7 @@ export interface VersionReplacerInput<T extends VersionSource = VersionSource> {

export interface VersionReplacerContext {
config: PdkBuilderConfig;
args: CommandArgs;
args: DefaultCommandArgs;
debug: Debugger;
}

Expand Down
4 changes: 2 additions & 2 deletions apps/app-builder/src/commands/upgrade/upgrade.types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {type CommandArgs, type PdkBuilderContext} from '../../types/command.types';
import {type DefaultCommandArgs, type PdkBuilderContext} from '../../types/command.types';
import {type UpgradeMode} from './enums';

export type UpgradeCommandArgs = CommandArgs & {
export type UpgradeCommandArgs = DefaultCommandArgs & {
check?: boolean;
commit?: boolean;
commitType?: string;
Expand Down
6 changes: 3 additions & 3 deletions apps/app-builder/src/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {defineCommand} from './utils/defineCommand';
import {defineBulkCommand} from './utils/defineBulkCommand';
import {type CommandDefinition} from './types/command.types';
import {type AnyCommandDefinition} from './types/command.types';
import {type BulkCommandDefinition} from './types/bulkCommand.types';
import {
COMMAND_BUILD_NAME,
Expand Down Expand Up @@ -112,7 +112,7 @@ const CORE_COMMANDS = Object.freeze([
copyCommand,
renameCommand,
transformCommand,
] satisfies CommandDefinition[]);
] satisfies AnyCommandDefinition[]);

export const buildBulkCommand = defineBulkCommand({
name: COMMAND_BUILD_NAME,
Expand Down Expand Up @@ -168,7 +168,7 @@ export const releaseBulkCommand = defineBulkCommand({
commands: [cleanCommand, scopePhpCommand, incrementCommand, ...CORE_COMMANDS, zipCommand],
});

export const ALL_COMMANDS: readonly CommandDefinition[] = Object.freeze([
export const ALL_COMMANDS: readonly AnyCommandDefinition[] = Object.freeze([
initCommand,
cleanCommand,
copyCommand,
Expand Down
29 changes: 17 additions & 12 deletions apps/app-builder/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ import {getBulkCommandDescription} from './utils/getBulkCommandDescription';
import {registerCommand} from './utils/command/registerCommand';
import {createWithContext} from './utils/command/createWithContext';
import {createWithConfig} from './utils/command/createWithConfig';
import {type CommandArgs, type CommandDefinition, type CommandDefinitionWithoutConfig} from './types/command.types';
import {
type AnyCommandDefinition,
type CommandDefinitionWithoutConfig,
type DefaultCommandArgs,
} from './types/command.types';
import {ALL_BULK_COMMANDS, ALL_COMMANDS} from './definitions';
import {BULK_COMMAND_OPTIONS, CONFIG_OPTIONS, TITLE} from './constants';

// eslint-disable-next-line max-lines-per-function
export const run = (env: LiftoffEnv, argv: string[]): void => {
const withContext = createWithContext(env, argv);
const withConfig = createWithConfig(env, argv);
const withContext = createWithContext(env);
const withConfig = createWithConfig(env);

program.name(TITLE).description('Builds a plugin for MyParcel.');

Expand All @@ -29,7 +33,7 @@ export const run = (env: LiftoffEnv, argv: string[]): void => {

ALL_BULK_COMMANDS.forEach(({name, description, commands}) => {
const commandDefinitions = commands.map((definition) => {
return toArray(definition)[0] as CommandDefinition;
return toArray(definition)[0] as AnyCommandDefinition;
});

const command = program.command(name).description(getBulkCommandDescription(commandDefinitions, description));
Expand All @@ -51,7 +55,7 @@ export const run = (env: LiftoffEnv, argv: string[]): void => {
for (const command of commands) {
const [resolvedCommand, commandOptions] = command;

const resolvedArgs: CommandArgs = {...args, ...commandOptions};
const resolvedArgs: DefaultCommandArgs = {...args, ...commandOptions};

await withConfig(resolvedCommand)(resolvedArgs, originalCommand);
}
Expand All @@ -63,13 +67,14 @@ export const run = (env: LiftoffEnv, argv: string[]): void => {
const config = await resolveConfig(env);

config.additionalCommands?.forEach((definition) => {
const resolvedDefinition = {
...definition,
options: [...CONFIG_OPTIONS, ...(definition.options ?? [])],
action: () => Promise.resolve({default: definition.action}),
};

return registerCommand(resolvedDefinition, withConfig);
return registerCommand(
{
...definition,
options: [...CONFIG_OPTIONS, ...(definition.options ?? [])],
action: () => Promise.resolve({default: definition.action}),
},
withConfig,
);
});
}

Expand Down
8 changes: 4 additions & 4 deletions apps/app-builder/src/types/bulkCommand.types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {type CommandDefinition} from './command.types';
import {type AnyCommandDefinition} from './command.types';

type CommandArguments<Cd extends CommandDefinition> = Cd extends CommandDefinition<infer Args> ? Args : never;
type CommandArguments<Cd extends AnyCommandDefinition> = Cd extends AnyCommandDefinition<infer Args> ? Args : never;

export type CommandWithArguments<Command extends CommandDefinition = CommandDefinition> =
export type CommandWithArguments<Command extends AnyCommandDefinition = AnyCommandDefinition> =
| [Command, Partial<CommandArguments<Command>>];

export type CommandWithOrWithoutArguments<Command extends CommandDefinition = CommandDefinition> =
export type CommandWithOrWithoutArguments<Command extends AnyCommandDefinition = AnyCommandDefinition> =
| Command
| CommandWithArguments<Command>;

Expand Down
66 changes: 40 additions & 26 deletions apps/app-builder/src/types/command.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,83 +4,97 @@ import type Liftoff from 'liftoff';
import {type LiftoffEnv} from 'liftoff';
import {type Debugger} from 'debug';
import {type Command} from 'commander';
import {type PromiseOr} from '@myparcel/ts-utils';
import {type PromiseOr, type Replace} from '@myparcel/ts-utils';
import {type CommandName, type PdkPlatformName} from '../constants';
import {type ResolvedPdkBuilderConfig} from './config.types';

export interface BaseCommandDefinition<Args = any> {
export interface BaseCommandDefinition<Args extends AnyCommandArgs = DefaultCommandArgs> {
name: CommandName;
action: (args?: Args & AnyCommandArgs) => Promise<any>;
description: string;
options?: any[];
args?: string[][];

action(): Promise<{default: PdkBuilderCommand<Args>}>;
}

export interface CommandDefinitionWithoutConfig<Args = any> extends BaseCommandDefinition<Args> {
export interface CommandDefinitionWithoutConfig<Args extends AnyCommandArgs = DefaultCommandArgs>
extends BaseCommandDefinition<Args> {
hasConfig?: false;
}

export type CommandDefinition<Args = any> = BaseCommandDefinition<Args> | CommandDefinitionWithoutConfig<Args>;
export type AnyCommandDefinition<Args extends AnyCommandArgs = DefaultCommandArgs> =
| BaseCommandDefinition<Args>
| CommandDefinitionWithoutConfig<Args>;

export type PdkBuilderCommand<Args extends AnyCommandArgs = CommandArgs> = (
context: PdkBuilderContext<Args>,
) => PromiseOr<void>;
export type AdditionalCommandDefinition<Args extends AnyCommandArgs = DefaultCommandArgs> = Replace<
AnyCommandDefinition<Args>,
'action',
PdkBuilderCommand<Args>
>;

export type PdkBuilderCommand<
Args extends AnyCommandArgs = DefaultCommandArgs,
Context extends BasePdkBuilderContext<Args> = BasePdkBuilderContext<Args>,
> = (context: Context) => PromiseOr<void>;

export type PdkBuilderCommandWithoutConfig<Args extends AnyCommandArgs = CommandArgs> = (
context: PdkBuilderContextWithoutConfig<Args>,
export type PdkBuilderCommandWithoutConfig<Args extends AnyCommandArgs = DefaultCommandArgs> = (
context: BasePdkBuilderContext<Args>,
) => PromiseOr<void>;

export type AnyCommandArgs = Record<string, unknown>;

export type CommandArgs = {
export type DefaultCommandArgs = {
arguments?: string[];
dryRun?: boolean;
parallel?: boolean;
quiet?: boolean;
verbose: number;
};

type PlatformArgs<Platform extends PdkPlatformName = PdkPlatformName> = {
export type PlatformCommandArgs<Platform extends PdkPlatformName = PdkPlatformName> = {
platform: Platform;
platformOutDir: string;
};

export type CommandArguments = (string | (CommandArgs & AnyCommandArgs) | Command)[];
export type InputCommandArguments<Args extends AnyCommandArgs = DefaultCommandArgs> = (
| string
| (DefaultCommandArgs & Args)
| Command
)[];

export type ParsedCommand<A extends Record<string, unknown> = Record<string, unknown>> = CommandArgs &
A & {
export type ParsedCommandArguments<Args extends AnyCommandArgs = DefaultCommandArgs> = DefaultCommandArgs &
Args & {
arguments?: string[];
command: Command;
};

export type CommandCb = (...args: CommandArguments) => void | Promise<void>;
export type CommandCb = (...args: InputCommandArguments) => void | Promise<void>;

export type CreateHook<Args extends AnyCommandArgs = CommandArgs> = (
export type CreateHook<Definition extends AnyCommandDefinition> = (
env: Liftoff.LiftoffEnv,
argv: string[],
) => (definition: CommandDefinition<Args>) => CommandCb;
) => (definition: Definition) => CommandCb;

export type BaseCommandContext<Args extends AnyCommandArgs = CommandArgs> = {
export type BaseCommandContext<Args extends AnyCommandArgs = DefaultCommandArgs> = {
env: LiftoffEnv;
args: Args;
debug?: PdkDebugger;
};

export type PdkBuilderContextWithoutConfig<Args extends AnyCommandArgs = CommandArgs> = BaseCommandContext<Args> & {
args: ParsedCommand<Args>;
export type BasePdkBuilderContext<Args extends AnyCommandArgs = DefaultCommandArgs> = BaseCommandContext<Args> & {
args: ParsedCommandArguments<Args>;
debug: PdkDebugger;
};

export type PdkBuilderContext<Args extends AnyCommandArgs = CommandArgs> = PdkBuilderContextWithoutConfig<
Args & Partial<PlatformArgs>
export type PdkBuilderContext<Args extends AnyCommandArgs = DefaultCommandArgs> = BasePdkBuilderContext<
Args & Partial<PlatformCommandArgs>
> & {
config: ResolvedPdkBuilderConfig;
};

export type PdkBuilderContextWithPlatformArgs<
Args extends AnyCommandArgs = CommandArgs,
Args extends AnyCommandArgs = DefaultCommandArgs,
Platform extends PdkPlatformName = PdkPlatformName,
> = PdkBuilderContext<PlatformArgs<Platform> & Args>;
> = PdkBuilderContext<PlatformCommandArgs<Platform> & Args>;

export type PdkDebugger = Debugger & {
logTimeTaken(): void;
Expand Down
4 changes: 2 additions & 2 deletions apps/app-builder/src/types/config.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {type NodePackageManager} from '../commands/upgrade/enums';
import {type VersionSource} from '../commands/increment/increment.types';
import {type StringGenerator} from './common.types';
import {
type CommandDefinition,
type AdditionalCommandDefinition,
type PdkBuilderCommand,
type PdkBuilderCommandWithoutConfig,
type PdkBuilderContext,
Expand Down Expand Up @@ -208,7 +208,7 @@ export type PdkBuilderConfig = {
/**
* Define additional custom commands on the pdk builder.
*/
additionalCommands?: CommandDefinition[];
additionalCommands?: AdditionalCommandDefinition[];

/**
* Hooks to execute before or after other commands. Works with custom commands added with `additionalCommands` as well.
Expand Down
6 changes: 5 additions & 1 deletion apps/app-builder/src/utils/addPlatformToContext.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import {
type AnyCommandArgs,
type DefaultCommandArgs,
type PdkBuilderContext,
type PdkBuilderContextWithPlatformArgs,
} from '../types/command.types';
import {type PdkPlatformName} from '../constants';
import {resolvePath} from './resolvePath';
import {withArgs} from './command/withArgs';

export const addPlatformToContext = <Platform extends PdkPlatformName, Args extends AnyCommandArgs = AnyCommandArgs>(
export const addPlatformToContext = <
Platform extends PdkPlatformName,
Args extends AnyCommandArgs = DefaultCommandArgs,
>(
context: PdkBuilderContext<Args>,
platform: Platform,
): PdkBuilderContextWithPlatformArgs<Args, Platform> => {
Expand Down
35 changes: 35 additions & 0 deletions apps/app-builder/src/utils/command/createCommandContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type Liftoff from 'liftoff';
import {resolveConfig} from '../resolveConfig';
import {mergeDefaultConfig} from '../mergeDefaultConfig';
import {
type AnyCommandArgs,
type AnyCommandDefinition,
type DefaultCommandArgs,
type InputCommandArguments,
type PdkBuilderCommand,
type PdkBuilderContext,
} from '../../types/command.types';
import {parseCommandInput} from './parseCommandInput';

type CommandContext<Args extends AnyCommandArgs = DefaultCommandArgs> = {
command: PdkBuilderCommand<Args>;
context: PdkBuilderContext<Args>;
};

export const createCommandContext = async <Args extends AnyCommandArgs = DefaultCommandArgs>(
definition: AnyCommandDefinition<Args>,
args: InputCommandArguments<Args>,
env: Liftoff.LiftoffEnv,
): Promise<CommandContext<Args>> => {
const {command, context} = await parseCommandInput(definition, args, env);

const config = await resolveConfig(env, context.args);

return {
command,
context: {
...context,
config: mergeDefaultConfig(config),
},
};
};
4 changes: 2 additions & 2 deletions apps/app-builder/src/utils/command/createDebugger.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import debugFactory from 'debug';
import chalk from 'chalk';
import {type CommandArgs, type PdkDebugger} from '../../types/command.types';
import {type DefaultCommandArgs, type PdkDebugger} from '../../types/command.types';

export const createDebugger = (name: string, args: CommandArgs): PdkDebugger => {
export const createDebugger = (name: string, args: DefaultCommandArgs): PdkDebugger => {
const debug = debugFactory(`@myparcel-pdk/app-builder:${name}`) as PdkDebugger;

const timeStart = Date.now();
Expand Down
Loading

0 comments on commit c5bd584

Please sign in to comment.