@nitra/cursor 5.3.2 → 5.3.4
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 +12 -0
- package/lib/docs/llm.md +23 -12
- package/package.json +1 -1
- package/rules/abie/fix.mjs +1 -2
- package/rules/adr/fix.mjs +1 -2
- package/rules/bun/fix.mjs +1 -2
- package/rules/capacitor/fix.mjs +1 -2
- package/rules/capacitor/js/platforms.mjs +0 -2
- package/rules/changelog/fix.mjs +1 -2
- package/rules/ci4/fix.mjs +1 -2
- package/rules/docker/fix.mjs +1 -2
- package/rules/efes/fix.mjs +1 -2
- package/rules/feedback/fix.mjs +1 -2
- package/rules/ga/fix.mjs +1 -2
- package/rules/graphql/fix.mjs +1 -2
- package/rules/hasura/fix.mjs +1 -2
- package/rules/image-avif/fix.mjs +1 -2
- package/rules/image-compress/fix.mjs +1 -2
- package/rules/js-bun-db/fix.mjs +1 -2
- package/rules/js-bun-redis/fix.mjs +1 -2
- package/rules/js-lint/fix.mjs +1 -2
- package/rules/js-lint-ci/fix.mjs +1 -2
- package/rules/js-mssql/fix.mjs +1 -2
- package/rules/js-run/fix.mjs +1 -2
- package/rules/k8s/fix.mjs +1 -2
- package/rules/k8s/js/manifests.mjs +1 -5
- package/rules/nginx-default-tpl/fix.mjs +1 -2
- package/rules/npm-module/fix.mjs +1 -2
- package/rules/npm-module/js/package_structure.mjs +0 -1
- package/rules/php/fix.mjs +1 -2
- package/rules/python/fix.mjs +1 -2
- package/rules/rego/fix.mjs +1 -2
- package/rules/release/fix.mjs +1 -2
- package/rules/rust/fix.mjs +1 -2
- package/rules/security/fix.mjs +1 -2
- package/rules/style-lint/fix.mjs +1 -2
- package/rules/tauri/fix.mjs +1 -2
- package/rules/test/coverage/coverage.mjs +0 -2
- package/rules/test/fix.mjs +1 -2
- package/rules/text/fix.mjs +1 -2
- package/rules/vue/fix.mjs +1 -2
- package/rules/worktree/fix.mjs +1 -2
- package/scripts/lib/run-rule.mjs +0 -2
- package/scripts/lint-cli.mjs +0 -1
- package/scripts/utils/with-lock.mjs +0 -1
- package/skills/doc-aggregate/js/docgen-scan.mjs +17 -18
- package/skills/doc-files/.changes/260612-0002.md +5 -0
- package/skills/doc-files/.changes/260612-0006.md +5 -0
- package/skills/doc-files/.changes/260612-0008.md +5 -0
- package/skills/doc-files/.changes/260612-0012.md +5 -0
- package/skills/doc-files/js/docgen-extract.mjs +136 -0
- package/skills/doc-files/js/docgen-prompts.mjs +2 -2
- package/skills/doc-files/js/docgen-scan.mjs +21 -22
- package/skills/doc-files/js/docs/docgen-extract-anchors.md +28 -10
- package/skills/doc-files/js/docs/docgen-extract.md +22 -12
- package/skills/doc-files/js/docs/docgen-files-batch.md +21 -11
- package/skills/doc-files/js/docs/docgen-gen.md +29 -13
- package/skills/doc-files/js/docs/docgen-ignore.md +37 -0
- package/skills/doc-files/js/docs/units-rs.md +35 -0
- package/skills/doc-files/js/units-rs.mjs +213 -0
- package/skills/doc-files/js/units.mjs +4 -3
|
@@ -1,30 +1,46 @@
|
|
|
1
1
|
---
|
|
2
2
|
docgen:
|
|
3
3
|
source: npm/skills/doc-files/js/docgen-gen.mjs
|
|
4
|
-
crc:
|
|
4
|
+
crc: e2af04d6
|
|
5
|
+
score: 100
|
|
5
6
|
---
|
|
6
7
|
|
|
7
|
-
# docgen-gen
|
|
8
|
+
# docgen-gen.mjs
|
|
8
9
|
|
|
9
10
|
## Огляд
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Огляд
|
|
13
|
+
Публічні функції виконують операції з текстом та оцінками. Вони включають виділення коду, вставлення інформації та генерацію документів на основі визначених параметрів.
|
|
12
14
|
|
|
13
15
|
## Поведінка
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
splitProtected
|
|
18
|
+
Вибирає та видаляє код-фенс-обгортку з секції
|
|
19
|
+
|
|
20
|
+
insertProtected
|
|
21
|
+
Вставляє захищений блок Призначення після заголовка Огляд
|
|
22
|
+
|
|
23
|
+
scoreDoc
|
|
24
|
+
Перевіряє вихід проти фактів і повертає оцінку та список проблем
|
|
25
|
+
|
|
26
|
+
DEFAULT_LOCAL_MODEL
|
|
27
|
+
Повертає дефолтну модель для роботи
|
|
28
|
+
|
|
29
|
+
generateDoc
|
|
30
|
+
Генерує документ з детермінованою оцінкою та метаданими
|
|
20
31
|
|
|
21
32
|
## Публічний API
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
splitProtected — Відокремлює захищену секцію `## Призначення` (Варіант B). Межа — наступний `## ` і `###` не обриває блок. (abie.mdc)
|
|
35
|
+
insertProtected — Вставляє захищений блок `## Призначення` одразу після H1 (фіксована позиція). (abie.mdc)
|
|
36
|
+
scoreDoc — Stage 2.5 — Детермінований скоринг (0 токенів): перевіряє вихід проти фактів. (abie.mdc)
|
|
37
|
+
DEFAULT_LOCAL_MODEL — Дефолтна модель: N_CURSOR_DOCGEN_MODEL → resolveModel → omlx напряму. Останній fallback гарантує локальний виклик без змін середовища (через pi CLI той самий локальний виклик виміряно повільніший на ~46%). (abie.mdc)
|
|
38
|
+
generateDoc — Головний API: файл → md-дока з det-оцінкою. (abie.mdc)
|
|
25
39
|
|
|
26
40
|
## Гарантії поведінки
|
|
27
41
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
42
|
+
- Read-only: файл не виконує операцій запису у файлову систему.
|
|
43
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
44
|
+
- За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
|
|
45
|
+
- Кешує результати в межах одного прогону.
|
|
46
|
+
- Не звертається до мережі.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/docgen-ignore.mjs
|
|
4
|
+
crc: f9206fe0
|
|
5
|
+
score: 100
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# docgen-ignore.mjs
|
|
9
|
+
|
|
10
|
+
## Огляд
|
|
11
|
+
|
|
12
|
+
DOCGEN_IGNORE_GLOBS
|
|
13
|
+
Список шляхів, які docgen повинен ігнорувати.
|
|
14
|
+
|
|
15
|
+
isDocgenIgnored
|
|
16
|
+
Перевіряє, чи вказаний шлях знаходиться у списку ігнорованих шляхів. Повертає булеве значення.
|
|
17
|
+
|
|
18
|
+
## Поведінка
|
|
19
|
+
|
|
20
|
+
DOCGEN_IGNORE_GLOBS
|
|
21
|
+
Базовий список glob-ів для docgen ignore
|
|
22
|
+
|
|
23
|
+
isDocgenIgnored
|
|
24
|
+
Перевіряє, чи шлях має бути пропущений docgen
|
|
25
|
+
|
|
26
|
+
## Публічний API
|
|
27
|
+
|
|
28
|
+
DOCGEN_IGNORE_GLOBS — Базовий список glob-ів для ігнорування у `docgen`.
|
|
29
|
+
isDocgenIgnored — Визначає, чи шлях повинен бути пропущений `docgen`. Для `kind = 'dir'` працює і на підкаталоги, наприклад, `**\\/demo/**` спрацьовує на `demo/x` під час рекурсивного обходу.
|
|
30
|
+
|
|
31
|
+
## Гарантії поведінки
|
|
32
|
+
|
|
33
|
+
- Read-only: файл не виконує операцій запису у файлову систему.
|
|
34
|
+
- За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
|
|
35
|
+
- Кешує результати в межах одного прогону.
|
|
36
|
+
- Свідомо пропускає шляхи: `.git`, `node_modules`.
|
|
37
|
+
- Не звертається до мережі.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
docgen:
|
|
3
|
+
source: npm/skills/doc-files/js/units-rs.mjs
|
|
4
|
+
crc: 11099381
|
|
5
|
+
score: 100
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# units-rs.mjs
|
|
9
|
+
|
|
10
|
+
## Огляд
|
|
11
|
+
|
|
12
|
+
Файл аналізує рядки для визначення структури та елементів модуля. Він ітерує по рядках для визначення глибини, обробляє рядкові літерали для визначення зміщення, змінює глибину при зустрічі відкриваючої чи закриваючої фігу, видаляє відкриті блоки зі стека, перевіряє наявність атрибутів експонування, визначає декларації на верхньому рівні чи в модулях, а також витягує тіло функції та збирає виклики інших юнітів у тілі функції.
|
|
13
|
+
|
|
14
|
+
## Поведінка
|
|
15
|
+
|
|
16
|
+
1. Скан файлу по рядках.
|
|
17
|
+
2. Ітерація по рядках для визначення глибини.
|
|
18
|
+
3. Обробка рядкових літералів для визначення зміщення.
|
|
19
|
+
4. Зміна глибини при зустрічі відкриваючої фігу.
|
|
20
|
+
5. Зміна глибини при зустрічі закриваючої фігу.
|
|
21
|
+
6. Видалення відкритих блоків з стека.
|
|
22
|
+
7. Перевірка наявності атрибутів експонування.
|
|
23
|
+
8. Визначення декларацій на верхньому рівні або в модулях.
|
|
24
|
+
9. Визначення публічних функцій, структур, перерахувань, трейтів, типів.
|
|
25
|
+
10. Витягування тіла функції через пошук закриваючої фігу.
|
|
26
|
+
11. Збір викликів інших юнітів у тілі функції.
|
|
27
|
+
|
|
28
|
+
## Публічний API
|
|
29
|
+
|
|
30
|
+
extractUnitsRs — Визначає top-level і impl-методи через підрахунок дужок по рядках. Обмеження: рядкові літерали з `{`/`}` всередині `{}` можуть дати хибну глибину.
|
|
31
|
+
|
|
32
|
+
## Гарантії поведінки
|
|
33
|
+
|
|
34
|
+
- Read-only: файл не виконує операцій запису у файлову систему.
|
|
35
|
+
- Не звертається до мережі.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/** @see ./docs/units-rs.md */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Пропускає рядковий літерал `"..."` (з escape-послідовностями).
|
|
5
|
+
* @param {string} src вміст файлу
|
|
6
|
+
* @param {number} i позиція відкриваючого `"`
|
|
7
|
+
* @returns {number} позиція ПІСЛЯ закриваючого `"`
|
|
8
|
+
*/
|
|
9
|
+
function skipString(src, i) {
|
|
10
|
+
i++ // відкриваючий "
|
|
11
|
+
while (i < src.length) {
|
|
12
|
+
if (src[i] === '\\') { i += 2; continue }
|
|
13
|
+
if (src[i] === '"') return i + 1
|
|
14
|
+
i++
|
|
15
|
+
}
|
|
16
|
+
return i
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Знаходить індекс закриваючої `}` для відкриваючої `{` на позиції `start`.
|
|
21
|
+
* Правильно пропускає рядки/блочні коментарі та рядкові літерали.
|
|
22
|
+
* @param {string} src вміст файлу
|
|
23
|
+
* @param {number} start позиція відкриваючої `{`
|
|
24
|
+
* @returns {number} індекс `}` або -1, якщо не знайдено
|
|
25
|
+
*/
|
|
26
|
+
function findClosingBrace(src, start) {
|
|
27
|
+
let depth = 0
|
|
28
|
+
let i = start
|
|
29
|
+
while (i < src.length) {
|
|
30
|
+
const ch = src[i]
|
|
31
|
+
if (ch === '/' && src[i + 1] === '/') {
|
|
32
|
+
const nl = src.indexOf('\n', i)
|
|
33
|
+
i = nl === -1 ? src.length : nl + 1
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
if (ch === '/' && src[i + 1] === '*') {
|
|
37
|
+
const end = src.indexOf('*/', i + 2)
|
|
38
|
+
i = end === -1 ? src.length : end + 2
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
if (ch === '"') { i = skipString(src, i); continue }
|
|
42
|
+
if (ch === '{') { depth++; i++; continue }
|
|
43
|
+
if (ch === '}') {
|
|
44
|
+
depth--
|
|
45
|
+
if (depth === 0) return i
|
|
46
|
+
i++
|
|
47
|
+
continue
|
|
48
|
+
}
|
|
49
|
+
i++
|
|
50
|
+
}
|
|
51
|
+
return -1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Видобуває `///` doc-рядки безпосередньо перед рядком `lineIdx`.
|
|
56
|
+
* Сканує назад через `///`, `#[...]` та пусті рядки.
|
|
57
|
+
* @param {string[]} lines рядки файлу
|
|
58
|
+
* @param {number} lineIdx рядок декларації
|
|
59
|
+
* @returns {string} склеєний опис або ''
|
|
60
|
+
*/
|
|
61
|
+
function docBefore(lines, lineIdx) {
|
|
62
|
+
const doc = []
|
|
63
|
+
for (let i = lineIdx - 1; i >= 0; i--) {
|
|
64
|
+
const t = lines[i].trim()
|
|
65
|
+
if (t.startsWith('///')) {
|
|
66
|
+
doc.unshift(t.slice(3).trim())
|
|
67
|
+
} else if (t.startsWith('#[') || t.startsWith('#![') || t === '') {
|
|
68
|
+
// пропустити атрибути та пусті рядки
|
|
69
|
+
} else {
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return doc.join(' ').trim()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Pub-items: pub fn / pub struct / pub enum / pub trait / pub type
|
|
77
|
+
// Також ловить fn без pub (для localSymbols і impl-методів)
|
|
78
|
+
const ITEM_RE = /^[ \t]*(pub(?:\([^)]*\))?\s+)?(?:async\s+)?(?:unsafe\s+)?(fn|struct|enum|trait|type)\s+(\w+)/
|
|
79
|
+
|
|
80
|
+
// impl Type { або impl<T> Trait for Type {
|
|
81
|
+
const IMPL_RE = /^[ \t]*(?:pub\s+)?impl(?:<[^>]*>)?\s+(?:(?:\w[\w:<>, ]*\s+for\s+))?(\w+)/
|
|
82
|
+
|
|
83
|
+
// Підозрілі exposure-атрибути, що роблять непуб-fn фактично публічними
|
|
84
|
+
const EXPOSURE_ATTR_RE = /#\[(?:tauri::command|wasm_bindgen|uniffi::export|pyo3::pyfunction|napi)/
|
|
85
|
+
|
|
86
|
+
// Базовий виклик fn-імені (для call-graph всередині юніта)
|
|
87
|
+
const CALL_RE = /\b([a-z_]\w*)\s*\(/g
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Юніт-екстрактор для `.rs` файлів.
|
|
91
|
+
* Визначає top-level і impl-методи через підрахунок дужок по рядках.
|
|
92
|
+
* Відомі обмеження: рядкові літерали з `{`/`}` всередині `{}` можуть дати
|
|
93
|
+
* хибну глибину (рідкісно в реальному Rust-коді з rustfmt).
|
|
94
|
+
* @param {string} src вміст файлу
|
|
95
|
+
* @param {string} [_relPath] резервний (не використовується)
|
|
96
|
+
* @returns {Array<{name:string, kind:string, exported:boolean, implName:string|null, span:{start:number,end:number}, body:string, calls:string[], doc:string}>|null}
|
|
97
|
+
*/
|
|
98
|
+
export function extractUnitsRs(src, _relPath) {
|
|
99
|
+
const lines = src.split('\n')
|
|
100
|
+
const units = []
|
|
101
|
+
let depth = 0
|
|
102
|
+
let lineOffset = 0
|
|
103
|
+
// Стек відкритих impl: { typeName, openDepth }
|
|
104
|
+
const implStack = []
|
|
105
|
+
// Флаг: наступний fn отримує exposure (через #[tauri::command] тощо)
|
|
106
|
+
let nextFnExposed = false
|
|
107
|
+
|
|
108
|
+
for (let li = 0; li < lines.length; li++) {
|
|
109
|
+
const line = lines[li]
|
|
110
|
+
const depthAtStart = depth
|
|
111
|
+
|
|
112
|
+
// Підраховуємо `{` і `}` в рядку (пропускаємо рядкові коментарі та рядки)
|
|
113
|
+
let j = 0
|
|
114
|
+
while (j < line.length) {
|
|
115
|
+
const ch = line[j]
|
|
116
|
+
if (ch === '/' && line[j + 1] === '/') break
|
|
117
|
+
if (ch === '"') {
|
|
118
|
+
j++
|
|
119
|
+
while (j < line.length && line[j] !== '"') {
|
|
120
|
+
if (line[j] === '\\') j++
|
|
121
|
+
j++
|
|
122
|
+
}
|
|
123
|
+
j++
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
if (ch === '{') depth++
|
|
127
|
+
else if (ch === '}') depth--
|
|
128
|
+
j++
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Закриті impl-блоки прибираємо зі стека
|
|
132
|
+
while (implStack.length > 0 && implStack[implStack.length - 1].openDepth > depth) {
|
|
133
|
+
implStack.pop()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const currentImpl = implStack.at(-1)?.typeName ?? null
|
|
137
|
+
|
|
138
|
+
// Перевіряємо exposure-атрибути
|
|
139
|
+
if (EXPOSURE_ATTR_RE.test(line)) {
|
|
140
|
+
nextFnExposed = true
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Impl-декларація (зазвичай глибина 0, але може бути в mod)
|
|
144
|
+
if (depthAtStart <= 1) {
|
|
145
|
+
const implM = line.match(IMPL_RE)
|
|
146
|
+
if (implM && line.includes('{')) {
|
|
147
|
+
implStack.push({ typeName: implM[1], openDepth: depth })
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Елементи на глибині 0 (top-level) і 1 (всередині impl)
|
|
152
|
+
if (depthAtStart <= 1) {
|
|
153
|
+
const m = line.match(ITEM_RE)
|
|
154
|
+
if (m) {
|
|
155
|
+
const isPub = Boolean(m[1]) || (m[2] === 'fn' && nextFnExposed)
|
|
156
|
+
if (m[2] === 'fn') nextFnExposed = false
|
|
157
|
+
const kind = m[2]
|
|
158
|
+
const name = m[3]
|
|
159
|
+
const doc = docBefore(lines, li)
|
|
160
|
+
|
|
161
|
+
// Витягуємо тіло через findClosingBrace для fn/struct/enum/trait
|
|
162
|
+
let body = ''
|
|
163
|
+
let itemEnd = lineOffset + line.length
|
|
164
|
+
if (kind !== 'type') {
|
|
165
|
+
const openBraceIdx = src.indexOf('{', lineOffset)
|
|
166
|
+
// Шукаємо `{` не далі ніж через 3 рядки від початку декларації
|
|
167
|
+
const threeLines = lines.slice(li, li + 3).join('\n').length
|
|
168
|
+
if (openBraceIdx !== -1 && openBraceIdx - lineOffset <= threeLines) {
|
|
169
|
+
const closeIdx = findClosingBrace(src, openBraceIdx)
|
|
170
|
+
if (closeIdx !== -1) {
|
|
171
|
+
itemEnd = closeIdx + 1
|
|
172
|
+
body = src.slice(lineOffset, itemEnd)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
units.push({
|
|
178
|
+
name,
|
|
179
|
+
kind,
|
|
180
|
+
exported: isPub,
|
|
181
|
+
implName: depthAtStart === 1 ? currentImpl : null,
|
|
182
|
+
span: { start: lineOffset, end: itemEnd },
|
|
183
|
+
body,
|
|
184
|
+
calls: [],
|
|
185
|
+
doc
|
|
186
|
+
})
|
|
187
|
+
} else {
|
|
188
|
+
// Рядок не є item — скидаємо exposure-флаг якщо не атрибут
|
|
189
|
+
const t = line.trim()
|
|
190
|
+
if (!t.startsWith('#[') && !t.startsWith('#![') && !t.startsWith('///') && t !== '') {
|
|
191
|
+
nextFnExposed = false
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
lineOffset += line.length + 1 // +1 для '\n'
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Базовий call-graph: виклики інших юнітів цього файлу
|
|
200
|
+
const unitNames = new Set(units.map(u => u.name))
|
|
201
|
+
for (const u of units) {
|
|
202
|
+
if (!u.body) continue
|
|
203
|
+
const calls = new Set()
|
|
204
|
+
let cm
|
|
205
|
+
const re = new RegExp(CALL_RE.source, 'g')
|
|
206
|
+
while ((cm = re.exec(u.body)) !== null) {
|
|
207
|
+
if (unitNames.has(cm[1]) && cm[1] !== u.name) calls.add(cm[1])
|
|
208
|
+
}
|
|
209
|
+
u.calls = [...calls]
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return units.length > 0 ? units : null
|
|
213
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/** @see ./docs/units.md */
|
|
2
2
|
|
|
3
3
|
import { extractUnitsJs } from './units-js.mjs'
|
|
4
|
+
import { extractUnitsRs } from './units-rs.mjs'
|
|
4
5
|
|
|
5
6
|
const JS_EXT = new Set(['js', 'mjs', 'ts', 'jsx', 'tsx', 'cts', 'mts'])
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* Мовно-агностичний фасад
|
|
9
|
-
* js/mjs/ts → oxc; vue/py
|
|
10
|
-
* відкочується на whole-file шлях, як і раніше).
|
|
9
|
+
* Мовно-агностичний фасад юніт-шару. Диспатчить за розширенням:
|
|
10
|
+
* js/mjs/ts → oxc AST; rs → regex+brace-counting; vue/py → null (whole-file шлях).
|
|
11
11
|
* @param {string} src вміст файлу
|
|
12
12
|
* @param {string} relPath шлях файлу
|
|
13
13
|
* @returns {Array<object>|null} юніти або null, якщо мова ще не підтримана / файл не парситься
|
|
@@ -15,5 +15,6 @@ const JS_EXT = new Set(['js', 'mjs', 'ts', 'jsx', 'tsx', 'cts', 'mts'])
|
|
|
15
15
|
export function extractUnits(src, relPath) {
|
|
16
16
|
const ext = (relPath.split('.').pop() || '').toLowerCase()
|
|
17
17
|
if (JS_EXT.has(ext)) return extractUnitsJs(src, relPath)
|
|
18
|
+
if (ext === 'rs') return extractUnitsRs(src, relPath)
|
|
18
19
|
return null
|
|
19
20
|
}
|