mirror of
https://github.com/voson-wang/toon.git
synced 2026-01-29 23:34:10 +08:00
feat(cli): stream output for both encoding and decoding
This commit is contained in:
161
packages/cli/src/json-stringify-stream.ts
Normal file
161
packages/cli/src/json-stringify-stream.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Streaming JSON stringifier.
|
||||
*
|
||||
* Yields JSON tokens one at a time, allowing streaming output without holding
|
||||
* the entire JSON string in memory.
|
||||
*
|
||||
* @param value - The value to stringify (must be JSON-serializable)
|
||||
* @param indent - Number of spaces for indentation (0 = compact, >0 = pretty)
|
||||
* @returns Generator that yields JSON string chunks
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const data = { name: "Alice", scores: [95, 87, 92] }
|
||||
* for (const chunk of jsonStringifyLines(data, 2)) {
|
||||
* process.stdout.write(chunk)
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function* jsonStringifyLines(
|
||||
value: unknown,
|
||||
indent: number = 2,
|
||||
): Iterable<string> {
|
||||
yield* stringifyValue(value, 0, indent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal generator for recursive stringification.
|
||||
*/
|
||||
function* stringifyValue(
|
||||
value: unknown,
|
||||
depth: number,
|
||||
indent: number,
|
||||
): Iterable<string> {
|
||||
// Handle null
|
||||
if (value === null) {
|
||||
yield 'null'
|
||||
return
|
||||
}
|
||||
|
||||
const type = typeof value
|
||||
|
||||
// Handle primitives
|
||||
if (type === 'boolean' || type === 'number') {
|
||||
yield JSON.stringify(value)
|
||||
return
|
||||
}
|
||||
|
||||
if (type === 'string') {
|
||||
yield JSON.stringify(value)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
if (Array.isArray(value)) {
|
||||
yield* stringifyArray(value, depth, indent)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle objects
|
||||
if (type === 'object') {
|
||||
yield* stringifyObject(value as Record<string, unknown>, depth, indent)
|
||||
return
|
||||
}
|
||||
|
||||
// Undefined, functions, symbols become null in JSON
|
||||
yield 'null'
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify an array with proper formatting.
|
||||
*/
|
||||
function* stringifyArray(
|
||||
arr: unknown[],
|
||||
depth: number,
|
||||
indent: number,
|
||||
): Iterable<string> {
|
||||
if (arr.length === 0) {
|
||||
yield '[]'
|
||||
return
|
||||
}
|
||||
|
||||
yield '['
|
||||
|
||||
if (indent > 0) {
|
||||
// Pretty-printed format
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
yield '\n'
|
||||
yield ' '.repeat((depth + 1) * indent)
|
||||
yield* stringifyValue(arr[i], depth + 1, indent)
|
||||
if (i < arr.length - 1) {
|
||||
yield ','
|
||||
}
|
||||
}
|
||||
yield '\n'
|
||||
yield ' '.repeat(depth * indent)
|
||||
yield ']'
|
||||
}
|
||||
else {
|
||||
// Compact format
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
yield* stringifyValue(arr[i], depth + 1, indent)
|
||||
if (i < arr.length - 1) {
|
||||
yield ','
|
||||
}
|
||||
}
|
||||
yield ']'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify an object with proper formatting.
|
||||
*/
|
||||
function* stringifyObject(
|
||||
obj: Record<string, unknown>,
|
||||
depth: number,
|
||||
indent: number,
|
||||
): Iterable<string> {
|
||||
const keys = Object.keys(obj)
|
||||
|
||||
if (keys.length === 0) {
|
||||
yield '{}'
|
||||
return
|
||||
}
|
||||
|
||||
yield '{'
|
||||
|
||||
if (indent > 0) {
|
||||
// Pretty-printed format
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]!
|
||||
const value = obj[key]
|
||||
|
||||
yield '\n'
|
||||
yield ' '.repeat((depth + 1) * indent)
|
||||
yield JSON.stringify(key)
|
||||
yield ': '
|
||||
yield* stringifyValue(value, depth + 1, indent)
|
||||
if (i < keys.length - 1) {
|
||||
yield ','
|
||||
}
|
||||
}
|
||||
yield '\n'
|
||||
yield ' '.repeat(depth * indent)
|
||||
yield '}'
|
||||
}
|
||||
else {
|
||||
// Compact format
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]!
|
||||
const value = obj[key]
|
||||
|
||||
yield JSON.stringify(key)
|
||||
yield ':'
|
||||
yield* stringifyValue(value, depth + 1, indent)
|
||||
if (i < keys.length - 1) {
|
||||
yield ','
|
||||
}
|
||||
}
|
||||
yield '}'
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user