Формат ответа
--reporter json использует две формы ответа в зависимости от природы команды.
Общее: метаданные и поле error
Каждый ответ начинается с одинакового набора полей:
{
"chefVersion": "1.10.0", // версия chef
"cwd": "/path/to/project", // рабочая директория, в которой запустили команду
"success": true, // итоговый успех
"command": "build" // 'build' | 'lint' | 'test' | 'typecheck' | 'diag.<sub>'
}error появляется в корне только при катастрофическом сбое до того, как команда успела что-то выполнить (например, cwd не существует, не нашёлся корень проекта, конфликт опций):
{
"success": false,
"error": {
"code": "CF5001",
"message": "Working directory does not exist: /nope"
},
...
}Если команда успела дойти до работы с расширениями и упала на каком-то из них — error корня будет отсутствовать, конкретные ошибки попадут в extensions[].errors[].
Форма 1: операции (build, lint, test, typecheck)
type JsonOperationResult<TDetails, TSummaryExtras = {}> = {
chefVersion: string;
cwd: string;
success: boolean;
command: string;
extensions: JsonExtensionResult<TDetails>[];
notFound: { name: string; reason: string }[];
summary: JsonSummary & TSummaryExtras;
error?: JsonErrorPayload;
};extensions[]— результаты по каждому найденному расширению.notFound[]— имена/паттерны, которые не разрешились в существующие расширения. Не приводят кsuccess: falseсами по себе — потребитель решает, считать ли это ошибкой.summary— агрегаты по расширениям. Счётчикиtotal/passed/failedвсегда считают расширения. Агрегаты по «единицам внутри» (тестам, lint-сообщениям) живут вsummary.<command-specific>(см. ниже).
JsonExtensionResult<TDetails>
type JsonExtensionResult<TDetails> = {
name: string;
path: string; // абсолютный путь к расширению
success: boolean;
durationMs: number;
details: TDetails; // зависит от команды — см. ниже
errors: JsonErrorPayload[];
warnings: JsonErrorPayload[];
};JsonSummary
type JsonSummary = {
total: number; // расширений всего
passed: number;
failed: number;
durationMs: number;
errorCount: number; // суммарно по всем extensions[].errors
warningCount: number;
};Дополнительные поля у конкретных команд:
| Команда | Дополнительные поля summary |
|---|---|
build | — |
lint | fixedCount |
test | tests: { total, passed, failed, skipped } — агрегат по самим тестам |
typecheck | skippedCount — количество расширений, пропущенных как не-TypeScript |
JsonErrorPayload
type JsonErrorPayload = {
code: string; // код из таблицы CF либо ESLint rule id
message: string;
file?: string; // абсолютный путь, где есть локация
line?: number;
column?: number;
frame?: string; // фрагмент исходника с пометкой строки — для build/typecheck/test
};details по командам
build
type BuildDetails = {
bundles: { file: string; size: number }[]; // file — относительный к extensions[].path
dependencies: string[];
standalone: boolean;
};Пример:
{
"name": "ui.buttons",
"path": "/Users/.../ui/install/js/ui/buttons",
"success": true,
"durationMs": 3295,
"details": {
"bundles": [
{ "file": "ui.buttons.bundle.js", "size": 92007 },
{ "file": "ui.buttons.bundle.css", "size": 91087 }
],
"dependencies": ["main.core", "ui.cnt"],
"standalone": false
},
"errors": [],
"warnings": [
{
"code": "CF1006",
"message": "Circular dependency: src/index.js -> src/base-button.js -> src/index.js"
}
]
}lint
type LintDetails = {
errorCount: number;
warningCount: number;
fixedCount: number;
skipped: boolean;
skipReason?: string;
};Сами сообщения linter'а — в errors[] и warnings[] корня extension. Группировку по файлам делайте сами (jq 'group_by(.file)').
typecheck
type TypecheckDetails = {
skipped: boolean; // true для не-TS расширений
skipReason?: string;
errorCount: number;
};TS-ошибки — в errors[] корня extension, с полем frame (фрагмент исходника).
test
type TestDetails = {
unit: TestKindDetails;
e2e: TestKindDetails;
};
type TestKindDetails = {
ran: boolean;
skipReason?: string;
durationMs: number;
browsers: string[]; // 'chromium', 'firefox', 'webkit' — реальные имена
passed: number;
failed: number;
skipped: number;
total: number;
tests: TestEntry[];
consoleLogs: { type: string; text: string }[];
};
type TestEntry = {
suite: string[]; // путь suite через describe/context
title: string;
status: 'passed' | 'failed' | 'skipped';
results: Record<string, BrowserTestResult>; // ключ — имя браузера
};
type BrowserTestResult = {
status: 'passed' | 'failed' | 'skipped';
durationMs?: number;
failure?: TestFailure;
};
type TestFailure = {
message: string;
file?: string;
line?: number;
column?: number;
frame?: string; // фрагмент исходника с указателем
diff?: { actual: unknown; expected: unknown }; // для assert.deepEqual и т.п.
};По умолчанию unit-тесты прогоняются во всех браузерах из playwright.config.ts. --project chromium сужает до одного.
В корневом extensions[].errors[] для каждого упавшего теста создаётся запись с frame/file/line/column — без полного стек-трейса.
Форма 2: отчёты (diag.*)
type JsonReportResult<TData> = {
chefVersion: string;
cwd: string;
success: boolean;
command: string; // 'diag.top-used', 'diag.circular-deps', ...
data: TData; // см. таблицу ниже
durationMs: number;
error?: JsonErrorPayload;
};В отличие от операций нет extensions[] — вместо него один блок data со специфичной для подкоманды структурой.
| Команда | data |
|---|---|
diag top-used | { scanned, results: { name, dependents }[] } |
diag top-deps | { scanned, results: { name, directDeps }[] } |
diag top-deps-tree | { scanned, results: { name, treeSize }[] } |
diag top-bundle-size | { scanned, sortBy, results: { name, js, css, assets, total }[] } |
diag top-total-size | { scanned, sortBy, results: { name, ownTotal, total, directDeps, treeDeps }[] } |
diag unused-deps | { scanned, results: { name, unused: string[] }[] } |
diag unused | { scanned, results: { name }[] } |
diag circular-deps | { scanned, results: { name, cycles: string[][] }[] } |
diag circular-imports | { scanned, results: { name, cycles: string[][] }[] } |
diag find-usages | { extension, summary: { totalUsages, totalFiles, totalModules, byType, imports[], namespaces[], inheritance[], topModules[], locationsByType }, usages: { type, file, line, content, details? }[] } |
diag find-loaders | { extension, totalLoaders, totalFiles, totalModules, byType, loaders: { type, file, line, content }[] } |
diag deps-tree | tagged union по mode: 'tree' | 'flat' | 'why' | 'not-found' |
diag bundle-size | { extension, own, dependencies?, total? } либо { extension, notFound: true } |
diag config | tagged union по mode: 'match' | 'except' | 'missing' |
Конкретные поля смотрите в TypeScript-определениях src/reporters/json/diag.ts.