@nitra/cursor 5.2.0 → 5.3.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.
- package/CHANGELOG.md +13 -0
- package/lib/docs/models.md +24 -17
- package/lib/llm.mjs +60 -47
- package/lib/omlx-trace.mjs +158 -0
- package/lib/omlx.mjs +49 -11
- package/package.json +1 -1
- package/rules/changelog/js/docs/consistency.md +36 -383
- package/rules/feedback/docs/fix.md +21 -131
- package/rules/ga/docs/fix.md +14 -12
- package/rules/ga/js/docs/lint.md +12 -9
- package/rules/ga/js/docs/workflows.md +20 -19
- package/rules/graphql/js/docs/tooling.md +18 -253
- package/rules/hasura/docs/fix.md +18 -111
- package/rules/js-bun-db/js-bun-db.mdc +7 -7
- package/rules/js-lint/js-lint.mdc +14 -1
- package/rules/js-run/js-run.mdc +16 -16
- package/rules/style-lint/js/tooling.mjs +13 -4
- package/rules/style-lint/style-lint.mdc +1 -1
- package/rules/test/js/data/stryker_config/stryker.config.baseline.mjs +1 -1
- package/rules/test/js/data/stryker_config/stryker.config.vue.baseline.mjs +1 -1
- package/rules/test/js/stryker_config.mjs +33 -5
- package/rules/test/js/vitest-config-pool-forks.mjs +11 -7
- package/rules/test/test.mdc +9 -9
- package/rules/vue/vue.mdc +6 -6
- package/scripts/coverage-classify/index.mjs +3 -15
- package/skills/doc-files/js/docgen-extract-anchors.mjs +28 -2
- package/skills/doc-files/js/docgen-extract.mjs +24 -1
- package/skills/doc-files/js/docgen-gen.mjs +54 -9
- package/skills/doc-files/js/docgen-prompts.mjs +28 -16
- package/skills/doc-files/js/units-js.mjs +139 -0
- package/skills/doc-files/js/units.mjs +19 -0
- package/skills/fix/js/llm-worker.mjs +10 -22
|
@@ -1,387 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/rules/changelog/js/consistency.mjs
|
|
4
|
+
crc: eaf98d6d
|
|
5
|
+
score: 100
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# consistency.mjs
|
|
2
9
|
|
|
3
10
|
## Огляд
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
`opts`:
|
|
37
|
-
|
|
38
|
-
- `opts.getPublishedVersion?: (name: string, kind?: 'npm' | 'python') => Promise<string | null>` — перевизначення стандартного резолвера опублікованої версії (для юніт-тестів, оффлайн-режимів).
|
|
39
|
-
- `opts.cwd?: string` — корінь репозиторію; за замовчуванням `process.cwd()`.
|
|
40
|
-
- `opts.autofix?: boolean` — autofix-режим. За замовчуванням береться з env `N_CURSOR_CHANGELOG_AUTOFIX === '1'` (виставляє лише крок `npm-changelog` у `hk.pkl` для pre-commit). Коли увімкнено, замість `fail` на відсутній change-файл правило створює його через `writeChange()` з дефолтами (`bump=patch`, `section=Changed`, `message` = subject останнього коміту, fallback — назва гілки чи `оновлення`) і ставить у git-індекс (`git add`), тож коміт не падає. **Жодної мережі:** у autofix-режимі published-перевірка пропускає реєстровий резолв (`npm view` / PyPI fetch) і drift-перевірку version vs опублікована — лишається лише наявність change-файлу та git-diff. Ручний bump `version` у хуці не ловиться; його далі ловить CI та ручний `fix changelog` без env. Поза хуком режим вимкнено — поведінка лишається fail-on-missing з повною drift-перевіркою, щоб CI не плодив артефактів.
|
|
41
|
-
|
|
42
|
-
Повертає **exit-код** (0 — pass, ≠ 0 — fail), отриманий від `createCheckReporter()`.
|
|
43
|
-
|
|
44
|
-
## Функції
|
|
45
|
-
|
|
46
|
-
### `gitOrNull(args, cwd)`
|
|
47
|
-
|
|
48
|
-
- **Сигнатура:** `async (args: string[], cwd: string) => Promise<string | null>`
|
|
49
|
-
- **Параметри:** `args` — аргументи `git`; `cwd` — робочий каталог процесу.
|
|
50
|
-
- **Повертає:** `stdout` команди або `null` при будь-якій помилці.
|
|
51
|
-
- **Side effects:** виконує дочірній процес `git` через `execFile`.
|
|
52
|
-
|
|
53
|
-
Тиха обгортка над `git`, що ковтає виключення — використовується скрізь, де відсутність гілки/ref/маніфесту є штатним кейсом.
|
|
54
|
-
|
|
55
|
-
### `isInsideGitRepo(cwd)`
|
|
56
|
-
|
|
57
|
-
- **Сигнатура:** `async (cwd: string) => Promise<boolean>`
|
|
58
|
-
- **Повертає:** `true`, якщо `cwd` всередині git working tree.
|
|
59
|
-
- **Side effects:** запит `git rev-parse --is-inside-work-tree`.
|
|
60
|
-
|
|
61
|
-
### `currentBranchName(cwd)`
|
|
62
|
-
|
|
63
|
-
- **Сигнатура:** `async (cwd: string) => Promise<string | null>`
|
|
64
|
-
- **Повертає:** ім'я поточної гілки (`git rev-parse --abbrev-ref HEAD`) або `null`.
|
|
65
|
-
|
|
66
|
-
### `baseRefLabel(ref)`
|
|
67
|
-
|
|
68
|
-
- **Сигнатура:** `(ref: string) => string`
|
|
69
|
-
- **Параметри:** `ref` — git-ref.
|
|
70
|
-
- **Повертає:** човничок без префіксу `origin/` (наприклад, `origin/main` → `main`); інакше повертає `ref` без змін.
|
|
71
|
-
- **Side effects:** немає.
|
|
72
|
-
|
|
73
|
-
### `isGitAncestor(ancestor, descendant, cwd)`
|
|
74
|
-
|
|
75
|
-
- **Сигнатура:** `async (ancestor: string, descendant: string, cwd: string) => Promise<boolean>`
|
|
76
|
-
- **Повертає:** `true`, якщо `ancestor` є предком `descendant` (через `git merge-base --is-ancestor`).
|
|
77
|
-
- **Зауваження:** `git merge-base --is-ancestor` повертає exit-код, тому всередині використовується `gitOrNull`, який ловить ненульовий exit і повертає `null` — у такому випадку результат функції `false`.
|
|
78
|
-
|
|
79
|
-
### `resolveBranchRef(branchName, cwd)`
|
|
80
|
-
|
|
81
|
-
- **Сигнатура:** `async (branchName: string, cwd: string) => Promise<string | null>`
|
|
82
|
-
- **Поведінка:** для `branchName` пробує спочатку локальний ref, потім `origin/<branchName>`; повертає перший, що верифікується через `git rev-parse --verify --quiet`.
|
|
83
|
-
|
|
84
|
-
### `isChangelogIgnoredPath(relPath)`
|
|
85
|
-
|
|
86
|
-
- **Сигнатура:** `(relPath: string) => boolean`
|
|
87
|
-
- **Поведінка:** нормалізує шлях до posix (заміна `\` на `/`, обрізання провідного `./`), повертає `true`, якщо починається з одного з префіксів `CHANGELOG_IGNORE_PATH_PREFIXES`.
|
|
88
|
-
|
|
89
|
-
### `isPathGitIgnored(relPath, cwd)`
|
|
90
|
-
|
|
91
|
-
- **Сигнатура:** `async (relPath: string, cwd: string) => Promise<boolean>`
|
|
92
|
-
- **Поведінка:** виконує `git check-ignore -q -- <relPath>`. Exit-код 0 → ignored (повертає `true`); будь-яка помилка → `false`.
|
|
93
|
-
- **Side effects:** дочірній процес `git`.
|
|
94
|
-
|
|
95
|
-
### `resolveMergeBase(baseRef, cwd)`
|
|
96
|
-
|
|
97
|
-
- **Сигнатура:** `async (baseRef: string, cwd: string) => Promise<string | null>`
|
|
98
|
-
- **Повертає:** SHA `git merge-base baseRef HEAD` або `null`.
|
|
99
|
-
|
|
100
|
-
### `resolveChangelogComparisonPoint(branch, cwd)`
|
|
101
|
-
|
|
102
|
-
- **Сигнатура:** `async (branch: string | null, cwd: string) => Promise<{ ref: string, label: string } | null>`
|
|
103
|
-
- **Логіка:**
|
|
104
|
-
- якщо `branch === 'dev'` → `null` (local-only пропускається);
|
|
105
|
-
- якщо `branch === 'main'`:
|
|
106
|
-
- якщо `origin/main` верифіковано і `origin/main === HEAD` або `origin/main` — предок `HEAD` → `{ ref: 'origin/main', label: 'main' }`;
|
|
107
|
-
- інакше `HEAD~1` → `{ ref: <sha>, label: 'main~1' }`;
|
|
108
|
-
- якщо ні те, ні те — `null`.
|
|
109
|
-
- feature-гілки: ітерує по `FEATURE_BASE_BRANCH_CANDIDATES` (`['dev', 'main']`); перший, для якого вдається резолвити ref **і** обчислити merge-base, дає `{ ref: <merge-base SHA>, label: baseRefLabel(...) }`.
|
|
110
|
-
- **Повертає:** опис точки порівняння (`ref` для `git diff`/`git show`, `label` для повідомлень) або `null`.
|
|
111
|
-
|
|
112
|
-
### `pathspecForWorkspace(ws, subWorkspaces)`
|
|
113
|
-
|
|
114
|
-
- **Сигнатура:** `(ws: string, subWorkspaces: string[]) => string[]`
|
|
115
|
-
- **Поведінка:**
|
|
116
|
-
- для `ws !== '.'` → `[\`${ws}/\`]`;
|
|
117
|
-
- для `ws === '.'` (корінь монорепо) → `['.', ':(exclude)<sub>/' для кожного підворкспейсу]`, щоб залишити лише файли кореня без вкладених воркспейсів.
|
|
118
|
-
- **Повертає:** масив pathspec-ів для передачі в `git diff -- <pathspec>`.
|
|
119
|
-
|
|
120
|
-
### `splitNulPaths(nulSeparated)`
|
|
121
|
-
|
|
122
|
-
- **Сигнатура:** `(nulSeparated: string | null) => string[]`
|
|
123
|
-
- **Поведінка:** ділить вхідний рядок по `\0`, відкидає порожні елементи.
|
|
124
|
-
- **Чому `-z`:** без прапорця git застосовує `core.quotePath` і повертає не-ASCII імена (наприклад, кирилицю) у C-quoted формі (`"docs/\320\262..."`), що ламає префіксне порівняння для `CHANGELOG_IGNORE_PATH_PREFIXES`.
|
|
125
|
-
|
|
126
|
-
### `listChangedPathsAgainstBase(baseRef, pathspec, cwd)`
|
|
127
|
-
|
|
128
|
-
- **Сигнатура:** `async (baseRef: string, pathspec: string[], cwd: string) => Promise<string[]>`
|
|
129
|
-
- **Поведінка:** об'єднує два джерела через `Set`:
|
|
130
|
-
- `git diff --name-only -z <baseRef> -- <pathspec>` — закомічені/staged зміни;
|
|
131
|
-
- `git ls-files --others --exclude-standard -z -- <pathspec>` — нові untracked-файли.
|
|
132
|
-
- **Повертає:** дедуплікований масив відносних шляхів.
|
|
133
|
-
|
|
134
|
-
### `workspaceHasRelevantChangesAgainstBase(baseRef, ws, subWorkspaces, cwd)`
|
|
135
|
-
|
|
136
|
-
- **Сигнатура:** `async (baseRef: string, ws: string, subWorkspaces: string[], cwd: string) => Promise<boolean>`
|
|
137
|
-
- **Поведінка:** обчислює pathspec для `ws`, отримує всі змінені шляхи, ітерує по них:
|
|
138
|
-
- інверсія (`docs/`, `.cursor/`, ...) → пропустити;
|
|
139
|
-
- `git check-ignore` → пропустити;
|
|
140
|
-
- інакше — повернути `true`.
|
|
141
|
-
- **Повертає:** `true`, якщо є хоч один шлях, що вважається релевантною змінною.
|
|
142
|
-
|
|
143
|
-
### `readBaseVersion(baseRef, manifest, cwd)`
|
|
144
|
-
|
|
145
|
-
- **Сигнатура:** `async (baseRef: string, manifest: PackageManifest, cwd: string) => Promise<string | null>`
|
|
146
|
-
- **Поведінка:** виконує `git show <baseRef>:<wsPath>`, де `wsPath` — шлях до маніфесту відносно репозиторію; парсить:
|
|
147
|
-
- `npm` → `JSON.parse(...).version` (`null` при помилці парсу);
|
|
148
|
-
- `python` → `parsePyprojectFields(out).version`.
|
|
149
|
-
- **Повертає:** версію з маніфесту на `baseRef` або `null`.
|
|
150
|
-
|
|
151
|
-
### `defaultGetPublishedNpmVersion(name)`
|
|
152
|
-
|
|
153
|
-
- **Сигнатура:** `async (name: string) => Promise<string | null>`
|
|
154
|
-
- **Поведінка:** `npm view <name> version` із таймаутом `REGISTRY_TIMEOUT_MS` (10 с). Trim і повернення; пуста відповідь / помилка → `null`.
|
|
155
|
-
- **Side effects:** дочірній процес `npm`, мережа.
|
|
156
|
-
|
|
157
|
-
### `defaultGetPublishedPyPiVersion(name)`
|
|
158
|
-
|
|
159
|
-
- **Сигнатура:** `async (name: string) => Promise<string | null>`
|
|
160
|
-
- **Поведінка:** `fetch('https://pypi.org/pypi/<encodedName>/json', { signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS) })`; читає `data.info.version`. Будь-яка помилка / `!res.ok` → `null`.
|
|
161
|
-
- **Side effects:** мережа.
|
|
162
|
-
|
|
163
|
-
### `resolvePublishedVersion(manifest, getPublishedVersion)`
|
|
164
|
-
|
|
165
|
-
- **Сигнатура:** `(manifest: PackageManifest, getPublishedVersion) => Promise<string | null>`
|
|
166
|
-
- **Поведінка:** якщо в маніфесті немає `name` → `Promise.resolve(null)`; інакше делегує до `getPublishedVersion(name, kind)`.
|
|
167
|
-
|
|
168
|
-
### `defaultGetPublishedVersion(name, kind = 'npm')`
|
|
169
|
-
|
|
170
|
-
- **Сигнатура:** `(name: string, kind?: 'npm' | 'python') => Promise<string | null>`
|
|
171
|
-
- **Поведінка:** диспетчер за `kind` (Python → PyPI, інакше — npm).
|
|
172
|
-
|
|
173
|
-
### `createDefaultGetPublishedVersion()`
|
|
174
|
-
|
|
175
|
-
- **Сигнатура:** `() => (name, kind?) => Promise<string | null>`
|
|
176
|
-
- **Поведінка:** фабрика, що повертає `defaultGetPublishedVersion`. Використовується як дефолт у `check` для зручної підміни в тестах.
|
|
177
|
-
|
|
178
|
-
### `checkNpmFilesArrayContainsChangelog(manifest, pass, fail)`
|
|
179
|
-
|
|
180
|
-
- **Сигнатура:** `(manifest: PackageManifest, pass: (msg)=>void, fail: (msg)=>void) => void`
|
|
181
|
-
- **Поведінка:**
|
|
182
|
-
- якщо `kind !== 'npm'` або `npmFiles` відсутній — рання терміновка;
|
|
183
|
-
- `pass`, якщо `npmFiles` містить `'CHANGELOG.md'`;
|
|
184
|
-
- інакше `fail` з рекомендацією додати рядок.
|
|
185
|
-
|
|
186
|
-
### `workspaceLabel(manifest)`
|
|
187
|
-
|
|
188
|
-
- **Сигнатура:** `(manifest: PackageManifest) => string`
|
|
189
|
-
- **Повертає:** `'<root>'` для `ws === '.'`, інакше `manifest.ws`.
|
|
190
|
-
|
|
191
|
-
### `missingChangeFileMessage(label, mf)`
|
|
192
|
-
|
|
193
|
-
- **Сигнатура:** `(label: string, mf: string) => string`
|
|
194
|
-
- **Повертає:** уніфікований текст для `fail` про відсутній change-файл, включно з інструкцією для `npx @nitra/cursor change`.
|
|
195
|
-
|
|
196
|
-
### `hasPendingChangeFiles(ws, cwd)`
|
|
197
|
-
|
|
198
|
-
- **Сигнатура:** `async (ws: string, cwd: string) => Promise<boolean>`
|
|
199
|
-
- **Поведінка:** `(await readChangeFiles(ws, cwd)).length > 0`.
|
|
200
|
-
|
|
201
|
-
### `checkPublishedWorkspacePendingGitChanges(manifest, _Vcurrent, subWorkspaces, pass, fail, cwd)`
|
|
202
|
-
|
|
203
|
-
- **Сигнатура:** `async (...) => Promise<void>`
|
|
204
|
-
- **Параметр `_Vcurrent`:** ігнорується (залишений для сумісності сигнатури; bump робить CI).
|
|
205
|
-
- **Поведінка:**
|
|
206
|
-
1. Якщо `hasPendingChangeFiles` → `pass` про change-файл(и) + перевірка `CHANGELOG.md` у `files` npm-маніфесту. Вихід.
|
|
207
|
-
2. Якщо не в git-репі — вихід без перевірок.
|
|
208
|
-
3. Беремо `currentBranchName`:
|
|
209
|
-
- `branch === 'dev'`: лише перевірка наявності релевантних змін відносно `HEAD` (staged/working tree). Є — `fail` `missingChangeFileMessage`; нема — мовчазний вихід.
|
|
210
|
-
- інакше: резолвимо `comparison`; якщо `comparison` + є зміни відносно `comparison.ref` → `fail`.
|
|
211
|
-
- на `main` додатково перевіряємо ще й `HEAD` (working/staged) — `fail`, якщо є зміни.
|
|
212
|
-
|
|
213
|
-
### `checkPublishedWorkspace(manifest, subWorkspaces, getPublishedVersion, pass, fail, cwd)`
|
|
214
|
-
|
|
215
|
-
- **Сигнатура:** `async (...) => Promise<void>`
|
|
216
|
-
- **Поведінка:**
|
|
217
|
-
1. `manifest.version` відсутній → `fail` («у маніфесті відсутнє поле version»). Вихід.
|
|
218
|
-
2. `manifest.name` відсутній → `fail` («відсутнє ім'я пакета»). Вихід.
|
|
219
|
-
3. `Vpublished = resolvePublishedVersion(...)`; якщо `null` → `pass` («опублікована версія недоступна, перевірку пропущено»). Вихід.
|
|
220
|
-
4. Якщо `Vpublished !== Vcurrent` → `fail` про drift (ручний bump заборонено — навіть із change-файлом). Вихід.
|
|
221
|
-
5. Інакше `pass` про збіг із реєстром і виклик `checkPublishedWorkspacePendingGitChanges`.
|
|
222
|
-
|
|
223
|
-
### `checkLocalOnlyChangedWorkspace(comparisonRef, manifest, baseLabel, pass, fail, cwd)`
|
|
224
|
-
|
|
225
|
-
- **Сигнатура:** `async (...) => Promise<void>`
|
|
226
|
-
- **Поведінка** (виконується для воркспейсів, де `workspaceHasRelevantChangesAgainstBase` дала `true`):
|
|
227
|
-
1. `Vbase = readBaseVersion(comparisonRef, manifest, cwd)`.
|
|
228
|
-
2. Якщо `Vbase && Vcurrent && Vbase !== Vcurrent` → `fail` про drift (`Vbase → Vcurrent`). Вихід.
|
|
229
|
-
3. Якщо `hasPendingChangeFiles` → `pass`. Вихід.
|
|
230
|
-
4. Інакше `fail` `missingChangeFileMessage`.
|
|
231
|
-
- Drift-перевірка йде **перед** перевіркою наявності change-файлу: симетрія з registry-published-шляхом (ручний bump заборонено навіть із change-файлом).
|
|
232
|
-
|
|
233
|
-
### `runLocalOnlyChecks(localOnly, subWorkspaces, pass, fail, cwd)`
|
|
234
|
-
|
|
235
|
-
- **Сигнатура:** `async (localOnly: PackageManifest[], subWorkspaces: string[], pass, fail, cwd) => Promise<void>`
|
|
236
|
-
- **Поведінка:**
|
|
237
|
-
1. Якщо `localOnly` пустий → ранній вихід.
|
|
238
|
-
2. Не git-репозиторій → `pass` про пропуск.
|
|
239
|
-
3. `branch === 'dev'` → `pass` про пропуск.
|
|
240
|
-
4. `comparison` не знайдено (немає `dev`/`main`/`origin/*`) → `pass` про пропуск.
|
|
241
|
-
5. Для кожного `manifest` із `localOnly`: пропустити, якщо немає релевантних змін відносно `comparison.ref`; інакше виставити `checkedAny = true` і викликати `checkLocalOnlyChangedWorkspace`.
|
|
242
|
-
6. Якщо жоден воркспейс не змінено — `pass` («local-only воркспейси без змін відносно `<label>`»).
|
|
243
|
-
|
|
244
|
-
### `check(opts)`
|
|
245
|
-
|
|
246
|
-
- **Сигнатура:** `async (opts?: { getPublishedVersion?, cwd? }) => Promise<number>`
|
|
247
|
-
- **Покрокове виконання:**
|
|
248
|
-
1. Створюється `reporter = createCheckReporter()`; беруться його `pass` і `fail`.
|
|
249
|
-
2. `getPublishedVersion` — з `opts` або `createDefaultGetPublishedVersion()`.
|
|
250
|
-
3. `cwd` — з `opts` або `process.cwd()`.
|
|
251
|
-
4. `workspaces = await getMonorepoProjectRootDirs(cwd)`; `subWorkspaces = workspaces.filter(w => w !== '.')`.
|
|
252
|
-
5. `isMonorepoRoot = subWorkspaces.length > 0` — корінь монорепо вважається glue/конфіг/tooling.
|
|
253
|
-
6. Розділяємо воркспейси на `published` та `localOnly`:
|
|
254
|
-
- корінь `.` за наявності підпакетів → одразу `pass` про пропуск, не читаємо маніфест;
|
|
255
|
-
- `readPackageManifest(ws, cwd)` → якщо `null`, ws пропускається;
|
|
256
|
-
- `manifest.registryPublishable === true` → у `published`, інакше — у `localOnly`.
|
|
257
|
-
7. Послідовно перевіряємо всі `published` через `checkPublishedWorkspace`.
|
|
258
|
-
8. `runLocalOnlyChecks(localOnly, ...)`.
|
|
259
|
-
9. Повертаємо `reporter.getExitCode()`.
|
|
260
|
-
|
|
261
|
-
## Залежності
|
|
262
|
-
|
|
263
|
-
### Стандартна бібліотека Node.js
|
|
264
|
-
|
|
265
|
-
- `node:child_process` → `execFile` — запуск `git`, `npm` без shell-інтерполяції.
|
|
266
|
-
- `node:util` → `promisify` — обгортка `execFileAsync = promisify(execFile)`.
|
|
267
|
-
- Глобальні: `fetch`, `AbortSignal.timeout` — для PyPI (Node ≥ 18).
|
|
268
|
-
|
|
269
|
-
### Внутрішні модулі
|
|
270
|
-
|
|
271
|
-
- `../../../scripts/lib/check-reporter.mjs` → `createCheckReporter` — створює пару `{ pass, fail }` і обчислює `getExitCode()`.
|
|
272
|
-
- `../lib/package-manifest.mjs`:
|
|
273
|
-
- `getMonorepoProjectRootDirs(cwd)` — список воркспейсів (включно з `.`);
|
|
274
|
-
- `manifestFilePath(ws, manifest)` — шлях до маніфесту в повідомленнях;
|
|
275
|
-
- `parsePyprojectFields(text)` — отримання `{ name, version }` із `pyproject.toml`;
|
|
276
|
-
- `readPackageManifest(ws, cwd)` — нормалізований опис воркспейсу (тип `PackageManifest`).
|
|
277
|
-
- `../../release/lib/change-file.mjs` → `readChangeFiles(ws, cwd)` — список pending change-файлів у `<ws>/.changes/`.
|
|
278
|
-
|
|
279
|
-
### Зовнішні системи / процеси
|
|
280
|
-
|
|
281
|
-
- `git` (CLI) — `rev-parse`, `merge-base`, `diff`, `ls-files`, `show`, `check-ignore`.
|
|
282
|
-
- `npm` (CLI) — `npm view <name> version` для registry-published npm-пакетів.
|
|
283
|
-
- PyPI HTTP API — `https://pypi.org/pypi/<name>/json` для Python-пакетів.
|
|
284
|
-
|
|
285
|
-
### Константи модуля
|
|
286
|
-
|
|
287
|
-
- `FEATURE_BASE_BRANCH_CANDIDATES = Object.freeze(['dev', 'main'])` — порядок пошуку бази для feature-гілок.
|
|
288
|
-
- `LOCAL_ONLY_SKIP_BRANCH = 'dev'` — гілка, де local-only перевірка не активна.
|
|
289
|
-
- `CHANGELOG_IGNORE_PATH_PREFIXES = Object.freeze(['docs/', 'doc/', '.cursor/', '.claude/'])` — інверсні префікси (зміни в них не релевантні).
|
|
290
|
-
- `REGISTRY_TIMEOUT_MS = 10_000` — таймаут для `npm view` / PyPI fetch.
|
|
291
|
-
- `LEADING_DOTSLASH_RE = /^\.\//` — для нормалізації шляхів у `isChangelogIgnoredPath`.
|
|
292
|
-
|
|
293
|
-
## Потік виконання / Використання
|
|
294
|
-
|
|
295
|
-
Типовий виклик (із CLI/скрипту):
|
|
296
|
-
|
|
297
|
-
```js
|
|
298
|
-
import { check } from './consistency.mjs'
|
|
299
|
-
|
|
300
|
-
const exitCode = await check()
|
|
301
|
-
process.exit(exitCode)
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
Із кастомним резолвером опублікованої версії (наприклад, у тестах):
|
|
305
|
-
|
|
306
|
-
```js
|
|
307
|
-
import { check } from './consistency.mjs'
|
|
308
|
-
|
|
309
|
-
const exitCode = await check({
|
|
310
|
-
cwd: '/tmp/sandbox-repo',
|
|
311
|
-
async getPublishedVersion(name, kind) {
|
|
312
|
-
if (name === '@scope/pkg-a') return '1.0.0'
|
|
313
|
-
return null
|
|
314
|
-
}
|
|
315
|
-
})
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
### Високорівневий потік `check`
|
|
319
|
-
|
|
320
|
-
```
|
|
321
|
-
check(opts)
|
|
322
|
-
├─ createCheckReporter() → { pass, fail, getExitCode }
|
|
323
|
-
├─ getMonorepoProjectRootDirs(cwd) → workspaces
|
|
324
|
-
├─ subWorkspaces = workspaces \ ['.']
|
|
325
|
-
├─ isMonorepoRoot = subWorkspaces.length > 0
|
|
326
|
-
├─ For each ws:
|
|
327
|
-
│ ├─ ws === '.' && isMonorepoRoot → pass (root skipped) ; continue
|
|
328
|
-
│ ├─ manifest = readPackageManifest(ws, cwd)
|
|
329
|
-
│ ├─ !manifest → continue
|
|
330
|
-
│ └─ manifest.registryPublishable ? published.push : localOnly.push
|
|
331
|
-
├─ For each published manifest:
|
|
332
|
-
│ └─ checkPublishedWorkspace(...)
|
|
333
|
-
│ ├─ no version → fail
|
|
334
|
-
│ ├─ no name → fail
|
|
335
|
-
│ ├─ Vpublished == null → pass (skipped)
|
|
336
|
-
│ ├─ drift → fail
|
|
337
|
-
│ └─ checkPublishedWorkspacePendingGitChanges(...)
|
|
338
|
-
│ ├─ hasPendingChangeFiles → pass + checkNpmFilesArrayContainsChangelog
|
|
339
|
-
│ ├─ branch dev → fail iff relevant changes vs HEAD
|
|
340
|
-
│ ├─ comparison ref + relevant changes → fail
|
|
341
|
-
│ └─ main + relevant changes vs HEAD → fail
|
|
342
|
-
├─ runLocalOnlyChecks(localOnly, ...)
|
|
343
|
-
│ ├─ not git → pass (skipped)
|
|
344
|
-
│ ├─ branch dev → pass (skipped)
|
|
345
|
-
│ ├─ no comparison → pass (skipped)
|
|
346
|
-
│ └─ for each localOnly with relevant changes:
|
|
347
|
-
│ └─ checkLocalOnlyChangedWorkspace(...)
|
|
348
|
-
│ ├─ Vbase != Vcurrent → fail (drift)
|
|
349
|
-
│ ├─ hasPendingChangeFiles → pass
|
|
350
|
-
│ └─ else fail (missing change file)
|
|
351
|
-
└─ return reporter.getExitCode()
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
### Контракти / гарантії
|
|
355
|
-
|
|
356
|
-
- **Безпека:** жодних викликів `exec` / `spawn` із інтерполяцією рядків — лише `execFile` із масивом аргументів.
|
|
357
|
-
- **Idempotency:** функція виконує лише читання (git/fs/network); не змінює нічого на диску.
|
|
358
|
-
- **Деградація:** мережеві / репо-помилки — м'які (повертають `null`); їх результат — `pass` про пропуск, а не `fail`. Виняток: реальні відмінності, які можна спостерігати локально (drift, відсутність change-файлу), завжди дають `fail`.
|
|
359
|
-
- **Симетрія шляхів:** registry-published і local-only обидва ставлять drift-перевірку **перед** перевіркою change-файлу, тому ручний bump поза CI стабільно falsies перевірку незалежно від моделі.
|
|
360
|
-
|
|
361
|
-
### Точки розширення
|
|
362
|
-
|
|
363
|
-
- `opts.getPublishedVersion` — підміна джерела опублікованих версій (стаб для офлайн-тестів або проксі-реєстру).
|
|
364
|
-
- `opts.cwd` — переключення активного репозиторію без `process.chdir`.
|
|
365
|
-
|
|
366
|
-
## Rebuild Test
|
|
367
|
-
|
|
368
|
-
Контрольний перелік для відтворення/верифікації поведінки:
|
|
369
|
-
|
|
370
|
-
1. **Експорт API** — модуль експортує єдину `async function check(opts?)`, що повертає `Promise<number>`.
|
|
371
|
-
2. **Дефолти** — `opts.cwd` за замовчуванням `process.cwd()`; `opts.getPublishedVersion` за замовчуванням `defaultGetPublishedVersion` (npm-view для `kind === 'npm'`, PyPI fetch для `kind === 'python'`).
|
|
372
|
-
3. **Корінь монорепо** — для `ws === '.'` за наявності підворкспейсів виставляється `pass` про пропуск без читання маніфесту.
|
|
373
|
-
4. **Класифікація** — `manifest.registryPublishable === true` → `published`; інакше → `localOnly`. Воркспейси без читабельного маніфесту мовчки пропускаються.
|
|
374
|
-
5. **Drift > change-файл** — для обох моделей перевірка drift `version` спрацьовує **раніше** за перевірку наявності change-файлу і `fail` має пріоритет.
|
|
375
|
-
6. **Гілка `dev`** — `runLocalOnlyChecks` повністю пропускає local-only (`pass`); registry-published у `checkPublishedWorkspacePendingGitChanges` на `dev` перевіряє лише робоче дерево/staged відносно `HEAD`.
|
|
376
|
-
7. **Гілка `main`** — точка порівняння: `origin/main`, якщо це предок `HEAD` або збігається; інакше `HEAD~1`; також додаткова перевірка `HEAD` (working/staged), щоб виявити незакомічені зміни.
|
|
377
|
-
8. **Feature-гілка** — точка порівняння визначається ітерацією по `['dev', 'main']`, береться merge-base першої доступної бази; `label` приводиться до короткої форми (`origin/main` → `main`).
|
|
378
|
-
9. **Інверсні шляхи** — `docs/`, `doc/`, `.cursor/`, `.claude/` (із normalize `\` → `/` і обрізанням `./`) не вважаються релевантними змінами.
|
|
379
|
-
10. **`git -z`** — у `git diff --name-only` та `git ls-files --others` обов'язково використовується `-z`, інакше не-ASCII імена потраплять у C-quoted формі й ламатимуть префіксне порівняння.
|
|
380
|
-
11. **Untracked + tracked** — `listChangedPathsAgainstBase` об'єднує `git diff` (відносно `baseRef`) і `git ls-files --others --exclude-standard`, дедуплікація через `Set`.
|
|
381
|
-
12. **gitignored** — кожен кандидат додатково перевіряється через `git check-ignore -q --`; ігноровані пропускаються.
|
|
382
|
-
13. **`checkNpmFilesArrayContainsChangelog`** — викликається лише в гілці «pending change-файли є» для registry-published; для не-npm або відсутнього `npmFiles` — раннє return без `pass`/`fail`.
|
|
383
|
-
14. **Мовчазний skip** — недоступність опублікованої версії (мережа/реєстр) даює `pass` про пропуск, а не `fail`.
|
|
384
|
-
15. **`workspaceLabel`** — `'<root>'` для `.`, інакше шлях ws.
|
|
385
|
-
16. **`missingChangeFileMessage`** — текст fail містить шлях до маніфесту, інструкцію `npx @nitra/cursor change --bump … --section … --message …` і нагадування «bump зробить CI на main (n-changelog.mdc)».
|
|
386
|
-
17. **Послідовність публічних перевірок** — спершу всі `published` (у порядку, повернутому з `getMonorepoProjectRootDirs`), потім `runLocalOnlyChecks`.
|
|
387
|
-
18. **Exit-код** — повертається з `reporter.getExitCode()` (агрегує всі `pass`/`fail`).
|
|
12
|
+
Перевіряє версіонування проектів у монорепозиторії, порівнюючи версії, зазначені в маніфестах, з даними, отриманими з мережі з https://pypi.org/pypi/. Аналізує відповідність версій встановленим правилам, визначеним у конфігурації res.json. Визначає, чи відповідають версії формату, а також перевіряє наявність змін, порівнюючи їх із даними, описаними у (n-changelog.mdc). При невдачі перевірки повертає false/null, не кидаючи винятків.
|
|
13
|
+
|
|
14
|
+
## Поведінка
|
|
15
|
+
|
|
16
|
+
1. Ініціалізує репортер для збору результатів перевірки.
|
|
17
|
+
2. Визначає робочий каталог та стан autofix-режиму.
|
|
18
|
+
3. Зчитує всі кореневі каталоги проектів у монорепо.
|
|
19
|
+
4. Класифікує кожен проект як публікувальний (registryPublishable) або локальний.
|
|
20
|
+
5. Для кожного публікувального проекту виконує перевірку:
|
|
21
|
+
а. Зчитує маніфест проекту.
|
|
22
|
+
б. Якщо у маніфесті відсутнє ім'я або поле version, фіксує помилку.
|
|
23
|
+
в. Якщо є change-файл(и) у .changes/, фіксує успіх, оскільки bump зробить CI (n-changelog.mdc), і перевіряє наявність "CHANGELOG.md" у файлах проекту.
|
|
24
|
+
г. Якщо немає change-файлу, визначає точку порівняння на основі поточної гілки.
|
|
25
|
+
д. Порівнює версію в маніфесті з опублікованою версією (за допомогою npm view або запиту до https://pypi.org/pypi/).
|
|
26
|
+
е. Якщо версія в маніфесті випереджає опубліковану, фіксує помилку, оскільки ручний bump поза CI заборонений.
|
|
27
|
+
ж. Якщо версія в маніфесті позаду опублікованої, фіксує успіх, оскільки це відставання локального репозиторію від реєстру.
|
|
28
|
+
з. Якщо версії збігаються, перевіряє змінений код відносно точки порівняння. Якщо зміни є, але немає change-файлу, викликає механізм фіксації або фіксує помилку, надаючи підказку для використання команди `npx @nitra/cursor change`.
|
|
29
|
+
6. Для кожного локального проекту виконує перевірку:
|
|
30
|
+
а. Зчитує маніфест проекту.
|
|
31
|
+
б. Визначає точку порівняння на основі поточної гілки.
|
|
32
|
+
в. Перевіряє, чи є релевантні зміни у проекті відносно точки порівняння.
|
|
33
|
+
г. Якщо зміни є, але немає change-файлу, викликає механізм фіксації або фіксує помилку, надаючи підказку для використання команди `npx @nitra/cursor change`.
|
|
34
|
+
7. Повертає кінцевий код виходу, що відображає результат перевірки.
|
|
35
|
+
|
|
36
|
+
## Гарантії поведінки
|
|
37
|
+
|
|
38
|
+
- Read-only: файл не виконує операцій запису у файлову систему.
|
|
39
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
40
|
+
- За невдалої перевірки повертає `false`/`null` замість винятку.
|
|
@@ -1,140 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/rules/feedback/fix.mjs
|
|
4
|
+
crc: 12fc1644
|
|
5
|
+
score: 100
|
|
6
|
+
---
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Файл є точкою входу (entry-point) для правила `feedback` у CLI-наборі `@nitra/cursor`. Він реалізує дві паралельні ролі того ж самого модуля:
|
|
6
|
-
|
|
7
|
-
1. **Library mode** — експортує функцію `run(ctx)`, яку викликають інші частини оркестратора (наприклад, агрегований `fix`/`lint`-прогін, де всі правила запускаються послідовно з шарінгом кешу обходу файлів `walkCache`).
|
|
8
|
-
2. **Standalone mode** — якщо файл стартує безпосередньо як CLI-скрипт (`bun rules/feedback/fix.mjs`), він виконується як повний аналог команди `npx @nitra/cursor fix feedback` із завантаженням конфігу, застосуванням whitelist та підсумком.
|
|
9
|
-
|
|
10
|
-
Сам файл логіки правила не містить — він є тонким адаптером, що делегує роботу до спільного раннера `runStandardRule`. Стандартний потік правила, який запускається через цей адаптер: `applies → JS-concerns → policy → mdc-refs` (тобто перевіряється застосовність до проєкту, далі — JS-специфічні перевірки, далі — policy-шар, наприкінці — синхронізація посилань у відповідному `.mdc`-файлі).
|
|
11
|
-
|
|
12
|
-
Модуль використовує `import.meta.dirname`, тому він повністю прив'язаний до місця свого розташування: каталог правила (`npm/rules/feedback/`) автоматично стає коренем, у якому раннер шукає `meta.json`, `feedback.mdc` та інші артефакти.
|
|
13
|
-
|
|
14
|
-
## Експорти / API
|
|
15
|
-
|
|
16
|
-
| Експорт | Тип | Призначення |
|
|
17
|
-
| ------- | --------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
18
|
-
| `run` | `function(ctx?): Promise<number>` | Library-точка входу правила. Викликається оркестратором; повертає exit-code (0 — OK, 1 — порушення). |
|
|
19
|
-
|
|
20
|
-
Side-експортів немає. Default-експорту немає.
|
|
21
|
-
|
|
22
|
-
Поведінка при прямому запуску файлу як CLI (через `process.exit(await runRuleCli(...))`) — це не експорт, а top-level side-effect, активний лише коли `isRunAsCli(import.meta.url)` повертає `true`.
|
|
23
|
-
|
|
24
|
-
## Функції
|
|
25
|
-
|
|
26
|
-
### `run(ctx)`
|
|
27
|
-
|
|
28
|
-
```js
|
|
29
|
-
export function run(ctx)
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
- **Призначення:** запустити правило `feedback` у library-режимі. Делегує всю роботу до `runStandardRule`, передаючи каталог поточного файлу як ідентифікатор правила.
|
|
33
|
-
- **Параметри:**
|
|
34
|
-
- `ctx` — `RuleContext | undefined`. Опціональний контекст прогону. Тип імпортується через JSDoc-посилання `import('../../scripts/lib/run-standard-rule.mjs').RuleContext`. Зокрема, контекст несе спільні структури між правилами (наприклад, `walkCache` — кеш обходу файлової системи, щоб не сканувати дерево кілька разів у разі прогону кількох правил поспіль). Якщо `ctx` не переданий, `runStandardRule` створює власний внутрішній контекст.
|
|
35
|
-
- **Повертає:** `Promise<number>` — exit-code:
|
|
36
|
-
- `0` — правило застосовне і порушень не знайдено, або правило не застосовне до поточного проєкту (`applies` повернув `false`);
|
|
37
|
-
- `1` — знайдені порушення (policy / JS-concerns / mdc-refs).
|
|
38
|
-
- **Side effects:** жодних прямих у цій функції. Опосередковано через `runStandardRule` можливі: читання файлів проєкту, читання `meta.json` і `feedback.mdc`, лог у stdout/stderr, мутація переданого `walkCache`.
|
|
39
|
-
- **Винятки:** функція сама не кидає; будь-яка помилка приходить як rejected promise від `runStandardRule`.
|
|
40
|
-
|
|
41
|
-
### Top-level standalone-блок
|
|
42
|
-
|
|
43
|
-
```js
|
|
44
|
-
if (isRunAsCli(import.meta.url)) {
|
|
45
|
-
process.exit(await runRuleCli(import.meta.dirname))
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
- **Призначення:** перетворити цей же модуль на CLI-скрипт. Якщо файл був запущений напряму (а не імпортований), виконується повний CLI-цикл правила — еквівалент `npx @nitra/cursor fix feedback`.
|
|
50
|
-
- **Виклик:** `runRuleCli(import.meta.dirname)` отримує абсолютний шлях до каталогу правила; всередині раннера це задає, який саме `meta.json`/`*.mdc` і `applies/policy/...`-функції підтягуються.
|
|
51
|
-
- **Повертає / завершення:** результат `runRuleCli` (число) йде в `process.exit(...)`, тобто процес одразу завершується з відповідним exit-code. Це навмисно: standalone entry-point має повернути код виходу для CI/IDE-інтеграцій.
|
|
52
|
-
- **Спеціальні маркери:**
|
|
53
|
-
- `// eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit` — свідома відмова від загальної заборони `process.exit` саме тут, бо це standalone-точка входу.
|
|
54
|
-
- **Side effects:** завершує процес Node/Bun.
|
|
55
|
-
|
|
56
|
-
## Залежності
|
|
8
|
+
# fix.mjs
|
|
57
9
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
| Імпорт | З файлу | Що використовується | Роль |
|
|
61
|
-
| ----------------- | ----------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
62
|
-
| `isRunAsCli` | `../../scripts/lib/run-rule-cli.mjs` | Функція-предикат | Визначає, чи цей `.mjs`-файл був запущений напряму як CLI (через `import.meta.url`), а не імпортований як модуль. |
|
|
63
|
-
| `runRuleCli` | `../../scripts/lib/run-rule-cli.mjs` | Async-функція | Виконує повний CLI-цикл правила: завантаження конфігу, whitelist-фільтрацію, прогін стандартного раннера, форматування підсумку, повернення exit-code. |
|
|
64
|
-
| `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Async-функція + JSDoc-тип `RuleContext` | Стандартний раннер для правил: `applies → JS-concerns → policy → mdc-refs`. Шукає артефакти у переданому каталозі. |
|
|
65
|
-
|
|
66
|
-
Глобали з рантайму:
|
|
67
|
-
|
|
68
|
-
- `import.meta.dirname` — абсолютний шлях каталогу поточного `.mjs`-файлу (Bun / Node ≥ 20.11). Використовується як «корінь правила».
|
|
69
|
-
- `import.meta.url` — file-URL модуля; передається в `isRunAsCli` для надійного порівняння з `process.argv[1]`.
|
|
70
|
-
- `process.exit` — викликається лише у standalone-гілці.
|
|
71
|
-
|
|
72
|
-
Зовнішні пакети не імпортуються.
|
|
73
|
-
|
|
74
|
-
## Потік виконання / Використання
|
|
75
|
-
|
|
76
|
-
### Сценарій A — імпорт як library
|
|
77
|
-
|
|
78
|
-
```js
|
|
79
|
-
import { run } from './npm/rules/feedback/fix.mjs'
|
|
80
|
-
|
|
81
|
-
const code = await run(/* ctx */)
|
|
82
|
-
if (code !== 0) {
|
|
83
|
-
// правило знайшло порушення
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Послідовність всередині:
|
|
88
|
-
|
|
89
|
-
1. Викликається `run(ctx)`.
|
|
90
|
-
2. `run` повертає `runStandardRule(import.meta.dirname, ctx)`.
|
|
91
|
-
3. Усередині `runStandardRule`:
|
|
92
|
-
- перевіряє `applies` (чи правило застосовне до проєкту);
|
|
93
|
-
- проганяє JS-concerns (lint/structure);
|
|
94
|
-
- проганяє policy-перевірки;
|
|
95
|
-
- звіряє `mdc-refs` (узгодженість посилань між кодом і `feedback.mdc`).
|
|
96
|
-
4. Повертається `0` або `1`.
|
|
97
|
-
|
|
98
|
-
Top-level `if (isRunAsCli(...))` у цьому випадку не спрацьовує — `isRunAsCli(import.meta.url)` повертає `false`, бо стартова точка процесу — інший файл.
|
|
99
|
-
|
|
100
|
-
### Сценарій B — прямий запуск як CLI
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
bun npm/rules/feedback/fix.mjs
|
|
104
|
-
# або еквівалент:
|
|
105
|
-
npx @nitra/cursor fix feedback
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Послідовність:
|
|
109
|
-
|
|
110
|
-
1. Node/Bun завантажує модуль; виконуються `import`-и.
|
|
111
|
-
2. Перевіряється `isRunAsCli(import.meta.url)` → `true`.
|
|
112
|
-
3. Викликається `await runRuleCli(import.meta.dirname)`. Цей раннер:
|
|
113
|
-
- завантажує загальний конфіг проєкту;
|
|
114
|
-
- застосовує whitelist (якщо в конфізі обмежений набір правил);
|
|
115
|
-
- всередині все одно проганяє ту ж саму `runStandardRule`-логіку;
|
|
116
|
-
- друкує форматований summary;
|
|
117
|
-
- повертає exit-code.
|
|
118
|
-
4. `process.exit(code)` миттєво завершує процес із отриманим кодом, який потім читає CI/IDE.
|
|
119
|
-
|
|
120
|
-
### Чому дві ролі в одному файлі
|
|
10
|
+
## Огляд
|
|
121
11
|
|
|
122
|
-
|
|
12
|
+
Модуль виконує задану перевірку, ініціалізуючи її з локального файлу конфігурації. При запуску як окрема програма, він завантажує конфігурацію, перевіряє білий список та надає звіт про результати виконання. Результат виконання правила повертається як код виходу.
|
|
123
13
|
|
|
124
|
-
|
|
125
|
-
- розробнику запустити **одне** правило точково з шелла без обгорток і отримати CI-сумісний exit-code.
|
|
14
|
+
## Поведінка
|
|
126
15
|
|
|
127
|
-
|
|
16
|
+
1. Викликається функція `run` для виконання правила.
|
|
17
|
+
2. Виконання правила відбувається шляхом ініціалізації стандартного правила з директорії цього файлу.
|
|
18
|
+
3. Якщо код виконується як окрема утиліта (standalone), ініціюється повний запуск правила.
|
|
19
|
+
4. Запуск правила як окремої утиліти включає завантаження конфігурації, перевірку білого списку та підведення підсумків.
|
|
20
|
+
5. Результат цього запуску повертається як код виходу.
|
|
128
21
|
|
|
129
|
-
##
|
|
22
|
+
## Публічний API
|
|
130
23
|
|
|
131
|
-
|
|
24
|
+
run — виконує послідовність перевірок: застосовує правила, аналізує JS-занепокоєння, порівнює з політикою та перевіряє посилання MDC.
|
|
132
25
|
|
|
133
|
-
|
|
134
|
-
- тіло: `return runStandardRule(import.meta.dirname, ctx)`;
|
|
135
|
-
- top-level `if`: `isRunAsCli(import.meta.url)` → `process.exit(await runRuleCli(import.meta.dirname))`;
|
|
136
|
-
- два імпорти з точно вказаних шляхів (`../../scripts/lib/run-rule-cli.mjs` для `isRunAsCli`/`runRuleCli`, `../../scripts/lib/run-standard-rule.mjs` для `runStandardRule`);
|
|
137
|
-
- ESLint-disable коментар над `process.exit` для `n/no-process-exit` і `unicorn/no-process-exit`;
|
|
138
|
-
- JSDoc-блок над `run` із посиланням на тип `RuleContext` через `import('...')`-нотацію.
|
|
26
|
+
## Гарантії поведінки
|
|
139
27
|
|
|
140
|
-
|
|
28
|
+
- Read-only: файл не виконує операцій запису у файлову систему.
|
|
29
|
+
- Кешує результати в межах одного прогону.
|
|
30
|
+
- Не звертається до мережі.
|