diff --git a/benchmarks/completion.bench.ts b/benchmarks/completion.bench.ts new file mode 100644 index 0000000..f0fbdfa --- /dev/null +++ b/benchmarks/completion.bench.ts @@ -0,0 +1,79 @@ +import { Bench } from 'tinybench'; +import { RootCommand } from '../src/t'; + +const bench = new Bench({ time: 1000 }); + +const setupCompletion = () => { + const completion = new RootCommand(); + completion.command('dev', 'Start dev server'); + completion.command('build', 'Build project'); + completion.command('test', 'Run tests'); + + completion.option('--config', 'Config file'); + completion.option('--verbose', 'Verbose output'); + + const devCommand = completion.commands.get('dev')!; + devCommand.option('--port', 'Port number', function (complete) { + complete('3000', 'Development port'); + complete('8080', 'Alternative port'); + }); + + return completion; +}; + +const suppressOutput = (fn: () => void) => { + const originalLog = console.log; + console.log = () => {}; + fn(); + console.log = originalLog; +}; + +bench.add('command completion', () => { + const completion = setupCompletion(); + suppressOutput(() => completion.parse(['d'])); +}); + +bench.add('option completion', () => { + const completion = setupCompletion(); + suppressOutput(() => completion.parse(['dev', '--p'])); +}); + +bench.add('option value completion', () => { + const completion = setupCompletion(); + suppressOutput(() => completion.parse(['dev', '--port', ''])); +}); + +bench.add('no match', () => { + const completion = setupCompletion(); + suppressOutput(() => completion.parse(['xyz'])); +}); + +bench.add('large command set', () => { + const completion = new RootCommand(); + for (let i = 0; i < 100; i++) { + completion.command(`cmd${i}`, `Command ${i}`); + } + suppressOutput(() => completion.parse(['cmd5'])); +}); + +async function runBenchmarks() { + await bench.run(); + + console.table( + bench.tasks.map((task) => ({ + name: task.name, + 'ops/sec': task.result?.hz + ? Math.round(task.result.hz).toLocaleString() + : 'N/A', + 'avg (ms)': task.result?.mean + ? (task.result.mean * 1000).toFixed(3) + : 'N/A', + })) + ); +} + +if (process.argv[1]?.endsWith('completion.bench.ts')) { + runBenchmarks().catch(console.error); +} + +export { runBenchmarks }; diff --git a/package.json b/package.json index b7b3b9f..065cfe3 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "build": "tsdown", "prepare": "pnpm build", "lint": "eslint src \"./*.ts\"", - "test-cli": "tsx bin/cli.ts" + "benchmark": "tsx benchmarks/completion.bench.ts" }, "files": [ "dist" @@ -30,6 +30,7 @@ "commander": "^13.1.0", "eslint-config-prettier": "^10.0.1", "prettier": "^3.5.2", + "tinybench": "^4.0.1", "tsdown": "^0.9.7", "tsx": "^4.19.1", "typescript": "^5.7.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78da1d2..d963698 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,9 @@ importers: prettier: specifier: ^3.5.2 version: 3.5.2 + tinybench: + specifier: ^4.0.1 + version: 4.0.1 tsdown: specifier: ^0.9.7 version: 0.9.7(typescript@5.7.3) @@ -1383,6 +1386,10 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@4.0.1: + resolution: {integrity: sha512-Nb1srn7dvzkVx0J5h1vq8f48e3TIcbrS7e/UfAI/cDSef/n8yLh4zsAEsFkfpw6auTY+ZaspEvam/xs8nMnotQ==} + engines: {node: '>=18.0.0'} + tinyexec@0.3.1: resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} @@ -2708,6 +2715,8 @@ snapshots: tinybench@2.9.0: {} + tinybench@4.0.1: {} + tinyexec@0.3.1: {} tinyexec@1.0.1: {}