diff --git a/.changeset/pretty-dingos-judge.md b/.changeset/pretty-dingos-judge.md new file mode 100644 index 0000000..b728417 --- /dev/null +++ b/.changeset/pretty-dingos-judge.md @@ -0,0 +1,5 @@ +--- +"termost": patch +--- + +Help fallback prevents the version being displayed. diff --git a/examples/empty/package.json b/examples/empty/package.json new file mode 100644 index 0000000..109a0ed --- /dev/null +++ b/examples/empty/package.json @@ -0,0 +1,14 @@ +{ + "private": true, + "name": "@examples/empty", + "version": "0.0.0", + "scripts": { + "start": "node -r esbuild-register ./src/index.ts" + }, + "dependencies": { + "termost": "workspace:^" + }, + "devDependencies": { + "esbuild-register": "3.6.0" + } +} diff --git a/examples/empty/src/index.ts b/examples/empty/src/index.ts new file mode 100644 index 0000000..38e7f67 --- /dev/null +++ b/examples/empty/src/index.ts @@ -0,0 +1,13 @@ +import { termost } from "termost"; + +const program = termost("Example to showcase empty `command` fallback"); + +program.command({ + name: "build", + description: "Transpile and bundle in production mode", +}); + +program.command({ + name: "watch", + description: "Rebuild your assets on any code change", +}); diff --git a/examples/empty/tsconfig.json b/examples/empty/tsconfig.json new file mode 100644 index 0000000..bc086ba --- /dev/null +++ b/examples/empty/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 14d6bea..7fab244 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,16 @@ importers: specifier: 3.6.0 version: 3.6.0(esbuild@0.21.5) + examples/empty: + dependencies: + termost: + specifier: workspace:^ + version: link:../../termost + devDependencies: + esbuild-register: + specifier: 3.6.0 + version: 3.6.0(esbuild@0.21.5) + examples/input: dependencies: termost: diff --git a/termost/src/__snapshots__/index.test.ts.snap b/termost/src/__snapshots__/index.test.ts.snap index 1db1864..13e95ac 100644 --- a/termost/src/__snapshots__/index.test.ts.snap +++ b/termost/src/__snapshots__/index.test.ts.snap @@ -28,24 +28,53 @@ OPTIONS:  -f, --flag  A super useful CLI flag" `; -exports[`termost > should display \`help\` given empty root command 1`] = ` +exports[`termost > should display \`help\` given empty command 1`] = ` " USAGE: -@examples/command [...options] +@examples/empty [...options]  DESCRIPTION: -Example to showcase the \`command\` API +Example to showcase empty \`command\` fallback  COMMANDS: - build  Transpile and bundle in production mode - watch  Rebuild your assets on any code change + build  Transpile and bundle in production mode + watch  Rebuild your assets on any code change  OPTIONS: - -h, --help  Display the help center - -v, --version  Print the version - --global  Shared flag between commands" + -h, --help  Display the help center + -v, --version  Print the version" `; +exports[`termost > should display \`help\` given empty command 2`] = ` +" +USAGE: +@examples/empty build [...options] + +DESCRIPTION: +Transpile and bundle in production mode + +OPTIONS: + -h, --help  Display the help center + -v, --version  Print the version" +`; + +exports[`termost > should display \`help\` given empty command 3`] = ` +" +USAGE: +@examples/empty watch [...options] + +DESCRIPTION: +Rebuild your assets on any code change + +OPTIONS: + -h, --help  Display the help center + -v, --version  Print the version" +`; + +exports[`termost > should display \`version\` 1`] = `"0.0.0"`; + +exports[`termost > should display \`version\` 2`] = `"0.0.0"`; + exports[`termost > should handle \`command\` api 1`] = ` " USAGE: diff --git a/termost/src/api/command/command.ts b/termost/src/api/command/command.ts index 0eda64a..94d1ede 100644 --- a/termost/src/api/command/command.ts +++ b/termost/src/api/command/command.ts @@ -40,20 +40,14 @@ export const createCommand = ( // metadata (especially to let the global help option to display all available commands): const optionKeys = Object.keys(argv.options); - if ( - optionKeys.includes(OPTION_HELP_NAMES[0]) || - optionKeys.includes(OPTION_HELP_NAMES[1]) || - (isRootCommand && !metadata.hasOutput[rootCommandName]) - ) { + const help = () => { showHelp({ controller, currentCommandName: name, isRootCommand, rootCommandName, }); - - return; - } + }; if ( optionKeys.includes(OPTION_VERSION_NAMES[0]) || @@ -64,7 +58,21 @@ export const createCommand = ( return; } - void controller.enable(); + if ( + optionKeys.includes(OPTION_HELP_NAMES[0]) || + optionKeys.includes(OPTION_HELP_NAMES[1]) + ) { + help(); + + return; + } + + if (metadata.isEmptyCommand[name]) { + // Show help by default if no processing is done for the current command + help(); + } else { + void controller.enable(); + } } }, 0); diff --git a/termost/src/index.test.ts b/termost/src/index.test.ts index 6b5c78e..4aeceb2 100644 --- a/termost/src/index.test.ts +++ b/termost/src/index.test.ts @@ -3,6 +3,19 @@ import { describe, expect, test } from "vitest"; import { exec } from "./helpers/process"; describe("termost", () => { + test("should display `version`", async () => { + const longFlagOutput = await safeExec( + "pnpm --filter @examples/default start --version", + ); + + const shortFlagOutput = await safeExec( + "pnpm --filter @examples/default start -v", + ); + + expect(longFlagOutput).toMatchSnapshot(); + expect(shortFlagOutput).toMatchSnapshot(); + }); + test("should display `help`", async () => { const longFlagOutput = await safeExec( "pnpm --filter @examples/default start --help", @@ -16,10 +29,22 @@ describe("termost", () => { expect(shortFlagOutput).toMatchSnapshot(); }); - test("should display `help` given empty root command", async () => { - const output = await safeExec("pnpm --filter @examples/command start"); + test("should display `help` given empty command", async () => { + const rootCommand = await safeExec( + "pnpm --filter @examples/empty start", + ); - expect(output).toMatchSnapshot(); + const buildCommand = await safeExec( + "pnpm --filter @examples/empty start build", + ); + + const watchCommand = await safeExec( + "pnpm --filter @examples/empty start watch", + ); + + expect(rootCommand).toMatchSnapshot(); + expect(buildCommand).toMatchSnapshot(); + expect(watchCommand).toMatchSnapshot(); }); test("should handle `command` api", async () => { diff --git a/termost/src/termost.ts b/termost/src/termost.ts index e983cc0..e73eee1 100644 --- a/termost/src/termost.ts +++ b/termost/src/termost.ts @@ -74,7 +74,7 @@ export function termost( name, description, argv: { command, operands, options }, - hasOutput: {}, + isEmptyCommand: {}, version, }); } @@ -115,12 +115,13 @@ export const createProgram = ( const program: Termost = { command(params: CommandParameters) { currentCommandName = createCommand(params, metadata); - metadata.hasOutput[currentCommandName] = false; + metadata.isEmptyCommand[currentCommandName] = true; return this as Termost; }, input(params) { createInstruction(createInput, params); + metadata.isEmptyCommand[currentCommandName] = false; return this; }, @@ -137,7 +138,7 @@ export const createProgram = ( }, task(params) { createInstruction(createTask, params); - metadata.hasOutput[currentCommandName] = true; + metadata.isEmptyCommand[currentCommandName] = false; return this; }, diff --git a/termost/src/types.ts b/termost/src/types.ts index 5cca179..d654eef 100644 --- a/termost/src/types.ts +++ b/termost/src/types.ts @@ -23,7 +23,7 @@ export type PackageMetadata = { export type ProgramMetadata = PackageMetadata & { argv: ArgumentValues; - hasOutput: Record; + isEmptyCommand: Record; }; export type Context = Values;