Initial commit
This commit is contained in:
198
skills/oclif-patterns/templates/command-with-config.ts
Normal file
198
skills/oclif-patterns/templates/command-with-config.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import { Command, Flags } from '@oclif/core'
|
||||
import * as fs from 'fs-extra'
|
||||
import * as path from 'path'
|
||||
import * as os from 'os'
|
||||
|
||||
export default class {{COMMAND_NAME}} extends Command {
|
||||
static description = 'Command with configuration file management'
|
||||
|
||||
static examples = [
|
||||
'<%= config.bin %> <%= command.id %> --init',
|
||||
'<%= config.bin %> <%= command.id %> --set key=value',
|
||||
'<%= config.bin %> <%= command.id %> --get key',
|
||||
'<%= config.bin %> <%= command.id %> --list',
|
||||
]
|
||||
|
||||
static flags = {
|
||||
init: Flags.boolean({
|
||||
description: 'Initialize configuration file',
|
||||
exclusive: ['set', 'get', 'list'],
|
||||
}),
|
||||
set: Flags.string({
|
||||
description: 'Set configuration value (key=value)',
|
||||
multiple: true,
|
||||
exclusive: ['init', 'get', 'list'],
|
||||
}),
|
||||
get: Flags.string({
|
||||
description: 'Get configuration value by key',
|
||||
exclusive: ['init', 'set', 'list'],
|
||||
}),
|
||||
list: Flags.boolean({
|
||||
description: 'List all configuration values',
|
||||
exclusive: ['init', 'set', 'get'],
|
||||
}),
|
||||
global: Flags.boolean({
|
||||
char: 'g',
|
||||
description: 'Use global config instead of local',
|
||||
default: false,
|
||||
}),
|
||||
}
|
||||
|
||||
private readonly DEFAULT_CONFIG = {
|
||||
version: '1.0.0',
|
||||
settings: {
|
||||
theme: 'default',
|
||||
verbose: false,
|
||||
timeout: 30000,
|
||||
},
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const { flags } = await this.parse({{COMMAND_NAME}})
|
||||
const configPath = this.getConfigPath(flags.global)
|
||||
|
||||
if (flags.init) {
|
||||
await this.initConfig(configPath)
|
||||
} else if (flags.set && flags.set.length > 0) {
|
||||
await this.setConfig(configPath, flags.set)
|
||||
} else if (flags.get) {
|
||||
await this.getConfigValue(configPath, flags.get)
|
||||
} else if (flags.list) {
|
||||
await this.listConfig(configPath)
|
||||
} else {
|
||||
// Default: show current config location
|
||||
this.log(`Config location: ${configPath}`)
|
||||
if (await fs.pathExists(configPath)) {
|
||||
this.log('Config file exists')
|
||||
await this.listConfig(configPath)
|
||||
} else {
|
||||
this.log('Config file does not exist. Run with --init to create.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getConfigPath(global: boolean): string {
|
||||
if (global) {
|
||||
return path.join(os.homedir(), '.config', 'mycli', 'config.json')
|
||||
} else {
|
||||
return path.join(process.cwd(), '.myclirc')
|
||||
}
|
||||
}
|
||||
|
||||
private async initConfig(configPath: string): Promise<void> {
|
||||
if (await fs.pathExists(configPath)) {
|
||||
this.error('Config file already exists. Remove it first or use --set to update.', {
|
||||
exit: 1,
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
await fs.ensureDir(path.dirname(configPath))
|
||||
|
||||
// Write default config
|
||||
await fs.writeJson(configPath, this.DEFAULT_CONFIG, { spaces: 2 })
|
||||
|
||||
this.log(`✓ Initialized config file at: ${configPath}`)
|
||||
}
|
||||
|
||||
private async setConfig(configPath: string, settings: string[]): Promise<void> {
|
||||
// Load existing config or use default
|
||||
let config = this.DEFAULT_CONFIG
|
||||
if (await fs.pathExists(configPath)) {
|
||||
config = await fs.readJson(configPath)
|
||||
} else {
|
||||
await fs.ensureDir(path.dirname(configPath))
|
||||
}
|
||||
|
||||
// Parse and set values
|
||||
for (const setting of settings) {
|
||||
const [key, ...valueParts] = setting.split('=')
|
||||
const value = valueParts.join('=')
|
||||
|
||||
if (!value) {
|
||||
this.warn(`Skipping invalid setting: ${setting}`)
|
||||
continue
|
||||
}
|
||||
|
||||
// Support dot notation
|
||||
this.setNestedValue(config, key, this.parseValue(value))
|
||||
this.log(`✓ Set ${key} = ${value}`)
|
||||
}
|
||||
|
||||
// Save config
|
||||
await fs.writeJson(configPath, config, { spaces: 2 })
|
||||
this.log(`\n✓ Updated config file: ${configPath}`)
|
||||
}
|
||||
|
||||
private async getConfigValue(configPath: string, key: string): Promise<void> {
|
||||
if (!await fs.pathExists(configPath)) {
|
||||
this.error('Config file does not exist. Run with --init first.', { exit: 1 })
|
||||
}
|
||||
|
||||
const config = await fs.readJson(configPath)
|
||||
const value = this.getNestedValue(config, key)
|
||||
|
||||
if (value === undefined) {
|
||||
this.error(`Key not found: ${key}`, { exit: 1 })
|
||||
}
|
||||
|
||||
this.log(typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value))
|
||||
}
|
||||
|
||||
private async listConfig(configPath: string): Promise<void> {
|
||||
if (!await fs.pathExists(configPath)) {
|
||||
this.error('Config file does not exist. Run with --init first.', { exit: 1 })
|
||||
}
|
||||
|
||||
const config = await fs.readJson(configPath)
|
||||
this.log('\nCurrent configuration:')
|
||||
this.log(JSON.stringify(config, null, 2))
|
||||
}
|
||||
|
||||
private setNestedValue(obj: any, path: string, value: any): void {
|
||||
const keys = path.split('.')
|
||||
const lastKey = keys.pop()!
|
||||
let current = obj
|
||||
|
||||
for (const key of keys) {
|
||||
if (!(key in current) || typeof current[key] !== 'object') {
|
||||
current[key] = {}
|
||||
}
|
||||
current = current[key]
|
||||
}
|
||||
|
||||
current[lastKey] = value
|
||||
}
|
||||
|
||||
private getNestedValue(obj: any, path: string): any {
|
||||
const keys = path.split('.')
|
||||
let current = obj
|
||||
|
||||
for (const key of keys) {
|
||||
if (current && typeof current === 'object' && key in current) {
|
||||
current = current[key]
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
private parseValue(value: string): any {
|
||||
// Try to parse as JSON
|
||||
if (value === 'true') return true
|
||||
if (value === 'false') return false
|
||||
if (value === 'null') return null
|
||||
if (/^-?\d+$/.test(value)) return parseInt(value, 10)
|
||||
if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value)
|
||||
if (value.startsWith('{') || value.startsWith('[')) {
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user