Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add plugins system #158

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions playground/cli.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineCommand, runMain } from "../src";
import logPlugin from "./plugins/log";

Check warning on line 2 in playground/cli.ts

View check run for this annotation

Codecov / codecov/patch

playground/cli.ts#L2

Added line #L2 was not covered by tests

const main = defineCommand({
meta: {
Expand All @@ -12,6 +13,7 @@
cleanup() {
console.log("Cleanup");
},
plugins: [logPlugin],

Check warning on line 16 in playground/cli.ts

View check run for this annotation

Codecov / codecov/patch

playground/cli.ts#L16

Added line #L16 was not covered by tests
subCommands: {
build: () => import("./commands/build").then((r) => r.default),
deploy: () => import("./commands/deploy").then((r) => r.default),
Expand Down
15 changes: 13 additions & 2 deletions playground/commands/debug.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import consola from "consola";
import { defineCommand } from "../../src";
import { defineCittyPlugin, defineCommand } from "../../src";

const buildPlugin = defineCittyPlugin({
name: "build",
setup() {
consola.info("Setting up build plugin");
},
cleanup() {
consola.info("Cleaning up build plugin");
},
});

Check warning on line 12 in playground/commands/debug.ts

View check run for this annotation

Codecov / codecov/patch

playground/commands/debug.ts#L2-L12

Added lines #L2 - L12 were not covered by tests

export default defineCommand({
meta: {
name: "debug",
description: "Debug the project",
hidden: true
hidden: true,

Check warning on line 18 in playground/commands/debug.ts

View check run for this annotation

Codecov / codecov/patch

playground/commands/debug.ts#L18

Added line #L18 was not covered by tests
},
args: {
verbose: {
Expand All @@ -18,6 +28,7 @@
description: "Only debug a specific function",
},
},
plugins: [buildPlugin],

Check warning on line 31 in playground/commands/debug.ts

View check run for this annotation

Codecov / codecov/patch

playground/commands/debug.ts#L31

Added line #L31 was not covered by tests
run({ args }) {
consola.log("Debug");
consola.log("Parsed args:", args);
Expand Down
2 changes: 2 additions & 0 deletions playground/hello.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import consola from "consola";
import { defineCommand, createMain } from "../src";
import logPlugin from "./plugins/log";

Check warning on line 3 in playground/hello.ts

View check run for this annotation

Codecov / codecov/patch

playground/hello.ts#L3

Added line #L3 was not covered by tests

const command = defineCommand({
meta: {
Expand Down Expand Up @@ -30,6 +31,7 @@
required: false,
},
},
plugins: [logPlugin],

Check warning on line 34 in playground/hello.ts

View check run for this annotation

Codecov / codecov/patch

playground/hello.ts#L34

Added line #L34 was not covered by tests
run({ args }) {
consola.log(args);
const msg = [
Expand Down
16 changes: 16 additions & 0 deletions playground/plugins/log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import consola from "consola";
import { defineCittyPlugin } from "../../src";

export default defineCittyPlugin(() => {
consola.success("Log plugin loaded");

return {
name: "log",
setup() {
consola.info("Setting up log plugin");
},
cleanup() {
consola.info("Cleaning up log plugin");
},
};
});

Check warning on line 16 in playground/plugins/log.ts

View check run for this annotation

Codecov / codecov/patch

playground/plugins/log.ts#L2-L16

Added lines #L2 - L16 were not covered by tests
18 changes: 18 additions & 0 deletions src/command.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { CommandContext, CommandDef, ArgsDef } from "./types";
import { CLIError, resolveValue } from "./_utils";
import { parseArgs } from "./args";
import { resolvePlugins } from "./plugin";

export function defineCommand<T extends ArgsDef = ArgsDef>(
def: CommandDef<T>,
Expand Down Expand Up @@ -28,6 +29,16 @@ export async function runCommand<T extends ArgsDef = ArgsDef>(
cmd,
};

const plugins = await resolvePlugins(cmd.plugins ?? []);

// Apply setup hooks from plugins
for (const plugin of plugins) {
if (typeof plugin.setup === "function") {
// TODO: Pass plugin context?
await plugin.setup();
}
}

// Setup hook
if (typeof cmd.setup === "function") {
await cmd.setup(context);
Expand Down Expand Up @@ -68,6 +79,13 @@ export async function runCommand<T extends ArgsDef = ArgsDef>(
if (typeof cmd.cleanup === "function") {
await cmd.cleanup(context);
}
// Apply cleanup hooks from plugins
for (const plugin of plugins) {
if (typeof plugin.cleanup === "function") {
// TODO: Pass plugin context?
await plugin.cleanup();
}
}
}
return { result };
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { defineCommand, runCommand } from "./command";
export { parseArgs } from "./args";
export { renderUsage, showUsage } from "./usage";
export { runMain, createMain } from "./main";
export { defineCittyPlugin } from "./plugin";
41 changes: 41 additions & 0 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CittyPlugin, Resolvable } from "./types";

/**
* Define a Citty plugin.
*
* Can be a function that returns a plugin object or a plugin object.
*
* @example
* ```ts
* import { defineCittyPlugin } from "citty";
*
* export const myPlugin = defineCittyPlugin({
* name: "my-plugin",
* async setup() {
* console.log("Setting up my plugin");
* },
* async cleanup() {
* console.log("Cleaning up my plugin");
* },
* });
* ```
*/
export function defineCittyPlugin(
plugin: Resolvable<CittyPlugin>,
): Resolvable<CittyPlugin> {
return plugin;
}

/**
* Resolve a Citty plugin since it can be a function that returns a plugin object.
*/
export const resolvePlugin = async (plugin: Resolvable<CittyPlugin>) => {
return typeof plugin === "function" ? await plugin() : plugin;
};

/**
* Resolve an array of Citty plugins.
*/
export const resolvePlugins = (plugins: Resolvable<CittyPlugin>[]) => {
return Promise.all(plugins.map((plugin) => resolvePlugin(plugin)));
};
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export type CommandDef<T extends ArgsDef = ArgsDef> = {
setup?: (context: CommandContext<T>) => any | Promise<any>;
cleanup?: (context: CommandContext<T>) => any | Promise<any>;
run?: (context: CommandContext<T>) => any | Promise<any>;
plugins?: Resolvable<CittyPlugin>[];
};

export type CommandContext<T extends ArgsDef = ArgsDef> = {
Expand All @@ -104,6 +105,14 @@ export type CommandContext<T extends ArgsDef = ArgsDef> = {
data?: any;
};

// ----- Plugin -----

export type CittyPlugin = {
name: string;
setup?(): Promise<void> | void;
cleanup?(): Promise<void> | void;
};

// ----- Utils -----

export type Awaitable<T> = () => T | Promise<T>;
Expand Down
Loading