mirror of
https://github.com/voson-wang/toon.git
synced 2026-01-29 23:34:10 +08:00
115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
import type { Delimiter } from '../../toon/src'
|
|
import type { InputSource } from './types'
|
|
import * as path from 'node:path'
|
|
import process from 'node:process'
|
|
import { defineCommand, runMain } from 'citty'
|
|
import { consola } from 'consola'
|
|
import { name, version } from '../../toon/package.json' with { type: 'json' }
|
|
import { DEFAULT_DELIMITER, DELIMITERS } from '../../toon/src'
|
|
import { decodeToJson, encodeToToon } from './conversion'
|
|
import { detectMode } from './utils'
|
|
|
|
const main = defineCommand({
|
|
meta: {
|
|
name,
|
|
description: 'TOON CLI — Convert between JSON and TOON formats',
|
|
version,
|
|
},
|
|
args: {
|
|
input: {
|
|
type: 'positional',
|
|
description: 'Input file path (omit or use "-" to read from stdin)',
|
|
required: false,
|
|
},
|
|
output: {
|
|
type: 'string',
|
|
description: 'Output file path',
|
|
alias: 'o',
|
|
},
|
|
encode: {
|
|
type: 'boolean',
|
|
description: 'Encode JSON to TOON (auto-detected by default)',
|
|
alias: 'e',
|
|
},
|
|
decode: {
|
|
type: 'boolean',
|
|
description: 'Decode TOON to JSON (auto-detected by default)',
|
|
alias: 'd',
|
|
},
|
|
delimiter: {
|
|
type: 'string',
|
|
description: 'Delimiter for arrays: comma (,), tab (\\t), or pipe (|)',
|
|
default: ',',
|
|
},
|
|
indent: {
|
|
type: 'string',
|
|
description: 'Indentation size',
|
|
default: '2',
|
|
},
|
|
lengthMarker: {
|
|
type: 'boolean',
|
|
description: 'Use length marker (#) for arrays',
|
|
default: false,
|
|
},
|
|
strict: {
|
|
type: 'boolean',
|
|
description: 'Enable strict mode for decoding',
|
|
default: true,
|
|
},
|
|
stats: {
|
|
type: 'boolean',
|
|
description: 'Show token statistics',
|
|
default: false,
|
|
},
|
|
},
|
|
async run({ args }) {
|
|
const input = args.input
|
|
|
|
const inputSource: InputSource = !input || input === '-'
|
|
? { type: 'stdin' }
|
|
: { type: 'file', path: path.resolve(input) }
|
|
const outputPath = args.output ? path.resolve(args.output) : undefined
|
|
|
|
// Parse and validate indent
|
|
const indent = Number.parseInt(args.indent || '2', 10)
|
|
if (Number.isNaN(indent) || indent < 0) {
|
|
throw new Error(`Invalid indent value: ${args.indent}`)
|
|
}
|
|
|
|
// Validate delimiter
|
|
const delimiter = args.delimiter || DEFAULT_DELIMITER
|
|
if (!(Object.values(DELIMITERS)).includes(delimiter as Delimiter)) {
|
|
throw new Error(`Invalid delimiter "${delimiter}". Valid delimiters are: comma (,), tab (\\t), pipe (|)`)
|
|
}
|
|
|
|
const mode = detectMode(inputSource, args.encode, args.decode)
|
|
|
|
try {
|
|
if (mode === 'encode') {
|
|
await encodeToToon({
|
|
input: inputSource,
|
|
output: outputPath,
|
|
delimiter: delimiter as Delimiter,
|
|
indent,
|
|
lengthMarker: args.lengthMarker === true ? '#' : false,
|
|
printStats: args.stats === true,
|
|
})
|
|
}
|
|
else {
|
|
await decodeToJson({
|
|
input: inputSource,
|
|
output: outputPath,
|
|
indent,
|
|
strict: args.strict !== false,
|
|
})
|
|
}
|
|
}
|
|
catch (error) {
|
|
consola.error(error)
|
|
process.exit(1)
|
|
}
|
|
},
|
|
})
|
|
|
|
runMain(main)
|