# fix.mjs — точка входу правила `js-lint`

## Огляд

Файл `npm/rules/js-lint/fix.mjs` — це тонкий ентрі-поінт (entry-point) правила з ідентифікатором `js-lint` для CLI `@nitra/cursor`. Він не містить власної логіки перевірок: уся механіка прогону правила інкапсульована у бібліотечних модулях `run-standard-rule.mjs` (стандартний пайплайн правил) та `run-rule-cli.mjs` (CLI-обгортка).

Модуль виконує **подвійну роль**:

1. **Library mode** — експортує функцію `run(ctx)`, яку імпортують інші частини оркестратора правил. У цьому режимі правило виконується в межах єдиного процесу, отримує контекст (`ctx`) із кешем обходу файлової системи (`walkCache`) та інших спільних структур, і повертає exit-код як значення Promise.
2. **Standalone mode** — якщо файл запущено напряму як CLI (`bun rules/js-lint/fix.mjs`), виконується повноцінний CLI-цикл: завантаження конфігурації, застосування whitelist-фільтрів, виведення підсумків і завершення процесу з відповідним exit-кодом (повний еквівалент команди `npx @nitra/cursor fix js-lint`).

Стандартний пайплайн, який стоїть за `runStandardRule`, складається з чотирьох послідовних фаз: `applies` (визначення цільових файлів) → `JS-concerns` (специфічні для правила перевірки/фікси) → `policy` (агрегування політик) → `mdc-refs` (узгодження з `.mdc`-метаописами правил).

## Експорти / API

| Символ | Тип                               | Призначення                                                                                                                                                                         |
| ------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `run`  | `function(ctx?): Promise<number>` | Library-API: запускає правило `js-lint` в межах поточного процесу, повертає exit-код (0 — OK, 1 — порушення). Експортується як іменований експорт через `export function run(ctx)`. |

Default-експорт відсутній. Інших іменованих експортів немає.

## Функції

### `run(ctx)`

```js
export function run(ctx)
```

- **Сигнатура:** `run(ctx?: RuleContext): Promise<number>`
- **Параметри:**
  - `ctx` (необовʼязковий) — обʼєкт контексту прогону правила типу `RuleContext` (визначений у `../../scripts/lib/run-standard-rule.mjs`). Зазвичай містить кешовані структури на кшталт `walkCache` (кеш обходу файлової системи), що дозволяє оркестратору перевикористовувати дані між послідовними правилами без повторного обходу диску. Якщо аргумент відсутній — кеші ініціалізуються всередині `runStandardRule`.
- **Повертає:** `Promise<number>` — exit-код прогону правила:
  - `0` — правило не знайшло порушень або всі знайдені порушення були автоматично виправлені;
  - `1` — присутні порушення, які залишилися після фази fix (тобто потрібне ручне втручання або правило в режимі lint-only).
- **Реалізація:** повертає результат виклику `runStandardRule(import.meta.dirname, ctx)`. Перший аргумент `import.meta.dirname` — абсолютний шлях до директорії, у якій лежить `fix.mjs` (тобто `.../npm/rules/js-lint`). Саме за цим шляхом `runStandardRule` знаходить решту артефактів правила (наприклад, `applies.mjs`, `policy.mjs`, `mdc`-довідники, та власне fix-логіку).
- **Side effects:** делегуються до `runStandardRule`. Стандартно включають читання вихідних файлів проєкту, можливе перезаписування файлів коду під час фази fix, читання конфігурації правил, а також виведення прогресу/підсумків у stdout/stderr (залежить від конкретної реалізації стандартного пайплайна).

### Standalone-блок (top-level)

```js
if (isRunAsCli(import.meta.url)) {
  process.exit(await runRuleCli(import.meta.dirname))
}
```

- **Сигнатура:** не є функцією — це top-level умовний блок, який виконується один раз при завантаженні модуля.
- **Логіка:**
  1. `isRunAsCli(import.meta.url)` визначає, чи файл запущено як головний модуль (через `bun fix.mjs` / `node fix.mjs`) або імпортовано як бібліотеку. Стандартна ідіома: порівнює `import.meta.url` із URL процесу запуску.
  2. Якщо запуск standalone — виконується `await runRuleCli(import.meta.dirname)`, який повертає exit-код. Цей виклик інкапсулює CLI-обвʼязки: парсинг аргументів, завантаження користувацької/проєктної конфігурації, застосування whitelist (фільтра шляхів), виклик `run(ctx)` під капотом і друк фінального підсумку.
  3. `process.exit(...)` гарантовано завершує процес із отриманим exit-кодом — це необхідно для коректної інтеграції з CI-системами та IDE, які орієнтуються на код виходу.
- **Side effects:** завершення процесу Node/Bun. У standalone-режимі правило ніколи не повертає керування викликачу — замість return-у виконується `process.exit`.
- **Зауваження по лінтерам:** перед викликом `process.exit` стоїть директива `// eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE` — це свідомий виняток із загальної заборони на `process.exit` для standalone entry-point-ів.

## Залежності

### Внутрішні (модулі репозиторію)

- `../../scripts/lib/run-rule-cli.mjs` — імпортуються:
  - `isRunAsCli` — детектор режиму запуску (CLI vs library).
  - `runRuleCli` — повноцінна CLI-обгортка для правила: парсинг аргументів, конфіг, whitelist, виклик `run`, друк summary; повертає `Promise<number>` (exit-код).
- `../../scripts/lib/run-standard-rule.mjs` — імпортується:
  - `runStandardRule` — стандартний пайплайн правил у чотири фази (`applies` → `JS-concerns` → `policy` → `mdc-refs`), приймає шлях до директорії правила та опційний контекст; повертає `Promise<number>`.
  - Також звідси типізовано `RuleContext` (через JSDoc-тип `import('../../scripts/lib/run-standard-rule.mjs').RuleContext`).

### Зовнішні / runtime

- `import.meta.dirname` — Node/Bun runtime API (стабільний з Node 20.11). Повертає абсолютний шлях до директорії модуля. Використовується як «адреса» правила для оркестратора.
- `import.meta.url` — URL поточного модуля; передається у `isRunAsCli` для розрізнення CLI-запуску й import-режиму.
- `process.exit` — Node/Bun global, термінує процес із заданим exit-кодом.
- Top-level `await` — використовується для `await runRuleCli(...)`; вимагає ES-modules (`.mjs`) і середовища з підтримкою top-level await.

Жодних npm-пакетів модуль напряму не імпортує.

## Потік виконання / Використання

### Сценарій 1: запуск як CLI (standalone)

```bash
bun npm/rules/js-lint/fix.mjs
# або еквівалент через головний CLI:
npx @nitra/cursor fix js-lint
```

1. ES-модуль завантажується, виконуються імпорти `isRunAsCli`, `runRuleCli`, `runStandardRule`.
2. Оголошується експорт `run` (але одразу не викликається).
3. Виконується top-level `if (isRunAsCli(import.meta.url))`. У standalone це `true`.
4. Викликається `await runRuleCli(import.meta.dirname)`:
   - читається конфігурація проєкту (whitelist, exclude, опції правил);
   - визначається область дії (які файли потрапляють у правило `js-lint`);
   - усередині викликається `run(ctx)` із підготовленим контекстом;
   - друкується підсумок (порушення, виправлення, час).
5. `process.exit(<код>)` завершує процес. CI-система отримує `0` (успіх) або `1` (є порушення).

### Сценарій 2: виклик як бібліотеки (library mode)

```js
import { run } from './npm/rules/js-lint/fix.mjs'

const ctx = { walkCache: /* спільний кеш */, /* ... */ }
const exitCode = await run(ctx)
if (exitCode !== 0) {
  // обробка порушень: повторний прогін, escalation у CI тощо
}
```

1. Сторонній модуль (зазвичай головний оркестратор `@nitra/cursor`) імпортує `run`.
2. `isRunAsCli(import.meta.url)` для `fix.mjs` поверне `false` (бо `import.meta.url` модуля не збігається з URL процесу) — standalone-блок не виконується, `process.exit` не викликається.
3. Викликач передає у `run(ctx)` готовий контекст із попередньо побудованими кешами.
4. `run` делегує у `runStandardRule(import.meta.dirname, ctx)`, який виконує чотирифазний пайплайн правила і повертає exit-код.
5. Викликач сам вирішує, що робити з кодом (агрегувати з іншими правилами, ескалувати, друкувати тощо). Завершення процесу — на боці викликача.

### Інтеграція в загальну архітектуру правил

- Кожне правило в каталозі `npm/rules/<id>/` має власний `fix.mjs` за **однаковим шаблоном**: експортує `run(ctx)`, що делегує у `runStandardRule(import.meta.dirname, ctx)`, плюс standalone-блок із `runRuleCli`. Конкретні перевірки/фікси правила лежать у сусідніх файлах директорії (`applies.mjs`, `policy.mjs`, `check-*.mjs` тощо) і автоматично підхоплюються стандартним пайплайном за конвенцією шляхів.
- Для правила `js-lint` стандартний пайплайн виконує: відбір JS/TS/Vue-файлів (фаза `applies`) → специфічні JS-перевірки (фаза `JS-concerns`, реалізована в інших файлах цієї ж директорії) → агрегування політик (`policy`) → крос-перевірку з `.mdc`-описом правила (`mdc-refs`).
- Контракт «`fix.mjs` = тонка обгортка» уніфікує всі правила: уся складність захована за `runStandardRule` і `runRuleCli`. Завдяки цьому файл `fix.mjs` будь-якого правила залишається маленьким (одиниці рядків коду) і легко піддається ревʼю/документації.
