ویرایشگرها، خط فرمان و زنجیرههای خودکارسازی
جمعبندی: ویرایشگر «روشن دیدن و درست ویرایش کردن» را حل میکند و خط فرمان «دستهای و تکرارپذیر» را. هر سو چند ابزار دارد که یک دهه مفید میمانند.

جمعبندی: ویرایشگر را بر اساس سناریو انتخاب کنید — CodeMirror 6 برای سایتهای ابزاری و موبایل، Monaco فقط وقتی به تجربه کامل IDE نیاز دارید؛ کارهای دستهای را به jq بههمراه سه بررسی CI برای نحو، ساختار و سبک بسپارید و همیشه آرگومانها را با jq --arg بفرستید.
عصر بررسی دستی JSON باید به پایان رسیده باشد: هر کاری که ماشین میتواند تکرار کند، به چشم انسان نسپارید.
۱. ویرایشگرها در مرورگر
۱.۱ حداقل ادغام CodeMirror 6
import { EditorView, basicSetup } from 'codemirror';
import { json, jsonParseLinter } from '@codemirror/lang-json';
import { linter, lintGutter } from '@codemirror/lint';
const view = new EditorView({
parent: document.getElementById('editor')!,
doc: '{"hello": "json"}',
extensions: [
basicSetup,
json(),
linter(jsonParseLinter()),
lintGutter(),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
// Debounce before doing heavy work; do not format on every keystroke
scheduleParse(update.state.doc.toString());
}
}),
],
});
چهار نکته:
۱. json() برجستهسازی نحو و تطبیق براکت میدهد — خودتان ننویسید؛
۲. jsonParseLinter() نشانگر خطا در سطح خط/ستون تولید میکند؛ آن را با lintGutter() جفت کنید تا آیکونها کنار شماره خطوط نمایش داده شوند؛
۳. updateListener باید debounce شود؛ صدا زدن JSON.parse روی صد هزار خط در هر فشردن کلید بهطور محسوس تأخیر ایجاد میکند؛
۴. اسناد بزرگ به بهینهسازیهایی فراتر از EditorView.lineWrapping نیاز دارند تا از رندر یکجای همه جلوگیری شود (CodeMirror 6 بهطور پیشفرض بر اساس viewport رندر میکند، که بهبودی نسبت به نسخههای قدیمی است).
۱.۲ Folding و outline
CodeMirror 6 folding داخلی دارد؛ در سناریوهای JSON معمولاً بر اساس «object/array» جمع میکنید:
import { foldGutter, foldKeymap } from '@codemirror/language';
import { keymap } from '@codemirror/view';
با foldGutter() و keymap.of(foldKeymap)، کاربران میتوانند آرایههای بلند را با میانبر صفحهکلید جمع کنند.
۱.۳ انتخاب میان CodeMirror و Monaco
| بُعد | CodeMirror 6 | Monaco |
|---|---|---|
| اندازه بسته | کوچک، ماژولار | بزرگ (چند مگابایت) |
| موبایل | پشتیبانی خوب | تجربه متوسط |
| سرویسهای زبانی | باید خودتان بسازید | غنی، داخلی |
| هزینه سفارشیسازی | کم | متوسط |
سایتهای ابزاری و موبایلمحور → CodeMirror؛ تجربه کامل IDE (چند فایل، go-to-definition، تکمیل) → Monaco.
۲. خط فرمان: شش دستور پرتکرار jq
jq برای JSON همان چیزی است که sed + awk برای متن است؛ یادگیری بخش کوچکی از نحو آن ۸۰٪ نیازهای روزمره را پوشش میدهد.
# 1. Pick fields (the most common)
jq '.data.items[].name' response.json
# 2. Filter + reshape
jq '.data.items[] | select(.price < 10) | {id, price}' response.json
# 3. Aggregates (length, sum, max)
jq '[.items[] | .price] | {count: length, total: add, max: max}' response.json
# 4. Group and aggregate
jq '[.items[]] | group_by(.category) | map({category: .[0].category, count: length})' response.json
# 5. Merge multiple files into an array
jq -s '.' part-*.json > merged.json
# 6. Process JSON Lines line by line
jq -c 'select(.level == "error")' app.jsonl
# Common output switches
jq -r '.data.name' # Strip the quotes so it pipes cleanly into the shell
jq -c '.' # Compact output, one line
jq --arg id "$ID" '.id == $id' # Pass arguments safely, avoiding injection through concatenation
--arg شایسته ذکر ویژه است: چسباندن مستقیم متغیر در عبارت بهمحض اینکه مقدار گیومه داشته باشد میشکند، در حالی که --arg میگذارد jq خودش escape را مدیریت کند.
۲.۱ ابزارهای دیگر در یک نگاه
| ابزار | کاربرد | در یک جمله |
|---|---|---|
yq |
تبدیل YAML/JSON/XML | نحوی شبیه jq، اسب کار فایلهای پیکربندی |
dasel |
پرسوجوی یکسان میان قالبها | JSON/YAML/TOML/XML/CSV را پشتیبانی میکند |
fx |
مرور تعاملی JSON | در ترمینال کلیک کنید، خوب برای کاوش |
jless |
مرور صفحهبندیشده در ترمینال | مانند less، با folding و جستوجو |
gron |
تخت کردن JSON به خطوط قابل grep | ابزار عالی برای فهمیدن «این فیلد در کدام سطح است» |
ajv-cli |
اعتبارسنجی Schema | اعتبارسنجی فایلهای JSON در CI |
ایده پشت gron بهویژه ارزش دانستن دارد:
$ echo '{"a":{"b":[1,2]}}' | gron
json = {};
json.a = {};
json.a.b = [];
json.a.b[0] = 1;
json.a.b[1] = 2;
پس از تخت شدن، grep میتواند مسیر را پیدا کند، که بسیار سریعتر از گشودن لایهبهلایه در ویرایشگر است.
۳. سه لایه اعتبارسنجی در CI
| لایه | ابزار | شکست چه معنایی دارد |
|---|---|---|
| نحو | JSON.parse / jq empty |
خود فایل بدشکل است |
| ساختار | ajv-cli + schema |
فیلدهای غایب، نوعهای نادرست |
| سبک | Prettier | قالببندی ناهمگون، diffهای پرنویز |
# Syntax: batch-validate every json file in the repository
find . -name '*.json' -not -path './node_modules/*' -print0 \
| xargs -0 -n1 jq empty
# Structure: validate against a schema
npx ajv-cli validate -s schemas/order.json -d 'fixtures/orders/*.json' --strict=false
# Style: check only, do not modify
npx prettier --check "**/*.json"
به هر سه لایه نیاز دارید، چون پیامهای خطایشان کاملاً متفاوت است:
jq empty فقط میگوید «کدام خط شکسته است»، ajv میگوید «کدام فیلد کدام کلمه کلیدی را نقض میکند» و Prettier فقط میگوید «قالببندی ناهمگون است».
۴. قلابهای pre-commit
# .husky/pre-commit example (excerpt)
npx lint-staged
{
"lint-staged": {
"*.json": ["prettier --write", "jq empty"]
}
}
با فایلهای پیکربندی ابزار مانند package.json و tsconfig.json مراقب باشید؛ نگذارید Prettier بیدلیل تورفتگیشان را عوض کند —
برخی ابزارها به قالببندی حساساند (JSON معتبر نباید باشد، اما در عمل این مشکل ایجاد کرده است). میتوانید آنها را جداگانه حذف کنید.
۵. نکات اشکالزدایی
۱. از jq -e بهعنوان assertion استفاده کنید: وقتی نتیجه عبارت false یا null است، کد خروج ۱ میشود، پس میتواند مستقیم بهعنوان بررسی در اسکریپتها برود؛
۲. jq 'paths' همه مسیرها را فهرست میکند و نمای کلی سریعی از ساختار سند ناآشنا میدهد؛
۳. jq '.. | objects | keys' هر نام کلیدی را که تاکنون ظاهر شده پیدا میکند، که برای شکار املای ناهمگون کاربردی است؛
۴. پیش از مقایسه در ویرایشگر قالببندی کنید: diff قالببندینشده نویز خالص است؛ مقایسه ساختاری مانند /diff دقیقتر است.
۶. چکلیست
- [ ] نشانگرهای خطای ویرایشگر تا خط و ستون دقیقاند و تجزیه debounce میشود؛
- [ ] اسکریپتهای تیم آرگومانها را با
jq --argمیفرستند نه با چسباندن رشته؛ - [ ] CI هر سه لایه اعتبارسنجی را دارد (نحو/ساختار/سبک)؛
- [ ] pre-commit فقط فایلهای staged را پردازش میکند تا اسکن کامل commitها را کند نکند؛
- [ ] نسخه ابزارها در
package.jsonیا.tool-versionsتثبیت شده است.
پرسشهای متداول
چگونه میان CodeMirror 6 و Monaco انتخاب کنم؟
برای سایتهای ابزاری و موبایل CodeMirror 6 را ترجیح دهید: کوچک، ماژولار و با پشتیبانی خوب در موبایل است و برجستهسازی نحو، تطبیق براکت و folding را میتوان بر اساس نیاز ترکیب کرد. فقط وقتی به تجربه کامل IDE با چند فایل، go-to-definition و تکمیل نیاز دارید به Monaco بروید — به قیمت بسته چندمگابایتی و تلاش بیشتر برای سفارشیسازی.
چرا CI به بررسی نحو، ساختار و سبک با هم نیاز دارد؟
سه لایه خطاهای کاملاً متفاوتی گزارش میکنند: jq empty میگوید کدام خط شکسته است، ajv میگوید کدام فیلد کدام کلمه کلیدی را نقض میکند و Prettier فقط میگوید قالببندی ناهمگون است. حذف یک لایه باعث میشود خطاهای بقیه بهآسانی بد خوانده شوند، پس هر سه را نگه دارید و بهمحض شکست یک لایه متوقف شوید.
آیا بهجای اعتبارسنجی در CI به pre-commit تکیه کنم؟
نه. قلابها فقط فایلهای staged را پردازش میکنند، که بازخوردی در یک دقیقه میدهد اما نمیتواند کل مخزن یا تغییرات دورزننده از commit را پوشش دهد. CI مسئول کامل بودن است: pre-commit را روی فایلهای staged نگه دارید و اسکن کامل را به CI بسپارید.