@zjy4fun/json-open 0.1.1
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.
- package/.github/workflows/ci.yml +24 -0
- package/.github/workflows/publish-gpr.yml +31 -0
- package/.github/workflows/publish.yml +35 -0
- package/README.md +160 -0
- package/README.zh-CN.md +160 -0
- package/bin/json.js +213 -0
- package/demo.gif +0 -0
- package/package.json +29 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Setup Node
|
|
16
|
+
uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: 20
|
|
19
|
+
|
|
20
|
+
- name: Install deps
|
|
21
|
+
run: npm ci
|
|
22
|
+
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: npm test
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Publish to GitHub Packages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
release:
|
|
6
|
+
types: [published]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish-gpr:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
packages: write
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Setup Node for GitHub Packages
|
|
19
|
+
uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: 20
|
|
22
|
+
registry-url: https://npm.pkg.github.com
|
|
23
|
+
scope: '@zjy4fun'
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: npm ci
|
|
27
|
+
|
|
28
|
+
- name: Publish package to GitHub Packages
|
|
29
|
+
run: npm publish
|
|
30
|
+
env:
|
|
31
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish to npm (tag)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*.*.*'
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
publish:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: 20
|
|
22
|
+
registry-url: 'https://registry.npmjs.org'
|
|
23
|
+
cache: 'npm'
|
|
24
|
+
|
|
25
|
+
- run: npm ci
|
|
26
|
+
- run: npm test --if-present
|
|
27
|
+
- run: npm run build --if-present
|
|
28
|
+
|
|
29
|
+
- name: Verify tag matches package version
|
|
30
|
+
run: |
|
|
31
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
32
|
+
PKG_VERSION=$(node -p "require('./package.json').version")
|
|
33
|
+
[ "$TAG_VERSION" = "$PKG_VERSION" ] || (echo "Tag $TAG_VERSION != package.json $PKG_VERSION" && exit 1)
|
|
34
|
+
|
|
35
|
+
- run: npm publish --access public --provenance
|
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# json-open
|
|
2
|
+
|
|
3
|
+
Open JSON in your browser with a collapsible tree view (supports stdin and inline JSON text).
|
|
4
|
+
|
|
5
|
+
> A tiny CLI for command-line users: feed JSON, instantly inspect it in a browser with foldable structure.
|
|
6
|
+
|
|
7
|
+
## Demo
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Why this exists
|
|
14
|
+
|
|
15
|
+
Reading JSON in a terminal is often painful:
|
|
16
|
+
|
|
17
|
+
- Long output is hard to scan
|
|
18
|
+
- Deep nesting is hard to understand quickly
|
|
19
|
+
- Heavy tools feel overkill for quick debugging
|
|
20
|
+
|
|
21
|
+
`json-open` keeps this simple: **make JSON inspection fast and visual**.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- ✅ Pipe input: `curl ... | json`
|
|
28
|
+
- ✅ Inline JSON: `json '{"a":1}'`
|
|
29
|
+
- ✅ Collapsible tree view in browser
|
|
30
|
+
- ✅ Expand all / Collapse all buttons
|
|
31
|
+
- ✅ Rendered from local temp file (no remote upload)
|
|
32
|
+
- ✅ Cross-platform browser open (macOS / Linux / Windows)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### Option A: GitHub Packages (current primary channel)
|
|
39
|
+
|
|
40
|
+
Configure npm for GitHub Packages first:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
echo "@zjy4fun:registry=https://npm.pkg.github.com" >> ~/.npmrc
|
|
44
|
+
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN" >> ~/.npmrc
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Then install:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm i -g @zjy4fun/json-open
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Option B: Local development install
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/zjy4fun/json-open.git
|
|
57
|
+
cd json-open
|
|
58
|
+
npm install
|
|
59
|
+
npm link
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
After that, the `json` command is available globally.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Quick start
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# 1) API response
|
|
70
|
+
curl https://jsonplaceholder.typicode.com/todos/1 | json
|
|
71
|
+
|
|
72
|
+
# 2) Inline JSON
|
|
73
|
+
json '{"hello":"world","list":[1,2,3]}'
|
|
74
|
+
|
|
75
|
+
# 3) JSON file content
|
|
76
|
+
cat response.json | json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The command opens your browser and shows a JSON tree view.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Common use cases
|
|
84
|
+
|
|
85
|
+
1. **API debugging**
|
|
86
|
+
Inspect response shape quickly, especially nested data.
|
|
87
|
+
|
|
88
|
+
2. **Backend/frontend integration checks**
|
|
89
|
+
Verify missing fields or type mismatches after API changes.
|
|
90
|
+
|
|
91
|
+
3. **Ad-hoc JSON inspection**
|
|
92
|
+
Visualize JSON copied from logs, queues, or snapshots.
|
|
93
|
+
|
|
94
|
+
4. **Team discussion/demo**
|
|
95
|
+
Share a clearer structure view when discussing payloads.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Command behavior
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Input source:
|
|
106
|
+
|
|
107
|
+
- stdin (pipe)
|
|
108
|
+
- inline argument JSON string
|
|
109
|
+
|
|
110
|
+
Example:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
json '{"ok":true}'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
If no input is provided, it prints usage help.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Release & distribution
|
|
121
|
+
|
|
122
|
+
Included GitHub Actions workflows:
|
|
123
|
+
|
|
124
|
+
- `CI`: basic validation flow
|
|
125
|
+
- `Publish to GitHub Packages`: publish to GPR
|
|
126
|
+
- `Publish to npm (Trusted Publishing)`: reserved for npm OIDC flow
|
|
127
|
+
|
|
128
|
+
Current primary distribution: **GitHub Packages**.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Contributing
|
|
133
|
+
|
|
134
|
+
Issues and PRs are welcome.
|
|
135
|
+
|
|
136
|
+
### Good contribution ideas
|
|
137
|
+
|
|
138
|
+
- Better error diagnostics (e.g. JSON syntax location)
|
|
139
|
+
- Theme switch (light/dark)
|
|
140
|
+
- Direct file path support (e.g. `json ./data.json`)
|
|
141
|
+
- Rich interactions (search, highlight, copy JSON path)
|
|
142
|
+
|
|
143
|
+
### Local development
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm install
|
|
147
|
+
npm test
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Before submitting:
|
|
151
|
+
|
|
152
|
+
- Ensure code runs correctly
|
|
153
|
+
- Ensure README examples still work
|
|
154
|
+
- Keep changes focused and clear
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# json-open
|
|
2
|
+
|
|
3
|
+
把 JSON 直接在浏览器里以可折叠树形结构查看(支持 stdin 和行内 JSON 字符串)。
|
|
4
|
+
|
|
5
|
+
> 一个给命令行用户准备的「JSON 临时观察器」:输入 JSON,立刻打开浏览器查看结构。
|
|
6
|
+
|
|
7
|
+
## 演示
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 为什么做这个
|
|
14
|
+
|
|
15
|
+
在终端里看 JSON 常见痛点:
|
|
16
|
+
|
|
17
|
+
- 内容太长,不好定位
|
|
18
|
+
- 嵌套太深,不直观
|
|
19
|
+
- 临时调试不想上重型工具
|
|
20
|
+
|
|
21
|
+
`json-open` 的目标是:**让“看 JSON”这一步更快更顺手**。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 功能特性
|
|
26
|
+
|
|
27
|
+
- ✅ 支持管道输入:`curl ... | json`
|
|
28
|
+
- ✅ 支持行内 JSON:`json '{"a":1}'`
|
|
29
|
+
- ✅ 浏览器树形展示(可折叠/展开)
|
|
30
|
+
- ✅ 一键全展开 / 全收起
|
|
31
|
+
- ✅ 本地临时文件渲染,不上传数据
|
|
32
|
+
- ✅ 跨平台打开浏览器(macOS / Linux / Windows)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
### 方式一:GitHub Packages(当前主分发)
|
|
39
|
+
|
|
40
|
+
先配置 npm 使用 GitHub Packages:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
echo "@zjy4fun:registry=https://npm.pkg.github.com" >> ~/.npmrc
|
|
44
|
+
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN" >> ~/.npmrc
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
然后安装:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm i -g @zjy4fun/json-open
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 方式二:本地开发安装
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/zjy4fun/json-open.git
|
|
57
|
+
cd json-open
|
|
58
|
+
npm install
|
|
59
|
+
npm link
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
安装后可全局使用 `json` 命令。
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 快速开始
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# 1) API 返回
|
|
70
|
+
curl https://jsonplaceholder.typicode.com/todos/1 | json
|
|
71
|
+
|
|
72
|
+
# 2) 行内 JSON
|
|
73
|
+
json '{"hello":"world","list":[1,2,3]}'
|
|
74
|
+
|
|
75
|
+
# 3) 文件内容
|
|
76
|
+
cat response.json | json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
执行后会自动打开浏览器,展示 JSON 树形视图。
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 常见应用场景
|
|
84
|
+
|
|
85
|
+
1. **API 调试**
|
|
86
|
+
快速查看接口返回结构,尤其是深层嵌套数据。
|
|
87
|
+
|
|
88
|
+
2. **前后端联调**
|
|
89
|
+
接口变更后快速确认字段缺失或类型异常。
|
|
90
|
+
|
|
91
|
+
3. **临时数据检查**
|
|
92
|
+
对日志、队列、快照中的 JSON 做可视化排查。
|
|
93
|
+
|
|
94
|
+
4. **沟通与演示**
|
|
95
|
+
与同事讨论 payload 时更直观。
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 命令行为
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
输入来源:
|
|
106
|
+
|
|
107
|
+
- stdin(管道)
|
|
108
|
+
- 命令行参数中的 JSON 字符串
|
|
109
|
+
|
|
110
|
+
示例:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
json '{"ok":true}'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
如果没有输入,会输出 usage 提示。
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 发布与分发
|
|
121
|
+
|
|
122
|
+
仓库已包含 GitHub Actions:
|
|
123
|
+
|
|
124
|
+
- `CI`:基础校验流程
|
|
125
|
+
- `Publish to GitHub Packages`:发布到 GPR
|
|
126
|
+
- `Publish to npm (Trusted Publishing)`:预留 npm OIDC 发布流程
|
|
127
|
+
|
|
128
|
+
当前主分发方式:**GitHub Packages**。
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 参与贡献
|
|
133
|
+
|
|
134
|
+
欢迎提 Issue / PR。
|
|
135
|
+
|
|
136
|
+
### 建议贡献方向
|
|
137
|
+
|
|
138
|
+
- 更好的错误提示(如 JSON 语法错误定位)
|
|
139
|
+
- 主题切换(亮色/暗色)
|
|
140
|
+
- 支持直接读取文件路径(如 `json ./data.json`)
|
|
141
|
+
- 更丰富交互(搜索、高亮、复制 JSON Path)
|
|
142
|
+
|
|
143
|
+
### 本地开发
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm install
|
|
147
|
+
npm test
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
提交前建议:
|
|
151
|
+
|
|
152
|
+
- 确保代码可运行
|
|
153
|
+
- 确保 README 示例可复现
|
|
154
|
+
- 改动保持聚焦、清晰
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 许可证
|
|
159
|
+
|
|
160
|
+
MIT
|
package/bin/json.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { spawn } from 'node:child_process'
|
|
6
|
+
|
|
7
|
+
function readStdin() {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
let data = ''
|
|
10
|
+
process.stdin.setEncoding('utf8')
|
|
11
|
+
process.stdin.on('data', (chunk) => (data += chunk))
|
|
12
|
+
process.stdin.on('end', () => resolve(data.trim()))
|
|
13
|
+
process.stdin.on('error', reject)
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function escapeHtml(str) {
|
|
18
|
+
return str
|
|
19
|
+
.replaceAll('&', '&')
|
|
20
|
+
.replaceAll('<', '<')
|
|
21
|
+
.replaceAll('>', '>')
|
|
22
|
+
.replaceAll('"', '"')
|
|
23
|
+
.replaceAll("'", ''')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function valueToHtml(value, key = null) {
|
|
27
|
+
const keyHtml = key === null ? '' : `<span class=\"key\">${escapeHtml(String(key))}</span><span class=\"colon\">: </span>`
|
|
28
|
+
|
|
29
|
+
if (value === null) {
|
|
30
|
+
return `<div class=\"line\">${keyHtml}<span class=\"null\">null</span></div>`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const type = typeof value
|
|
34
|
+
|
|
35
|
+
if (type === 'string') {
|
|
36
|
+
return `<div class=\"line\">${keyHtml}<span class=\"string\">\"${escapeHtml(value)}\"</span></div>`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (type === 'number') {
|
|
40
|
+
return `<div class=\"line\">${keyHtml}<span class=\"number\">${String(value)}</span></div>`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (type === 'boolean') {
|
|
44
|
+
return `<div class=\"line\">${keyHtml}<span class=\"boolean\">${String(value)}</span></div>`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
const children = value
|
|
49
|
+
.map((item, idx) => `<li>${valueToHtml(item, idx)}</li>`)
|
|
50
|
+
.join('')
|
|
51
|
+
|
|
52
|
+
return `
|
|
53
|
+
<details open>
|
|
54
|
+
<summary>${keyHtml}<span class=\"symbol\">[ ]</span> <span class=\"meta\">(${value.length} items)</span></summary>
|
|
55
|
+
<ul>${children}</ul>
|
|
56
|
+
</details>
|
|
57
|
+
`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (type === 'object') {
|
|
61
|
+
const entries = Object.entries(value)
|
|
62
|
+
const children = entries
|
|
63
|
+
.map(([childKey, childValue]) => `<li>${valueToHtml(childValue, childKey)}</li>`)
|
|
64
|
+
.join('')
|
|
65
|
+
|
|
66
|
+
return `
|
|
67
|
+
<details open>
|
|
68
|
+
<summary>${keyHtml}<span class=\"symbol\">{ }</span> <span class=\"meta\">(${entries.length} keys)</span></summary>
|
|
69
|
+
<ul>${children}</ul>
|
|
70
|
+
</details>
|
|
71
|
+
`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return `<div class=\"line\">${keyHtml}<span>${escapeHtml(String(value))}</span></div>`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function toHtml(jsonObj) {
|
|
78
|
+
const body = valueToHtml(jsonObj)
|
|
79
|
+
return `<!doctype html>
|
|
80
|
+
<html lang=\"en\">
|
|
81
|
+
<head>
|
|
82
|
+
<meta charset=\"UTF-8\" />
|
|
83
|
+
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
|
|
84
|
+
<title>JSON Response Viewer</title>
|
|
85
|
+
<style>
|
|
86
|
+
:root {
|
|
87
|
+
color-scheme: dark;
|
|
88
|
+
}
|
|
89
|
+
body {
|
|
90
|
+
margin: 0;
|
|
91
|
+
padding: 24px;
|
|
92
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
|
93
|
+
background: #0f172a;
|
|
94
|
+
color: #e2e8f0;
|
|
95
|
+
line-height: 1.5;
|
|
96
|
+
}
|
|
97
|
+
.toolbar {
|
|
98
|
+
position: sticky;
|
|
99
|
+
top: 0;
|
|
100
|
+
background: rgba(15, 23, 42, 0.8);
|
|
101
|
+
backdrop-filter: blur(6px);
|
|
102
|
+
border-bottom: 1px solid #334155;
|
|
103
|
+
padding: 12px 0;
|
|
104
|
+
margin-bottom: 16px;
|
|
105
|
+
display: flex;
|
|
106
|
+
gap: 8px;
|
|
107
|
+
}
|
|
108
|
+
button {
|
|
109
|
+
border: 1px solid #475569;
|
|
110
|
+
background: #1e293b;
|
|
111
|
+
color: #e2e8f0;
|
|
112
|
+
border-radius: 8px;
|
|
113
|
+
padding: 8px 12px;
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
}
|
|
116
|
+
button:hover {
|
|
117
|
+
background: #334155;
|
|
118
|
+
}
|
|
119
|
+
details {
|
|
120
|
+
margin-left: 16px;
|
|
121
|
+
}
|
|
122
|
+
summary {
|
|
123
|
+
cursor: pointer;
|
|
124
|
+
list-style: none;
|
|
125
|
+
}
|
|
126
|
+
summary::-webkit-details-marker {
|
|
127
|
+
display: none;
|
|
128
|
+
}
|
|
129
|
+
summary::before {
|
|
130
|
+
content: '▸';
|
|
131
|
+
margin-right: 6px;
|
|
132
|
+
color: #94a3b8;
|
|
133
|
+
}
|
|
134
|
+
details[open] > summary::before {
|
|
135
|
+
content: '▾';
|
|
136
|
+
}
|
|
137
|
+
ul {
|
|
138
|
+
list-style: none;
|
|
139
|
+
margin: 4px 0 0 12px;
|
|
140
|
+
padding-left: 12px;
|
|
141
|
+
border-left: 1px dashed #334155;
|
|
142
|
+
}
|
|
143
|
+
.key { color: #93c5fd; }
|
|
144
|
+
.colon { color: #94a3b8; }
|
|
145
|
+
.string { color: #86efac; }
|
|
146
|
+
.number { color: #fcd34d; }
|
|
147
|
+
.boolean { color: #f9a8d4; }
|
|
148
|
+
.null { color: #cbd5e1; }
|
|
149
|
+
.symbol { color: #c4b5fd; }
|
|
150
|
+
.meta { color: #64748b; }
|
|
151
|
+
</style>
|
|
152
|
+
</head>
|
|
153
|
+
<body>
|
|
154
|
+
<div class=\"toolbar\">
|
|
155
|
+
<button id=\"expand-all\">Expand all</button>
|
|
156
|
+
<button id=\"collapse-all\">Collapse all</button>
|
|
157
|
+
</div>
|
|
158
|
+
<main>${body}</main>
|
|
159
|
+
<script>
|
|
160
|
+
const details = () => Array.from(document.querySelectorAll('details'))
|
|
161
|
+
document.getElementById('expand-all').addEventListener('click', () => details().forEach((d) => d.open = true))
|
|
162
|
+
document.getElementById('collapse-all').addEventListener('click', () => details().forEach((d) => d.open = false))
|
|
163
|
+
</script>
|
|
164
|
+
</body>
|
|
165
|
+
</html>`
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function openInBrowser(filePath) {
|
|
169
|
+
const platform = process.platform
|
|
170
|
+
const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open'
|
|
171
|
+
|
|
172
|
+
if (platform === 'win32') {
|
|
173
|
+
spawn('cmd', ['/c', command, filePath], { detached: true, stdio: 'ignore' }).unref()
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
spawn(command, [filePath], { detached: true, stdio: 'ignore' }).unref()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function main() {
|
|
181
|
+
const inlineInput = process.argv.slice(2).join(' ').trim()
|
|
182
|
+
|
|
183
|
+
if (!inlineInput && process.stdin.isTTY) {
|
|
184
|
+
console.error('Usage: curl https://example.com | json\n or: json "{\"hello\":\"world\"}"')
|
|
185
|
+
process.exit(1)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const input = inlineInput || (await readStdin())
|
|
189
|
+
|
|
190
|
+
if (!input) {
|
|
191
|
+
console.error('No JSON input received.')
|
|
192
|
+
process.exit(1)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let parsed
|
|
196
|
+
try {
|
|
197
|
+
parsed = JSON.parse(input)
|
|
198
|
+
} catch {
|
|
199
|
+
console.error('Input is not valid JSON.')
|
|
200
|
+
process.exit(1)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const html = toHtml(parsed)
|
|
204
|
+
const filePath = path.join(os.tmpdir(), `json-viewer-${Date.now()}.html`)
|
|
205
|
+
await fs.writeFile(filePath, html, 'utf8')
|
|
206
|
+
openInBrowser(filePath)
|
|
207
|
+
console.log(`Opened JSON viewer: ${filePath}`)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
main().catch((err) => {
|
|
211
|
+
console.error('Unexpected error:', err.message)
|
|
212
|
+
process.exit(1)
|
|
213
|
+
})
|
package/demo.gif
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zjy4fun/json-open",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Open JSON (stdin or inline text) in a browser with collapsible tree view",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"json": "bin/json.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/json.js",
|
|
11
|
+
"test": "node --test"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"json",
|
|
15
|
+
"curl",
|
|
16
|
+
"cli",
|
|
17
|
+
"browser",
|
|
18
|
+
"formatter"
|
|
19
|
+
],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"registry": "https://registry.npmjs.org"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
}
|
|
29
|
+
}
|