Hono CLI calls commander.js directly. This is tightly coupled.
If we will change CLI library, we have to re-write production code. It may be difficult to guarantee same behavior.
So, How about introducing anti-corruption layer (ACL)? 👀
1. make src/acl/command.ts
Hono CLI uses only belows from commander.js.
- name
- description
- version
- parse
- command
- argument
- option
- action
We make original class named Command which has thees methods.
e.g,)
import { Command as CommanderCommand } from 'commander'
type ActionCallback = (...args: any[]) => void | Promise<void>
export class Command {
private instance: CommanderCommand
/**
* @internal
* This method is used to pass the internal commander instance to other commands.
* It should not be used directly.
*/
_getCommanderInstance(): CommanderCommand {
return this.instance
}
constructor(name?: string) {
this.instance = new CommanderCommand(name)
}
name(name: string): this {
this.instance.name(name)
return this
}
description(str: string): this {
this.instance.description(str)
return this
}
addCommand(cmd: Command): this {
this.instance.addCommand(cmd._getCommanderInstance())
return this
}
option(flags: string, description?: string, defaultValue?: string | boolean | string[]): this {
this.instance.option(flags, description, defaultValue)
return this
}
action(fn: ActionCallback): this {
this.instance.action(fn)
return this
}
parse(argv?: readonly string[]): this {
this.instance.parse(argv)
return this
}
version(str: string, flags?: string, description?: string): this {
this.instance.version(str, flags, description)
return this
}
}
This inverts the dependency: instead of Hono CLI being tied to the Commander interface, you can decide what interface you need for Hono CLI and then use the functionality you need from Commander.js.
2. Change import resouce
All files excluding src/acl/command.ts replace commander to src/acl/command.ts
- import { Command } from 'commander'
+ import { Command } from './acl/command.ts'
import { readFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { docsCommand } from './commands/docs/index.js'
import { optimizeCommand } from './commands/optimize/index.js'
import { requestCommand } from './commands/request/index.js'
import { searchCommand } from './commands/search/index.js'
import { serveCommand } from './commands/serve/index.js'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
...
3. Add Test Code for src/acl/command.ts
This makes it easy to verify operation even if you replace commander.js with another CLI library.
Hono CLI calls
commander.jsdirectly. This is tightly coupled.If we will change CLI library, we have to re-write production code. It may be difficult to guarantee same behavior.
So, How about introducing anti-corruption layer (ACL)? 👀
1. make
src/acl/command.tsHono CLI uses only belows from
commander.js.We make original class named
Commandwhich has thees methods.e.g,)
This inverts the dependency: instead of Hono CLI being tied to the Commander interface, you can decide what interface you need for Hono CLI and then use the functionality you need from Commander.js.
2. Change import resouce
All files excluding
src/acl/command.tsreplacecommandertosrc/acl/command.ts3. Add Test Code for
src/acl/command.tsThis makes it easy to verify operation even if you replace
commander.jswith another CLI library.