diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 6e75b84..9ceabbd 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -12,9 +12,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - - uses: pnpm/action-setup@v2 - with: - version: 8 - run: bun install - run: bun run prettier - run: bun run build diff --git a/bun.lockb b/bun.lockb index 665afa6..3392015 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 536358e..b6cc07b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maastrich/moonx", - "version": "0.1.1", + "version": "0.1.2", "description": "A CLI tool to help you with moon syntax", "keywords": [ "moon", @@ -8,12 +8,13 @@ "cli", "proto" ], - "license": "ISC", + "license": "MIT", "author": "Maastrich @maastrich", "type": "module", "main": "src/index.ts", "bin": { - "moonx": "bin/moonx" + "moonx": "bin/moonx", + "mx": "bin/moonx" }, "files": [ "src", @@ -39,6 +40,7 @@ "@trivago/prettier-plugin-sort-imports": "^4.2.1", "@types/nunjucks": "^3.2.6", "@types/semver": "^7.5.5", + "@withfig/autocomplete-types": "^1.29.0", "bun-types": "^1.0.11", "prettier": "^3.0.3", "semver": "^7.5.4", diff --git a/scripts/fig.ts b/scripts/fig.ts new file mode 100644 index 0000000..f900f6f --- /dev/null +++ b/scripts/fig.ts @@ -0,0 +1,29 @@ +const generator: Fig.Generator = { + script(args) { + return [ + "moonx", + "_moonx_list", + ...args.filter( + (arg) => !arg.startsWith("-") && arg.length && "moonx" !== arg, + ), + ]; + }, + trigger: "moonx", + postProcess(out) { + const lines = out.split("\n"); + return lines.map((line) => { + return { + name: line, + }; + }); + }, +}; + +const completionSpec: Fig.Spec = { + name: "moonx", + args: { + isVariadic: true, + generators: generator, + }, +}; +export default completionSpec; diff --git a/src/cli/exec.ts b/src/cli/exec.ts new file mode 100644 index 0000000..81aeb32 --- /dev/null +++ b/src/cli/exec.ts @@ -0,0 +1,16 @@ +export function exec( + command: string, + wss: Array, + commands: Map, +) { + const wokspaces = commands.get(command); + if (!wokspaces) { + throw new Error(`task ${command} does not exist`); + } + if (!wss.length) { + return [`:${command}`]; + } + return wss + .filter((ws) => wokspaces.includes(ws)) + .map((ws) => `${ws}:${command}`); +} diff --git a/src/cli/list.ts b/src/cli/list.ts new file mode 100644 index 0000000..7ba7ec3 --- /dev/null +++ b/src/cli/list.ts @@ -0,0 +1,17 @@ +export function list( + [command, ...wss]: Array, + commands: Map, +) { + if (!commands.has(command) && wss.length) { + console.log(`task ${command} does not exist`); + return []; + } + if (!commands.has(command)) { + return Array.from(commands.keys()); + } + const workspaces = commands.get(command)!; + if (!wss.length) { + return workspaces; + } + return workspaces.filter((ws) => !wss.includes(ws)); +} diff --git a/src/index.ts b/src/index.ts index 1a03eb5..a37e888 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,10 @@ import { cac } from "cac"; import { spawnSync } from "child_process"; +import pkg from "../package.json"; + +import { exec } from "./cli/exec.js"; +import { list } from "./cli/list.js"; import { help } from "./utils/help.js"; import { logger } from "./utils/logger.js"; import { scan } from "./utils/scan-moon.js"; @@ -17,39 +21,32 @@ const commands = await scan(); const cli = cac("moonx"); -for (const [name, workspaces] of commands) { +cli.option("cache", ""); +cli.option("color", ""); +cli.option("concurrency", ""); +cli.option("c", ""); +cli.option("log", ""); +cli.option("logFile", ""); +cli.option("moon-help", ""); +cli.option("moon-version", ""); + +cli + .command("_moonx_list [...params]", "List all available tasks") + .action((arg) => { + const result = list(arg, commands); + const stdout = Bun.stdout.writer(); + stdout.write(result.join("\n")); + stdout.end(); + }); + +for (const name of commands.keys()) { cli .command(`${name} [...workspaces]`, "", { allowUnknownOptions: true }) - .usage(`${name} [...workspaces] [options]`) .action(async (wss: Array, options) => { - const args = ["moon", "run"]; + const workspaces = exec(name, wss, commands); const rest = ["--", ...options["--"]]; - if (wss.length === 0) { - return Bun.spawnSync({ - cmd: [args, `:${name}`, rest].flat(), - stdout: "inherit", - stderr: "inherit", - stdin: "inherit", - onExit(_, exitCode) { - if (exitCode) { - logger.error(`task ${name} failed`); - process.exit(exitCode); - } - }, - }); - } - wss = wss.filter((ws) => { - if (!workspaces.includes(ws)) { - logger.warn(`task ${name} does not exist on workspace ${ws}`); - return false; - } - return true; - }); - if (wss.length === 0) { - return; - } return Bun.spawnSync({ - cmd: [args, wss.map((ws) => `${name}:${ws}`), rest].flat(), + cmd: ["moon", "run", workspaces, rest].flat(), stdout: "inherit", stderr: "inherit", stdin: "inherit", @@ -80,6 +77,14 @@ cli.help((sections) => { return [{ body: help.task(workspaces) }]; }); +cli.on("command:*", () => { + logger.error(`Invalid command: ${cli.args.join(" ")}`); + cli.outputHelp(); + process.exit(1); +}); + +cli.version(pkg.version); + try { cli.parse(Bun.argv, { run: false }); await cli.runMatchedCommand(); diff --git a/src/utils/help.ts b/src/utils/help.ts index 8f64aeb..5abadd8 100644 --- a/src/utils/help.ts +++ b/src/utils/help.ts @@ -35,7 +35,9 @@ ${chalk.bold("Moon option:")} --log Lowest log level to output [env: MOON_LOG=] [default: info] [possible values: off, error, warn, info, debug, trace] --logFile Path to a file to dump the moon logs [env: MOON_LOG_FILE=] -h, --help Print help - -V, --version Print version + -v, --version Print moonx version + --moon-version Print moon version + --moon-help Print moon help For more info, run any command with the --help flag e.g. ${chalk.yellow("moonx --help")} diff --git a/tsconfig.json b/tsconfig.json index 27a3c0c..b8895a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "target": "ESNext", "module": "NodeNext", "skipLibCheck": true, - "types": ["bun-types"], + "types": ["bun-types", "@withfig/autocomplete-types"], "resolveJsonModule": true, "moduleResolution": "NodeNext" },