node-plop
Version:
programmatic plopping for fun and profit
289 lines (232 loc) • 7.24 kB
TypeScript
import inquirer, {
Answers,
CheckboxQuestion,
ConfirmQuestion,
EditorQuestion,
ExpandQuestion,
InputQuestion,
ListQuestion,
NumberQuestion,
PasswordQuestion,
PromptModule,
Question,
RawListQuestion,
} from "inquirer";
type Inquirer = typeof inquirer;
import { type GlobOptions } from "tinyglobby";
import { HelperDelegate as HelperFunction } from "handlebars";
export interface IncludeDefinitionConfig {
generators?: boolean;
helpers?: boolean;
partials?: boolean;
actionTypes?: boolean;
}
export type IncludeDefinition = boolean | string[] | IncludeDefinitionConfig;
export interface NodePlopAPI {
setGenerator(
name: string,
config: Partial<PlopGeneratorConfig>,
): PlopGenerator;
setPrompt(name: string, prompt: inquirer.prompts.PromptConstructor): void;
setWelcomeMessage(message: string): void;
getWelcomeMessage(): string;
getGenerator(name: string): PlopGenerator;
getGeneratorList(): { name: string; description: string }[];
setPartial(name: string, str: string): void;
getPartial(name: string): string;
getPartialList(): string[];
setHelper(name: string, fn: HelperFunction): void;
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
getHelper(name: string): Function;
getHelperList(): string[];
setActionType(name: string, fn: CustomActionFunction): void;
/**
* This does not include a `CustomActionConfig` for the same reasons
* Listed in the `ActionType` declaration. Please see that JSDoc for more
*/
getActionType(name: string): ActionType;
getActionTypeList(): string[];
setPlopfilePath(filePath: string): void;
getPlopfilePath(): string;
getDestBasePath(): string;
// plop.load functionality
load(
target: string[] | string,
loadCfg?: Partial<PlopCfg> | null,
includeOverride?: IncludeDefinition,
): Promise<void>;
setDefaultInclude(inc: object): void;
getDefaultInclude(): object;
renderString(template: string, data: any): string; //set to any matching handlebars declaration
// passthroughs for backward compatibility
/**
* @deprecated Use "setPrompt" instead. This will be removed in the next major release
*/
addPrompt(name: string, prompt: PromptModule): void;
/**
* @deprecated Use "setPartial" instead. This will be removed in the next major release
*/
addPartial(name: string, str: string): void;
/**
* @deprecated Use "setHelper" instead. This will be removed in the next major release
*/
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
addHelper(name: string, fn: Function): void;
}
interface PlopActionHooksFailures {
type: string;
path: string;
error: string;
message: string;
}
interface PlopActionHooksChanges {
type: string;
path: string;
}
interface PlopActionHooks {
onComment?: (msg: string) => void;
onSuccess?: (change: PlopActionHooksChanges) => void;
onFailure?: (failure: PlopActionHooksFailures) => void;
}
export interface PlopGeneratorConfig {
description: string;
prompts: Prompts;
actions: Actions;
}
export interface PlopGenerator extends PlopGeneratorConfig {
runPrompts: (bypassArr?: string[]) => Promise<any>;
runActions: (
answers: Answers,
hooks?: PlopActionHooks,
) => Promise<{
changes: PlopActionHooksChanges[];
failures: PlopActionHooksFailures[];
}>;
}
export type PromptQuestion =
| Question
| CheckboxQuestion
| ListQuestion
| ExpandQuestion
| ConfirmQuestion
| EditorQuestion
| RawListQuestion
| PasswordQuestion
| NumberQuestion
| InputQuestion;
export type DynamicPromptsFunction = (inquirer: Inquirer) => Promise<Answers>;
export type DynamicActionsFunction = (data?: Answers) => ActionType[];
export type Prompts = DynamicPromptsFunction | PromptQuestion[];
export type Actions = DynamicActionsFunction | ActionType[];
interface Template {
template: string;
templateFile?: never;
}
interface TemplateFile {
template?: never;
templateFile: string;
}
type TemplateStrOrFile = Template | TemplateFile;
/**
* "TypeString" is a type generic of what type is present. It allows us
* to pass arbitrary keys to a field alongside any type name that doesn't
* intersect with built-ins. This is an unfortunate limitation of TypeScript
*
* We strongly suggest you actually use a const type (so, 'customActionName' instead of `string`),
* that way, you can make sure that your custom action doesn't interfere with built-ins in
* the future.
*
* However, keep in mind that by doing so it means your config object MUST have the same `type` value
* as the generic input string.
*
* To ignore this limitation, simply pass "string"
*/
export interface CustomActionConfig<TypeString extends string>
extends Omit<ActionConfig, "type"> {
type: TypeString extends "addMany" | "modify" | "append" ? never : TypeString;
[key: string]: any;
}
export type CustomActionFunction = (
answers: Answers,
config: CustomActionConfig<string>,
plopfileApi: NodePlopAPI,
) => Promise<string> | string;
/**
* Ideally, we'd have `CustomActionConfig` here,
* but if we do, we lose the ability to strictly type an action,
* since "type: 'append'" is now considered a custom action config
* and not an append action config.
*
* See `CustomActionConfig` declaration for more usage instructions
*
* If you know of a solution, please open a GitHub issue to discuss
*/
export type ActionType =
| string
| ActionConfig
| AddActionConfig
| AddManyActionConfig
| ModifyActionConfig
| AppendActionConfig
| CustomActionFunction;
export interface ActionConfig {
type: string;
force?: boolean;
data?: object;
abortOnFail?: boolean;
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
skip?: Function;
}
type TransformFn<T> = (
template: string,
data: any,
cfg: T,
) => string | Promise<string>;
interface AddActionConfigBase extends ActionConfig {
type: "add";
path: string;
skipIfExists?: boolean;
transform?: TransformFn<AddActionConfig>;
}
export type AddActionConfig = AddActionConfigBase & TemplateStrOrFile;
export interface AddManyActionConfig
extends Pick<
AddActionConfig,
Exclude<
keyof AddActionConfig,
"type" | "templateFile" | "template" | "transform"
>
> {
type: "addMany";
destination: string;
base: string;
templateFiles: string | string[];
stripExtensions?: string[];
globOptions: GlobOptions;
verbose?: boolean;
transform?: TransformFn<AddManyActionConfig>;
}
interface ModifyActionConfigBase extends ActionConfig {
type: "modify";
path: string;
pattern: string | RegExp;
transform?: TransformFn<ModifyActionConfig>;
}
export type ModifyActionConfig = ModifyActionConfigBase & TemplateStrOrFile;
interface AppendActionConfigBase extends ActionConfig {
type: "append";
path: string;
pattern: string | RegExp;
unique: boolean;
separator: string;
}
export type AppendActionConfig = AppendActionConfigBase & TemplateStrOrFile;
export interface PlopCfg {
force: boolean;
destBasePath: string | undefined;
}
declare function nodePlop(
plopfilePath?: string,
plopCfg?: PlopCfg,
): Promise<NodePlopAPI>;
export default nodePlop;