Files
toon/packages/cli/src/conversion.ts
2025-11-03 11:39:10 +01:00

91 lines
2.7 KiB
TypeScript

import type { DecodeOptions, Delimiter, EncodeOptions } from '../../toon/src'
import type { InputSource } from './types'
import * as fsp from 'node:fs/promises'
import * as path from 'node:path'
import process from 'node:process'
import { consola } from 'consola'
import { estimateTokenCount } from 'tokenx'
import { decode, encode } from '../../toon/src'
import { formatInputLabel, readInput } from './utils'
export async function encodeToToon(config: {
input: InputSource
output?: string
delimiter: Delimiter
indent: number
lengthMarker: NonNullable<EncodeOptions['lengthMarker']>
printStats: boolean
}): Promise<void> {
const jsonContent = await readInput(config.input)
let data: unknown
try {
data = JSON.parse(jsonContent)
}
catch (error) {
throw new Error(`Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}`)
}
const encodeOptions: EncodeOptions = {
delimiter: config.delimiter,
indent: config.indent,
lengthMarker: config.lengthMarker,
}
const toonOutput = encode(data, encodeOptions)
if (config.output) {
await fsp.writeFile(config.output, toonOutput, 'utf-8')
const relativeInputPath = formatInputLabel(config.input)
const relativeOutputPath = path.relative(process.cwd(), config.output)
consola.success(`Encoded \`${relativeInputPath}\`\`${relativeOutputPath}\``)
}
else {
console.log(toonOutput)
}
if (config.printStats) {
const jsonTokens = estimateTokenCount(jsonContent)
const toonTokens = estimateTokenCount(toonOutput)
const diff = jsonTokens - toonTokens
const percent = ((diff / jsonTokens) * 100).toFixed(1)
console.log()
consola.info(`Token estimates: ~${jsonTokens} (JSON) → ~${toonTokens} (TOON)`)
consola.success(`Saved ~${diff} tokens (-${percent}%)`)
}
}
export async function decodeToJson(config: {
input: InputSource
output?: string
indent: number
strict: boolean
}): Promise<void> {
const toonContent = await readInput(config.input)
let data: unknown
try {
const decodeOptions: DecodeOptions = {
indent: config.indent,
strict: config.strict,
}
data = decode(toonContent, decodeOptions)
}
catch (error) {
throw new Error(`Failed to decode TOON: ${error instanceof Error ? error.message : String(error)}`)
}
const jsonOutput = JSON.stringify(data, undefined, config.indent)
if (config.output) {
await fsp.writeFile(config.output, jsonOutput, 'utf-8')
const relativeInputPath = formatInputLabel(config.input)
const relativeOutputPath = path.relative(process.cwd(), config.output)
consola.success(`Decoded \`${relativeInputPath}\`\`${relativeOutputPath}\``)
}
else {
console.log(jsonOutput)
}
}