---
docgen:
  source: npm/scripts/lib/load-cursor-config.mjs
  crc: c86a80f4
---

# load-cursor-config.mjs

## Огляд

Утилітарний модуль для читання конфігураційного файлу `.n-cursor.json` з кореня репозиторію. Призначений для використання check-скриптами, що обходять файлову систему й мають виключати певні каталоги зі сканування.

Наразі експортує лише одну публічну функцію — `loadCursorIgnorePaths(root)`, яка повертає нормалізовані абсолютні posix-шляхи з масиву `ignore` у конфізі. Якщо конфіг відсутній, пошкоджений, або поле `ignore` має невалідний формат — функція повертає порожній масив без кидання винятків (fail-soft підхід).

Модуль свідомо **не валідує** структуру конфігу повністю — це робота окремої перевірки (наприклад, через `v8r`). Тут перевіряється лише наявність і тип поля `ignore` та його елементів.

Шлях до конфігу зашитий константою `CONFIG_FILE = '.n-cursor.json'` й читається з кореня репозиторію, переданого через параметр `root`.

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

| Експорт                 | Тип                                                | Призначення                                                          |
| ----------------------- | -------------------------------------------------- | -------------------------------------------------------------------- |
| `loadCursorIgnorePaths` | `async function (root: string): Promise<string[]>` | Зчитати й нормалізувати список ігнорованих шляхів з `.n-cursor.json` |

Внутрішні (не експортовані) сутності:

| Сутність      | Тип                                          | Призначення                                                        |
| ------------- | -------------------------------------------- | ------------------------------------------------------------------ |
| `CONFIG_FILE` | `string` (константа `'.n-cursor.json'`)      | Ім'я конфіг-файлу в корені репозиторію                             |
| `toAbsPosix`  | `function (root: string, p: string): string` | Нормалізатор шляху до абсолютного posix-формату без trailing-slash |

## Функції

### `toAbsPosix(root, p)`

Внутрішня (приватна для модуля) функція-нормалізатор шляху.

- **Сигнатура:** `function toAbsPosix(root: string, p: string): string`
- **Параметри:**
  - `root` — абсолютний корінь репозиторію (використовується для розв'язання відносних шляхів).
  - `p` — шлях з конфігу; може бути відносним або абсолютним.
- **Повертає:** абсолютний posix-шлях (роздільник `/`) без жодного завершального `/`.
- **Алгоритм:**
  1. Конвертує `p` у рядок через `String(p)` і прибирає пробіли по краях через `trim()`.
  2. Якщо результат вже абсолютний (`isAbsolute`), використовує його як є; інакше — резолвить відносно `root` через `resolve(root, trimmed)`.
  3. Замінює нативний роздільник платформи (`sep`) на `/` (через `split(sep).join('/')`) — на Windows це конвертує `\` у `/`.
  4. У циклі видаляє всі завершальні `/` (нормалізація `foo/bar//` → `foo/bar`).
- **Side effects:** немає (чиста функція над аргументами).

### `loadCursorIgnorePaths(root)`

Публічна async-функція — точка входу модуля.

- **Сигнатура:** `async function loadCursorIgnorePaths(root: string): Promise<string[]>`
- **Параметри:**
  - `root` — абсолютний шлях до кореня репозиторію, де очікується `.n-cursor.json`.
- **Повертає:** `Promise<string[]>` — масив абсолютних posix-шляхів без trailing-slash; може бути порожнім.
- **Алгоритм (fail-soft):**
  1. Обчислює абсолютний шлях до конфіг-файлу через `join(root, CONFIG_FILE)`.
  2. Якщо файлу немає на диску (`existsSync` повернув `false`) — повертає `[]`.
  3. Намагається прочитати файл через `readFile(file, 'utf8')` й розпарсити його як JSON. Якщо парсинг кидає виняток (битий JSON, помилка читання тощо) — `catch` повертає `[]`.
  4. Дістає поле `ignore` з розпарсеного об'єкта через optional chaining (`raw?.ignore`). Якщо поле не є масивом (`Array.isArray` повернув `false`) — повертає `[]`.
  5. Ітерує по `list`, для кожного елемента:
     - якщо це не рядок — пропускає;
     - тримить пробіли; якщо рядок став порожнім — пропускає;
     - інакше додає в результат `toAbsPosix(root, v)`.
  6. Повертає накопичений масив `out`.
- **Side effects:**
  - Синхронний `existsSync` на дисковий файл `<root>/.n-cursor.json`.
  - Асинхронне читання того ж файлу через `readFile` (тільки якщо `existsSync` повернув `true`).
  - Жодних записів, мережевих викликів чи мутацій глобального стану.
- **Обробка помилок:**
  - Файл не існує → `[]`.
  - Файл існує, але JSON битий або `readFile` падає → `[]` (через `try/catch`).
  - `ignore` не масив, або взагалі відсутнє → `[]`.
  - Нестрингові або порожні після `trim()` елементи масиву → мовчки пропускаються.

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

### Node.js built-ins

| Модуль             | Імпортовані сутності                   | Використання                                                                                                             |
| ------------------ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `node:fs`          | `existsSync`                           | Синхронна перевірка наявності `.n-cursor.json` перед читанням                                                            |
| `node:fs/promises` | `readFile`                             | Асинхронне читання вмісту конфіг-файлу як UTF-8                                                                          |
| `node:path`        | `isAbsolute`, `join`, `resolve`, `sep` | Перевірка абсолютності, склейка шляху до конфігу, розв'язання відносних шляхів, заміна платформного роздільника на posix |

### Зовнішні залежності

Жодних — лише стандартна бібліотека Node.js.

### Конфігураційні артефакти

- `.n-cursor.json` у корені репозиторію — джерело правди для `ignore` (опціональне поле, масив рядків).

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

### Типовий сценарій виклику

Викликач (наприклад, check-скрипт) передає абсолютний корінь репозиторію й отримує перелік каталогів, які слід виключити з обходу.

```js
import { loadCursorIgnorePaths } from './load-cursor-config.mjs'

const root = process.cwd() // або інший абсолютний корінь
const ignored = await loadCursorIgnorePaths(root)
// ignored: ['/abs/path/.worktrees', '/abs/path/node_modules', ...]

// Далі при обході файлів пропускаємо ті, що під будь-яким із ignored:
/**
 *
 */
function isIgnored(absPosixPath) {
  return ignored.some(ig => absPosixPath === ig || absPosixPath.startsWith(ig + '/'))
}
```

### Очікуваний формат `.n-cursor.json`

```json
{
  "ignore": [".worktrees", "node_modules", "/abs/path/to/skip"]
}
```

Інші поля у файлі допустимі — їх ігнорує цей модуль (валідація схеми — окрема відповідальність).

### Послідовність кроків усередині `loadCursorIgnorePaths`

1. `join(root, '.n-cursor.json')` → шлях до конфігу.
2. `existsSync(file)` → якщо `false`, ранній вихід з `[]`.
3. `readFile + JSON.parse` у `try/catch` → при будь-якій помилці `[]`.
4. `Array.isArray(raw?.ignore)` → якщо `false`, `[]`.
5. Лінійний прохід по `ignore`-елементах: фільтр по типу й непорожності, нормалізація через `toAbsPosix(root, v)`.
6. Повернення масиву `out`.

### Інваріанти результату

- Усі шляхи — **абсолютні**.
- Усі шляхи — у **posix-форматі** (роздільник `/`), навіть на Windows.
- Жоден шлях не має **завершального `/`**.
- Порядок шляхів — відповідає порядку в `ignore` (вхідні дублікати **не дедуплікуються**).
- За жодних умов функція не кидає винятки — повертає `[]` як єдиний "поганий" результат.

### Тестування (Rebuild Test)

Перевірка контрактів модуля:

- Файлу немає → `loadCursorIgnorePaths(root)` резолвиться у `[]`.
- Файл є, але JSON битий → `[]`.
- Файл є, але `ignore` відсутнє/не масив → `[]`.
- `ignore` містить нестрингові елементи й порожні рядки — вони відфільтровані; рядкові — нормалізовані до абсолютного posix-шляху без trailing-slash.
- Відносний шлях у `ignore` (наприклад `"node_modules"`) перетворюється на `<root>/node_modules` у posix-формі.
- Абсолютний шлях у `ignore` зберігається як є (з можливою конверсією `\` → `/`).
- На Windows шляхи з `\` конвертуються у `/`.
- Trailing `/` (один або кілька) видаляються.
