@7n/rules 1.46.0 → 1.47.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/CHANGELOG.md +22 -0
- package/package.json +1 -1
- package/rules/doc-files/check/docs/main.md +11 -14
- package/rules/doc-files/check/main.mjs +8 -1
- package/rules/doc-files/docgen-scan/docs/lang-extensions.md +22 -14
- package/rules/doc-files/docgen-scan/lang-extensions.mjs +18 -1
- package/scripts/lib/docs/resolve-plugins.md +113 -6
- package/scripts/lib/resolve-plugins.mjs +20 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.47.1] - 2026-07-23
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- doc-files: явна діагностика замість тихого 0 кандидатів, коли задекларований плагін (.n-rules.json) не встановлений у node_modules
|
|
8
|
+
|
|
9
|
+
## [1.47.0] - 2026-07-23
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- концерн coverage правила test: гейт покриття/мутаційки як lint-детектор (--no-fix = CI-гейт), CoverageProvider порт у plugin-api (spec 2026-07-22 absorb-7n-test)
|
|
14
|
+
- coverage: спільні lib manifest-roots/lcov, подовжений full-таймаут для мутаційного тестування (×4), пілот classify (0.7) у цьому репо
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- doc_comments rollout: header-JSDoc у vitest.config (T0 promote)
|
|
19
|
+
- doc_comments rollout: header-JSDoc у vitest.config (T0 promote)
|
|
20
|
+
- doc_comments rollout: header-JSDoc у vitest.config (T0 promote)
|
|
21
|
+
- doc_comments rollout: header-JSDoc у vitest.config (T0 promote)
|
|
22
|
+
- doc_comments rollout: header/export JSDoc у конфігах demo
|
|
23
|
+
- doc_comments rollout: header-JSDoc у vitest.config
|
|
24
|
+
|
|
3
25
|
## [1.46.0] - 2026-07-23
|
|
4
26
|
|
|
5
27
|
### Added
|
package/package.json
CHANGED
|
@@ -3,30 +3,27 @@ type: JS Module
|
|
|
3
3
|
title: main.mjs
|
|
4
4
|
resource: npm/rules/doc-files/check/main.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
7
|
-
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score: 100
|
|
9
|
-
issues: judge:inaccurate:0.99
|
|
10
|
-
judgeModel: openai-codex/gpt-5.4-mini
|
|
6
|
+
crc: dd3f8c2a
|
|
11
7
|
---
|
|
12
8
|
|
|
13
9
|
## Огляд
|
|
14
10
|
|
|
15
|
-
Lint-детектор
|
|
11
|
+
Lint-детектор doc-files: знаходить застарілі файлові доки (CRC-mismatch/missing/degraded) і сирітські доки, чиє джерело видалено. Read-only — саму генерацію/очистку виконує `fix-worker.mjs`.
|
|
16
12
|
|
|
17
13
|
## Поведінка
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
`lint(ctx)` збирає застарілі доки через `collectStale` (для конкретного набору змінених файлів або, якщо файлів не передано, повним скануванням дерева) і додає для кожної окреме порушення з причиною (`missing`/`crc-mismatch`/`degraded`) та шляхом джерела. Окремо сканує `docs/`-теки на сирітські доки (`scanOrphanedDocs`) — джерело яких більше не існує.
|
|
16
|
+
|
|
17
|
+
Реверс-мапінг: якщо серед змінених файлів трапляється сама `.md`-дока (а не її джерело), `sourceForDoc` шукає відповідний вихідний файл поруч (той самий basename, легальне розширення) і саме його передає далі в перевірку застарілості — так зміна доки теж тригерить звірку CRC її джерела.
|
|
18
|
+
|
|
19
|
+
Якщо мапа doc-files-розширень від активних плагінів порожня, а хоча б один плагін явно задекларований у `.n-rules.json`, але не встановлений у `node_modules` — детектор додає `diagnostics`-запис (`level: 'warn'`) з підказкою запустити `bun install`. Це відрізняє "плагін не встановлено" від "усі доки актуальні": без нього обидва випадки виглядають як 0 порушень.
|
|
23
20
|
|
|
24
21
|
## Публічний API
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
lint — детектор застарілих і сирітських файлових доків, повертає `{ violations }`.
|
|
23
|
+
- `lint(ctx)` — детектор concern-а `doc-files/check`: повертає `{ violations, diagnostics? }`.
|
|
28
24
|
|
|
29
25
|
## Гарантії поведінки
|
|
30
26
|
|
|
31
|
-
- Read-only: не
|
|
32
|
-
-
|
|
27
|
+
- Read-only: не пише і не видаляє жодних файлів.
|
|
28
|
+
- Перехоплює помилки читання файлової системи (напр. нечитабельна тека) — не пропускає винятків назовні.
|
|
29
|
+
- Diagnostics-попередження про невстановлений плагін рендериться лише при `--verbose` на explicit CLI-виклику (`lint doc-files --no-fix --verbose`) — у PostToolUse-хуку (без `verbose`) обчислюється, але ніколи не друкується.
|
|
@@ -5,6 +5,7 @@ import { join, dirname, basename, extname } from 'node:path'
|
|
|
5
5
|
import { existsSync, readdirSync } from 'node:fs'
|
|
6
6
|
|
|
7
7
|
import { describeFile, isDocCandidate, isSourceFile, scanForDocFiles, scanOrphanedDocs } from '../docgen-scan/main.mjs'
|
|
8
|
+
import { unavailableDocFilesPlugins } from '../docgen-scan/lang-extensions.mjs'
|
|
8
9
|
|
|
9
10
|
const DOC_MD_RE = /(?:^|\/)docs\/[^/]+\.md$/u
|
|
10
11
|
|
|
@@ -95,5 +96,11 @@ export function lint(ctx) {
|
|
|
95
96
|
)
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
const unavailable = unavailableDocFilesPlugins(cwd)
|
|
100
|
+
if (unavailable.length === 0) return { violations }
|
|
101
|
+
|
|
102
|
+
const word = unavailable.length > 1 ? 'плагіни' : 'плагін'
|
|
103
|
+
const verb = unavailable.length > 1 ? 'не встановлені' : 'не встановлений'
|
|
104
|
+
const message = `doc-files: 0 кандидатів через недоступні плагіни — ${word} ${unavailable.join(', ')} ${verb} у node_modules — запусти bun install`
|
|
105
|
+
return { violations, diagnostics: [{ level: 'warn', message }] }
|
|
99
106
|
}
|
|
@@ -3,32 +3,40 @@ type: JS Module
|
|
|
3
3
|
title: lang-extensions.mjs
|
|
4
4
|
resource: npm/rules/doc-files/docgen-scan/lang-extensions.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
7
|
-
model:
|
|
8
|
-
tier:
|
|
9
|
-
score:
|
|
10
|
-
issues: judge:inaccurate:0.98
|
|
11
|
-
judgeModel: openai-codex/gpt-5.4-mini
|
|
6
|
+
crc: 14b28432
|
|
7
|
+
model: omlx/gemma-4-e2b-it-4bit
|
|
8
|
+
tier: local-min
|
|
9
|
+
score: 95
|
|
12
10
|
---
|
|
13
11
|
|
|
14
12
|
## Огляд
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
Огляд: Модуль керує завантаженням та очищенням документів та логіки для перевірок. Забезпечує функціональний потік, який включає отримання мапи розширень, завантаження екстракторів та очищення кешу для тестування.
|
|
17
15
|
|
|
18
16
|
## Поведінка
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
Поведінка крос-функціональний потік
|
|
19
|
+
pluginDocFilesExtensions повертає мапу розширень задекларованих у конфігах
|
|
20
|
+
loadDocFilesExtractors вантажує мовні екстрактори з handler-модулів
|
|
21
|
+
unavailableDocFilesPlugins повертає список плагінів, які не встановлені
|
|
22
|
+
clearDocFilesLangCache скидає кеші для тестування
|
|
23
23
|
|
|
24
24
|
## Публічний API
|
|
25
25
|
|
|
26
|
-
- pluginDocFilesExtensions —
|
|
27
|
-
|
|
28
|
-
-
|
|
26
|
+
- pluginDocFilesExtensions — Мапа doc-files-розширень від плагінів для репо (`.rs` → 'Rust Module', …),
|
|
27
|
+
з кешем на процес. Порожня мапа — жодний активний плагін їх не декларує.
|
|
28
|
+
- loadDocFilesExtractors — Асинхронно вантажить мовні екстрактори з handler-модулів плагінів
|
|
29
|
+
(extension-point `doc-files`): default-експорт
|
|
30
|
+
`{ id, extensions: string[], extractFacts?, extractUnits? }`.
|
|
31
|
+
Битий модуль — мовчазний пропуск (генерація тоді йде whole-file шляхом).
|
|
32
|
+
- unavailableDocFilesPlugins — Задекларовані у `.n-rules.json` плагіни, недоступні в `node_modules` — рахується лише
|
|
33
|
+
коли мапа doc-files-розширень порожня (інакше принаймні один плагін реально доступний,
|
|
34
|
+
шукати "недоступні" немає сенсу — не hot-path concern, рахується лише в рідкісному
|
|
35
|
+
порожньому випадку).
|
|
36
|
+
- clearDocFilesLangCache — Скидає кеші (для тестів).
|
|
29
37
|
|
|
30
38
|
## Гарантії поведінки
|
|
31
39
|
|
|
32
|
-
-
|
|
40
|
+
- Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
|
|
33
41
|
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
34
42
|
- Кешує результати в межах одного прогону.
|
|
@@ -3,7 +3,11 @@ import { existsSync, readFileSync } from 'node:fs'
|
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
import { pathToFileURL } from 'node:url'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getDocFilesExtensions,
|
|
8
|
+
getHandlers,
|
|
9
|
+
getUnavailableDeclaredPlugins
|
|
10
|
+
} from '../../../scripts/lib/resolve-plugins.mjs'
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* Мовні розширення doc-files від плагінів (фаза 4 spec lang-plugins-extraction).
|
|
@@ -82,6 +86,19 @@ export async function loadDocFilesExtractors(cwd) {
|
|
|
82
86
|
return map
|
|
83
87
|
}
|
|
84
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Задекларовані у `.n-rules.json` плагіни, недоступні в `node_modules` — рахується лише
|
|
91
|
+
* коли мапа doc-files-розширень порожня (інакше принаймні один плагін реально доступний,
|
|
92
|
+
* шукати "недоступні" немає сенсу — не hot-path concern, рахується лише в рідкісному
|
|
93
|
+
* порожньому випадку).
|
|
94
|
+
* @param {string} cwd корінь репозиторію
|
|
95
|
+
* @returns {string[]} npm-імена задекларованих, але не встановлених плагінів (порожньо — усе гаразд)
|
|
96
|
+
*/
|
|
97
|
+
export function unavailableDocFilesPlugins(cwd) {
|
|
98
|
+
if (Object.keys(pluginDocFilesExtensions(cwd)).length > 0) return []
|
|
99
|
+
return getUnavailableDeclaredPlugins(cwd, readPluginsConfigSync(cwd))
|
|
100
|
+
}
|
|
101
|
+
|
|
85
102
|
/** Скидає кеші (для тестів). */
|
|
86
103
|
export function clearDocFilesLangCache() {
|
|
87
104
|
EXT_CACHE.clear()
|
|
@@ -3,15 +3,122 @@ type: JS Module
|
|
|
3
3
|
title: resolve-plugins.mjs
|
|
4
4
|
resource: npm/scripts/lib/resolve-plugins.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 52b1765f
|
|
7
|
+
model: omlx/gemma-4-e2b-it-4bit
|
|
8
|
+
tier: local-min
|
|
9
|
+
score: 40
|
|
10
|
+
issues: internal-name:hasLangSignal,internal-name:listScannableSubdirs,internal-name:readRepositoryUrl,internal-name:hasGithubWorkflows,internal-name:detectCiPlugins,surzhik,best-of-2:retry-error
|
|
7
11
|
---
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
## Огляд
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
Огляд: Цей файл забезпечує пошук сигналів-маркерів у файловій системі для визначення активності плагінів. Він використовує BFS-обхід для пошуку цих сигналів, визначає доступні `capabilities`, розв'язує список плагінів з конфігурації, гарантує встановлення необхідних пакетів, обробляє маніфести плагінів та надає інструменти для роботи з їхніми обробниками (`handlers`).
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
## Поведінка
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
Перевіряє наявність файлу-сигналу у корені або підтеках до `maxDepth` рівнів. Використовує BFS-обхід, який пропускає приховані директорії, `node_modules`, та `target`, для забезпечення ефективного виклику на hot-path. Повертає `true` при знаходженні сигналу. Викликає `listScannableSubdirs`.
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
### listScannableSubdirs
|
|
22
|
+
Отримує видимі підтеки для неглибокого скану, ігноруючи приховані та службові директорії. Повертає масив абсолютних шляхів підтеків (порожній, якщо директорія нечитабельна).
|
|
23
|
+
|
|
24
|
+
### readRepositoryUrl
|
|
25
|
+
Отримує `repository.url` з кореневого `package.json` (як рядок або об'єкт). Повертає `null`, якщо URL відсутній або файл нечитабельний.
|
|
26
|
+
|
|
27
|
+
### hasGithubWorkflows
|
|
28
|
+
Перевіряє наявність файлів у директорії `.github/workflows/` будь-якого YAML/YML файлу. Повертає `true` при наявності.
|
|
29
|
+
|
|
30
|
+
### detectCiPlugins
|
|
31
|
+
Автоматично визначає CI-плагіни за станом репозиторію. Пріоритет надається файловим сигналам. Якщо файлових сигналів немає, використовується `repository.url` з кореневого `package.json`. Якщо обидва сигнали присутні, обидва плагіни повертаються. Якщо немає жодного сигналу, повертається порожній масив. Викликає `hasGithubWorkflows` та `readRepositoryUrl`.
|
|
32
|
+
|
|
33
|
+
### detectPluginsFromRepo
|
|
34
|
+
Автоматично визначає плагіни за станом репозиторію. Використовує файлові сигнали як пріоритет, з fallback на `repository.url` лише для CI-плагінів, коли файлових сигналів немає. Для Rust використовується перевірка до трьох рівнів підтек. Викликає `detectCiPlugins` та `hasLangSignal`.
|
|
35
|
+
|
|
36
|
+
### pluginCategory
|
|
37
|
+
Визначає категорію плагіна на основі naming convention `@7n/rules-<category>-<name>`. Повертає `null` для пакетів, що не відповідають цій конвенції (сторонні/кастомні плагіни) — такий пакет ніколи не з'являється сам через автодетект, а якщо присутній у явному `config.plugins`, вимикає backfill категорій для всього списку.
|
|
38
|
+
|
|
39
|
+
### resolvePluginList
|
|
40
|
+
Обчислює список плагінів проєкту. Використовує явне поле `config.plugins` з `.n-rules.json` або автоматичний автодетект. Явний пустий масив означає "плагіни вимкнено". Якщо `config.plugins` містить сторонній пакет, який не є `@7n/rules-*`, то backfill для заповнення списку вимикається повністю.
|
|
41
|
+
|
|
42
|
+
### computePluginList
|
|
43
|
+
Обчислює список плагінів без кешу. Використовує сире значення `config.plugins` з `.n-rules.json`. Повертає список плагінів. Викликає `detectPluginsFromRepo` та `pluginCategory`.
|
|
44
|
+
|
|
45
|
+
### ensurePluginInstalled
|
|
46
|
+
Гарантує наявність пакета: якщо він відсутній у `node_modules`, виконує `bun add -d <pkg>` для додання його як `devDependency`. Фейл повертає `false` з попередженням.
|
|
47
|
+
|
|
48
|
+
### readPluginManifest
|
|
49
|
+
Отримує нормалізований маніфест плагіна з його `package.json` з блоку `"n-rules"`.
|
|
50
|
+
|
|
51
|
+
### resolvePlugins
|
|
52
|
+
Виконує повне визначення доступних плагінів проєкту з використанням кешу. Дозволяє встановити вже встановлені пакети через `allowInstall: false` (для hot-path хука), ігнорує `bun add`, ігнорує `quiet` для уникнення попереджень при виклику на кожному файлі. Повертає масив доступних плагінів.
|
|
53
|
+
|
|
54
|
+
### resolveRulesDirs
|
|
55
|
+
Отримує шляхи до каталогів для всіх поверхонь ядра. Ядро має пріоритет. Надає масив об'єктів, що містять ім'я плагіна, шлях до його директорії та шлях до його кореня пакета.
|
|
56
|
+
|
|
57
|
+
### getActiveCapabilities
|
|
58
|
+
Отримує набір capability-рядків з усіх доступних плагінів, що використовуються для визначення `requires.capability` у `concern.json`.
|
|
59
|
+
|
|
60
|
+
### getDocFilesExtensions
|
|
61
|
+
Агрегує розширення файлів, що використовуються для генерації мапи від розширення до типу-мітка. Повертає мапу `extension -> type-мітка`.
|
|
62
|
+
|
|
63
|
+
### getUnavailableDeclaredPlugins
|
|
64
|
+
Повертає список плагінів, які задекларовані у `config.plugins`, але відсутні у `node_modules`. Нічого не встановлює і нічого не друкує — чистий предикат для explicit CLI-діагностики.
|
|
65
|
+
|
|
66
|
+
### getHandlers
|
|
67
|
+
Отримує шляхи до модулів-обробників для певного extension-point правила ядра.
|
|
68
|
+
|
|
69
|
+
### clearPluginResolveCache
|
|
70
|
+
Скидає кеш, призначений для тестів.
|
|
71
|
+
|
|
72
|
+
## Публічний API
|
|
73
|
+
|
|
74
|
+
- KNOWN_CI_PLUGINS — Відомі CI-плагіни для автовизначення: сигнал у дереві репо → npm-пакет.
|
|
75
|
+
- KNOWN_LANG_PLUGINS — Відомі мовні плагіни: файловий сигнал екосистеми → npm-пакет. `maxDepth` —
|
|
76
|
+
до якої глибини шукати сигнал: python — лише корінь (uv-провайдер v1
|
|
77
|
+
обробляє тільки кореневий pyproject.toml; js — кореневий package.json); rust — до 3 рівнів, бо в
|
|
78
|
+
монорепо Cargo.toml часто вкладений (Tauri `app/src-tauri/Cargo.toml`),
|
|
79
|
+
а провайдер обробляє всі знайдені маніфести.
|
|
80
|
+
- detectPluginsFromRepo — Автодетект плагінів за станом репозиторію: CI-плагіни (файлові сигнали з
|
|
81
|
+
fallback на `repository.url`) + мовні плагіни (лише файлові сигнали —
|
|
82
|
+
маніфест екосистеми в корені або, для rust, у підтеках до 3 рівнів; URL-fallback для мов безглуздий).
|
|
83
|
+
- pluginCategory — Категорія плагіна за naming convention `@7n/rules-<category>-<name>` (напр. `ci`, `lang`).
|
|
84
|
+
`null` — пакет поза цією конвенцією (сторонній/кастомний плагін); такий пакет ніколи не
|
|
85
|
+
зʼявляється сам через автодетект і, якщо присутній у явному `config.plugins`, вимикає
|
|
86
|
+
per-категорійний backfill для всього списку (див. `resolvePluginList`).
|
|
87
|
+
- resolvePluginList — Список плагінів проєкту: явний `config.plugins` або автодетект.
|
|
88
|
+
|
|
89
|
+
Явний `plugins` непорожній і складається **виключно** з пакетів `@7n/rules-<category>-*` —
|
|
90
|
+
автодетект домішує лише категорії, відсутні в списку (ADR
|
|
91
|
+
`260719-2154-per-category-автодетект-плагінів`); категорія, присутня хоч одним пакетом,
|
|
92
|
+
лишається зафіксованою користувачем. Якщо `plugins` містить хоча б один сторонній
|
|
93
|
+
(не `@7n/rules-*`) пакет — це сигнал ручного керування, backfill вимикається повністю
|
|
94
|
+
(як і раніше: список повертається як є). Явний `[]` — «плагіни вимкнено», без backfill.
|
|
95
|
+
Поле відсутнє взагалі — повний автодетект.
|
|
96
|
+
|
|
97
|
+
Результат кешується на процес за `(projectRoot, declared)` — виклик з `resolvePlugins`
|
|
98
|
+
(через `resolveRulesDirs` тощо) і прямий виклик у sync-CLI інакше дублювали б і файловий
|
|
99
|
+
скан, і warning про backfill.
|
|
100
|
+
ігнорується при cache hit — warning друкується щонайбільше раз на `(root, declared)` за процес
|
|
101
|
+
- ensurePluginInstalled — Гарантує, що плагін встановлений: якщо `node_modules/<pkg>` нема — `bun add -d <pkg>`
|
|
102
|
+
(дописує devDependency і ставить). Фейл — warning + false, без винятку.
|
|
103
|
+
- resolvePlugins — Повний резолв плагінів проєкту (з кешем на процес).
|
|
104
|
+
лише вже встановлені пакети, без `bun add`; `quiet` — без warning-ів (hook на кожен файл)
|
|
105
|
+
- resolveRulesDirs — Rules-каталоги для всіх поверхонь ядра: ядро першим (його правила/концерни виграють
|
|
106
|
+
колізії), далі плагіни у порядку списку.
|
|
107
|
+
- getActiveCapabilities — Активні capabilities від усіх доступних плагінів (для гейта `requires.capability` у concern.json).
|
|
108
|
+
- getDocFilesExtensions — Агреговані doc-files-розширення активних плагінів: '.rs' → 'Rust Module' тощо.
|
|
109
|
+
Синхронно і без установки (hot-path hook) — лише вже встановлені плагіни.
|
|
110
|
+
- getUnavailableDeclaredPlugins — Задекларовані у `config.plugins` пакети, недоступні в `node_modules` (не встановлені).
|
|
111
|
+
Не встановлює нічого (`allowInstall: false`) і не друкує — чистий предикат для
|
|
112
|
+
explicit CLI-діагностики (напр. doc-files: 0 кандидатів через невстановлений плагін),
|
|
113
|
+
яка сама вирішує, коли й де показати попередження. Автодетектовані (не задекларовані
|
|
114
|
+
явно) плагіни тут не враховуються — сигнал стосується саме явного `.n-rules.json`.
|
|
115
|
+
- getHandlers — Handlers для extension-point правила ядра (v1 — лише API; перший споживач — v2).
|
|
116
|
+
- clearPluginResolveCache — Скидає кеш резолву (для тестів).
|
|
117
|
+
|
|
118
|
+
## Гарантії поведінки
|
|
119
|
+
|
|
120
|
+
- Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
|
|
121
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
122
|
+
- За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
|
|
123
|
+
- Кешує результати в межах одного прогону.
|
|
124
|
+
- Свідомо пропускає шляхи: `.github`, `.git`, `node_modules`.
|
|
@@ -401,6 +401,26 @@ export function getDocFilesExtensions(projectRoot, config) {
|
|
|
401
401
|
return out
|
|
402
402
|
}
|
|
403
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Задекларовані у `config.plugins` пакети, недоступні в `node_modules` (не встановлені).
|
|
406
|
+
* Не встановлює нічого (`allowInstall: false`) і не друкує — чистий предикат для
|
|
407
|
+
* explicit CLI-діагностики (напр. doc-files: 0 кандидатів через невстановлений плагін),
|
|
408
|
+
* яка сама вирішує, коли й де показати попередження. Автодетектовані (не задекларовані
|
|
409
|
+
* явно) плагіни тут не враховуються — сигнал стосується саме явного `.n-rules.json`.
|
|
410
|
+
* @param {string} projectRoot корінь репозиторію
|
|
411
|
+
* @param {{ plugins?: unknown } | null | undefined} config розпарсений `.n-rules.json`
|
|
412
|
+
* @returns {string[]} npm-імена задекларованих, але не встановлених плагінів (порожньо — усе гаразд)
|
|
413
|
+
*/
|
|
414
|
+
export function getUnavailableDeclaredPlugins(projectRoot, config) {
|
|
415
|
+
const declared = Array.isArray(config?.plugins)
|
|
416
|
+
? config.plugins.filter(p => typeof p === 'string' && p.trim() !== '')
|
|
417
|
+
: []
|
|
418
|
+
if (declared.length === 0) return []
|
|
419
|
+
const root = resolve(projectRoot)
|
|
420
|
+
const availableNames = new Set(resolvePlugins(root, config, { allowInstall: false, quiet: true }).map(p => p.name))
|
|
421
|
+
return declared.filter(name => !availableNames.has(name))
|
|
422
|
+
}
|
|
423
|
+
|
|
404
424
|
/**
|
|
405
425
|
* Handlers для extension-point правила ядра (v1 — лише API; перший споживач — v2).
|
|
406
426
|
* @param {string} projectRoot корінь репозиторію
|