json-pretty-toml 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +60 -0
  2. package/cli.mjs +23 -0
  3. package/index.ts +120 -0
  4. package/package.json +25 -0
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # json-pretty-toml
2
+
3
+ JSON 转扁平 TOML,最多两层 key。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ git clone https://github.com/nano-properties/json-pretty-toml.git
9
+ cd json-pretty-toml
10
+ bun install
11
+ ```
12
+
13
+ ## 使用
14
+
15
+ ```bash
16
+ # 从文件转换
17
+ bun index.ts < in.json > out.toml
18
+
19
+ # 或直接输入
20
+ echo '{"a":{"b":1}}' | bun index.ts
21
+ ```
22
+
23
+ ## 规则
24
+
25
+ | JSON 层级 | TOML 输出 |
26
+ | ------------ | ------------------- |
27
+ | 第一层 | `[Table]` |
28
+ | 第二层简单值 | `key = value` |
29
+ | 第二层对象 | `key.sub = { ... }` |
30
+
31
+ ## 示例
32
+
33
+ 输入:
34
+
35
+ ```json
36
+ {
37
+ "gateway": {
38
+ "port": 18789,
39
+ "auth": { "mode": "token" }
40
+ }
41
+ }
42
+ ```
43
+
44
+ 输出:
45
+
46
+ ```toml
47
+ [gateway]
48
+ port = 18789
49
+ auth.mode = "token"
50
+ ```
51
+
52
+ ## 测试
53
+
54
+ ```bash
55
+ bun test
56
+ ```
57
+
58
+ ## License
59
+
60
+ MIT
package/cli.mjs ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import { TOMLWriter } from './index.ts'
3
+
4
+ async function readStdin() {
5
+ const chunks = []
6
+ for await (const chunk of process.stdin) {
7
+ chunks.push(chunk)
8
+ }
9
+ return Buffer.concat(chunks).toString('utf8')
10
+ }
11
+
12
+ const input = await readStdin()
13
+
14
+ let json
15
+ try {
16
+ json = JSON.parse(input)
17
+ } catch {
18
+ console.error('Error: Invalid JSON input')
19
+ process.exit(1)
20
+ }
21
+
22
+ const writer = new TOMLWriter()
23
+ console.log(writer.convert(json))
package/index.ts ADDED
@@ -0,0 +1,120 @@
1
+ export class TOMLWriter {
2
+ private indent = ' '
3
+
4
+ convert(json: Record<string, unknown>): string {
5
+ const lines: string[] = []
6
+
7
+ for (const [tableName, tableValue] of Object.entries(json)) {
8
+ lines.push(`[${this.formatKey(tableName)}]`)
9
+
10
+ if (this.isObject(tableValue)) {
11
+ for (const [secondKey, secondValue] of Object.entries(tableValue)) {
12
+ if (this.isObject(secondValue)) {
13
+ for (const [thirdKey, thirdValue] of Object.entries(secondValue)) {
14
+ const dottedKey = `${this.formatKey(secondKey)}.${this.formatKey(thirdKey)}`
15
+ lines.push(`${dottedKey} = ${this.formatValue(thirdValue, '')}`)
16
+ }
17
+ } else {
18
+ lines.push(`${this.formatKey(secondKey)} = ${this.formatValue(secondValue, '')}`)
19
+ }
20
+ }
21
+ } else {
22
+ lines.push(`value = ${this.formatValue(tableValue, '')}`)
23
+ }
24
+
25
+ lines.push('')
26
+ }
27
+
28
+ return lines.join('\n').trim()
29
+ }
30
+
31
+ private formatValue(value: unknown, currentIndent: string): string {
32
+ if (value === null) return '""'
33
+ if (typeof value === 'string') return JSON.stringify(value)
34
+ if (typeof value === 'number') return String(value)
35
+ if (typeof value === 'boolean') return String(value)
36
+ if (Array.isArray(value)) {
37
+ return this.formatArray(value, currentIndent)
38
+ }
39
+ if (this.isObject(value)) {
40
+ return this.formatInlineTable(value, currentIndent)
41
+ }
42
+ return String(value)
43
+ }
44
+
45
+ private formatArray(arr: unknown[], currentIndent: string): string {
46
+ if (arr.length === 0) return '[]'
47
+
48
+ const hasObject = arr.some((v) => this.isObject(v))
49
+ if (!hasObject) {
50
+ const items = arr.map((v) => this.formatValue(v, currentIndent)).join(', ')
51
+ return `[${items}]`
52
+ }
53
+
54
+ const nextIndent = currentIndent + this.indent
55
+ const items = arr.map((v) => {
56
+ const formatted = this.isObject(v) ? this.formatInlineTable(v, nextIndent) : this.formatValue(v, nextIndent)
57
+ return `${nextIndent}${formatted},`
58
+ })
59
+
60
+ return `[
61
+ ${items.join('\n')}
62
+ ${currentIndent}]`
63
+ }
64
+
65
+ private formatInlineTable(obj: Record<string, unknown>, currentIndent: string): string {
66
+ const nextIndent = currentIndent + this.indent
67
+ const entries = Object.entries(obj)
68
+
69
+ const isSimple = entries.length <= 2 && entries.every(([, v]) => !this.isObject(v) && !Array.isArray(v))
70
+
71
+ if (isSimple) {
72
+ const inline = entries.map(([k, v]) => `${this.formatKey(k)} = ${this.formatValue(v, currentIndent)}`).join(', ')
73
+ return `{ ${inline} }`
74
+ }
75
+
76
+ const lines = entries.map(([k, v]) => {
77
+ const value = this.formatValue(v, nextIndent)
78
+ return `${nextIndent}${this.formatKey(k)} = ${value},`
79
+ })
80
+
81
+ return `{
82
+ ${lines.join('\n')}
83
+ ${currentIndent}}`
84
+ }
85
+
86
+ private formatKey(key: string): string {
87
+ return /^[A-Za-z0-9_-]+$/.test(key) ? key : JSON.stringify(key)
88
+ }
89
+
90
+ private isObject(value: unknown): value is Record<string, unknown> {
91
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
92
+ }
93
+ }
94
+
95
+ async function readStdin(): Promise<string> {
96
+ const chunks: Buffer[] = []
97
+ for await (const chunk of process.stdin) {
98
+ chunks.push(chunk)
99
+ }
100
+ return Buffer.concat(chunks).toString('utf8')
101
+ }
102
+
103
+ async function main() {
104
+ const input = await readStdin()
105
+
106
+ let json: Record<string, unknown>
107
+ try {
108
+ json = JSON.parse(input)
109
+ } catch {
110
+ console.error('Error: Invalid JSON input')
111
+ process.exit(1)
112
+ }
113
+
114
+ const writer = new TOMLWriter()
115
+ console.log(writer.convert(json))
116
+ }
117
+
118
+ if (process.argv[1] === import.meta.filename) {
119
+ main()
120
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "json-pretty-toml",
3
+ "version": "1.0.0",
4
+ "description": "Convert JSON to flat TOML with max 2-level keys",
5
+ "main": "index.ts",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=23"
9
+ },
10
+ "bin": "./cli.mjs",
11
+ "scripts": {
12
+ "test": "bun test",
13
+ "format": "prettier --write ."
14
+ },
15
+ "keywords": [
16
+ "json",
17
+ "toml",
18
+ "converter",
19
+ "cli"
20
+ ],
21
+ "license": "MIT",
22
+ "devDependencies": {
23
+ "prettier": "^3.8.1"
24
+ }
25
+ }