@7n/rules 1.21.0 → 1.23.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 +16 -0
- package/package.json +1 -1
- package/rules/doc-files/check/docs/main.md +1 -1
- package/rules/doc-files/check/main.mjs +1 -1
- package/rules/doc-files/docgen-crc/docs/main.md +1 -1
- package/rules/doc-files/docgen-crc/main.mjs +7 -6
- package/rules/doc-files/docgen-extract/docs/main.md +1 -1
- package/rules/doc-files/docgen-extract/main.mjs +0 -196
- package/rules/doc-files/docgen-gen/docs/main.md +1 -1
- package/rules/doc-files/docgen-gen/main.mjs +6 -1
- package/rules/doc-files/docgen-scan/docs/index.md +10 -0
- package/rules/doc-files/docgen-scan/docs/lang-extensions.md +34 -0
- package/rules/doc-files/docgen-scan/docs/main.md +1 -1
- package/rules/doc-files/docgen-scan/lang-extensions.mjs +89 -0
- package/rules/doc-files/docgen-scan/main.mjs +16 -7
- package/rules/doc-files/units/docs/main.md +1 -2
- package/rules/doc-files/units/main.mjs +2 -3
- package/scripts/lib/docs/resolve-plugins.md +1 -1
- package/scripts/lib/resolve-plugins.mjs +30 -4
- package/skills/doc-files/SKILL.md +1 -1
- package/skills/taze/js/docs/orchestrate.md +1 -1
- package/skills/taze/js/orchestrate.mjs +175 -64
- package/rules/doc-files/units-rs/concern.json +0 -3
- package/rules/doc-files/units-rs/docs/main.md +0 -36
- package/rules/doc-files/units-rs/main.mjs +0 -311
|
@@ -1,311 +0,0 @@
|
|
|
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] === '\\') {
|
|
13
|
-
i += 2
|
|
14
|
-
continue
|
|
15
|
-
}
|
|
16
|
-
if (src[i] === '"') return i + 1
|
|
17
|
-
i++
|
|
18
|
-
}
|
|
19
|
-
return i
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Пропускає рядковий (`//`) чи блочний коментар на позиції `i`.
|
|
24
|
-
* @param {string} src вміст файлу
|
|
25
|
-
* @param {number} i позиція `/` початку коментаря
|
|
26
|
-
* @returns {number} позиція ПІСЛЯ коментаря, або `-1` якщо на `i` не коментар
|
|
27
|
-
*/
|
|
28
|
-
function skipComment(src, i) {
|
|
29
|
-
if (src[i] !== '/') return -1
|
|
30
|
-
if (src[i + 1] === '/') {
|
|
31
|
-
const nl = src.indexOf('\n', i)
|
|
32
|
-
return nl === -1 ? src.length : nl + 1
|
|
33
|
-
}
|
|
34
|
-
if (src[i + 1] === '*') {
|
|
35
|
-
const end = src.indexOf('*/', i + 2)
|
|
36
|
-
return end === -1 ? src.length : end + 2
|
|
37
|
-
}
|
|
38
|
-
return -1
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Знаходить індекс закриваючої `}` для відкриваючої `{` на позиції `start`.
|
|
43
|
-
* Правильно пропускає рядки/блочні коментарі та рядкові літерали.
|
|
44
|
-
* @param {string} src вміст файлу
|
|
45
|
-
* @param {number} start позиція відкриваючої `{`
|
|
46
|
-
* @returns {number} індекс `}` або -1, якщо не знайдено
|
|
47
|
-
*/
|
|
48
|
-
function findClosingBrace(src, start) {
|
|
49
|
-
let depth = 0
|
|
50
|
-
let i = start
|
|
51
|
-
while (i < src.length) {
|
|
52
|
-
const ch = src[i]
|
|
53
|
-
const afterComment = skipComment(src, i)
|
|
54
|
-
if (afterComment !== -1) {
|
|
55
|
-
i = afterComment
|
|
56
|
-
continue
|
|
57
|
-
}
|
|
58
|
-
if (ch === '"') {
|
|
59
|
-
i = skipString(src, i)
|
|
60
|
-
continue
|
|
61
|
-
}
|
|
62
|
-
if (ch === '{') {
|
|
63
|
-
depth++
|
|
64
|
-
i++
|
|
65
|
-
continue
|
|
66
|
-
}
|
|
67
|
-
if (ch === '}') {
|
|
68
|
-
depth--
|
|
69
|
-
if (depth === 0) return i
|
|
70
|
-
i++
|
|
71
|
-
continue
|
|
72
|
-
}
|
|
73
|
-
i++
|
|
74
|
-
}
|
|
75
|
-
return -1
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Видобуває `///` doc-рядки безпосередньо перед рядком `lineIdx`.
|
|
80
|
-
* Сканує назад через `///`, `#[...]` та пусті рядки.
|
|
81
|
-
* @param {string[]} lines рядки файлу
|
|
82
|
-
* @param {number} lineIdx рядок декларації
|
|
83
|
-
* @returns {string} склеєний опис або ''
|
|
84
|
-
*/
|
|
85
|
-
function docBefore(lines, lineIdx) {
|
|
86
|
-
const doc = []
|
|
87
|
-
for (let i = lineIdx - 1; i >= 0; i--) {
|
|
88
|
-
const t = lines[i].trim()
|
|
89
|
-
if (t.startsWith('///')) {
|
|
90
|
-
doc.unshift(t.slice(3).trim())
|
|
91
|
-
} else if (t.startsWith('#[') || t.startsWith('#![') || t === '') {
|
|
92
|
-
// пропустити атрибути та пусті рядки
|
|
93
|
-
} else {
|
|
94
|
-
break
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return doc.join(' ').trim()
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Pub-items матчаться у два кроки по trim-нутому рядку (прості регекспи без
|
|
101
|
-
// бектрекінгу): спершу опційний pub(...)-префікс, потім сама декларація.
|
|
102
|
-
// Також ловить fn без pub (для localSymbols і impl-методів)
|
|
103
|
-
const PUB_PREFIX_RE = /^pub(?:\([^)]*\))?\s+/
|
|
104
|
-
const ITEM_DECL_RE = /^(?:async\s+)?(?:unsafe\s+)?(fn|struct|enum|trait|type)\s+(\w+)/
|
|
105
|
-
|
|
106
|
-
// impl Type { або impl<T> Trait for Type { — теж двокроково: голова `impl<...>`,
|
|
107
|
-
// далі тип після `for` (trait-impl) або перше слово (inherent impl)
|
|
108
|
-
const IMPL_HEAD_RE = /^impl(?:<[^>]*>)?\s+/
|
|
109
|
-
const IMPL_FOR_TYPE_RE = /\bfor\s+(\w+)/
|
|
110
|
-
const TYPE_NAME_RE = /^(\w+)/
|
|
111
|
-
|
|
112
|
-
// Підозрілі exposure-атрибути, що роблять непуб-fn фактично публічними
|
|
113
|
-
const EXPOSURE_ATTR_RE = /#\[(?:tauri::command|wasm_bindgen|uniffi::export|pyo3::pyfunction|napi)/
|
|
114
|
-
|
|
115
|
-
// Базовий виклик fn-імені (для call-graph всередині юніта)
|
|
116
|
-
const CALL_RE = /\b([a-z_]\w*)\s*\(/g
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Рахує дельту глибини `{}` в рядку, пропускаючи `//`-коментарі й рядкові літерали.
|
|
120
|
-
* @param {string} line рядок коду
|
|
121
|
-
* @returns {number} приріст глибини (додатний — відкрито більше, ніж закрито)
|
|
122
|
-
*/
|
|
123
|
-
function braceDeltaInLine(line) {
|
|
124
|
-
let delta = 0
|
|
125
|
-
let j = 0
|
|
126
|
-
while (j < line.length) {
|
|
127
|
-
const ch = line[j]
|
|
128
|
-
if (ch === '/' && line[j + 1] === '/') break
|
|
129
|
-
if (ch === '"') {
|
|
130
|
-
j++
|
|
131
|
-
while (j < line.length && line[j] !== '"') {
|
|
132
|
-
if (line[j] === '\\') j++
|
|
133
|
-
j++
|
|
134
|
-
}
|
|
135
|
-
j++
|
|
136
|
-
continue
|
|
137
|
-
}
|
|
138
|
-
if (ch === '{') delta++
|
|
139
|
-
else if (ch === '}') delta--
|
|
140
|
-
j++
|
|
141
|
-
}
|
|
142
|
-
return delta
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Оновлює impl-стек за поточним рядком: прибирає закриті impl і, якщо рядок — impl-
|
|
147
|
-
* декларація на глибині ≤1, додає новий запис.
|
|
148
|
-
* @param {Array<{typeName:string, openDepth:number}>} implStack стек відкритих impl (мутується)
|
|
149
|
-
* @param {string} line сирий рядок
|
|
150
|
-
* @param {string} trimmed рядок без лідируючих пробілів
|
|
151
|
-
* @param {number} depth глибина ПІСЛЯ обробки рядка
|
|
152
|
-
* @param {number} depthAtStart глибина ДО обробки рядка
|
|
153
|
-
* @returns {void}
|
|
154
|
-
*/
|
|
155
|
-
function updateImplStack(implStack, line, trimmed, depth, depthAtStart) {
|
|
156
|
-
while (implStack.length > 0 && implStack.at(-1).openDepth > depth) {
|
|
157
|
-
implStack.pop()
|
|
158
|
-
}
|
|
159
|
-
if (depthAtStart <= 1) {
|
|
160
|
-
const headM = trimmed.match(IMPL_HEAD_RE)
|
|
161
|
-
if (headM && line.includes('{')) {
|
|
162
|
-
const rest = trimmed.slice(headM[0].length)
|
|
163
|
-
const typeM = rest.match(IMPL_FOR_TYPE_RE) ?? rest.match(TYPE_NAME_RE)
|
|
164
|
-
if (typeM) implStack.push({ typeName: typeM[1], openDepth: depth })
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Витягує тіло item-а (fn/struct/enum/trait) через `findClosingBrace`.
|
|
171
|
-
* @param {string} src вміст файлу
|
|
172
|
-
* @param {string[]} lines рядки файлу
|
|
173
|
-
* @param {number} li індекс рядка декларації
|
|
174
|
-
* @param {number} lineOffset зсув початку рядка в `src`
|
|
175
|
-
* @param {string} kind вид item-а
|
|
176
|
-
* @returns {{body:string, itemEnd:number}} тіло та зсув кінця item-а
|
|
177
|
-
*/
|
|
178
|
-
function extractItemBody(src, lines, li, lineOffset, kind) {
|
|
179
|
-
let body = ''
|
|
180
|
-
let itemEnd = lineOffset + lines[li].length
|
|
181
|
-
if (kind !== 'type') {
|
|
182
|
-
const openBraceIdx = src.indexOf('{', lineOffset)
|
|
183
|
-
// Шукаємо `{` не далі ніж через 3 рядки від початку декларації
|
|
184
|
-
const threeLines = lines.slice(li, li + 3).join('\n').length
|
|
185
|
-
if (openBraceIdx !== -1 && openBraceIdx - lineOffset <= threeLines) {
|
|
186
|
-
const closeIdx = findClosingBrace(src, openBraceIdx)
|
|
187
|
-
if (closeIdx !== -1) {
|
|
188
|
-
itemEnd = closeIdx + 1
|
|
189
|
-
body = src.slice(lineOffset, itemEnd)
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return { body, itemEnd }
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Заповнює `calls` кожного юніта викликами інших юнітів цього ж файлу.
|
|
198
|
-
* @param {Array<{name:string, body:string, calls:string[]}>} units юніти файлу (мутуються)
|
|
199
|
-
* @returns {void}
|
|
200
|
-
*/
|
|
201
|
-
function fillCallGraph(units) {
|
|
202
|
-
const unitNames = new Set(units.map(u => u.name))
|
|
203
|
-
for (const u of units) {
|
|
204
|
-
if (!u.body) continue
|
|
205
|
-
const calls = new Set()
|
|
206
|
-
let cm
|
|
207
|
-
const re = new RegExp(CALL_RE.source, 'g')
|
|
208
|
-
while ((cm = re.exec(u.body)) !== null) {
|
|
209
|
-
if (unitNames.has(cm[1]) && cm[1] !== u.name) calls.add(cm[1])
|
|
210
|
-
}
|
|
211
|
-
u.calls = [...calls]
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Обробляє рядок на глибині ≤1: якщо це декларація item-а — додає юніт у `units`;
|
|
217
|
-
* інакше скидає exposure-флаг на не-атрибутних рядках. Повертає новий стан флага.
|
|
218
|
-
* @param {object} p параметри
|
|
219
|
-
* @param {string} p.src вміст файлу
|
|
220
|
-
* @param {string[]} p.lines рядки файлу
|
|
221
|
-
* @param {number} p.li індекс рядка
|
|
222
|
-
* @param {number} p.lineOffset зсув початку рядка в `src`
|
|
223
|
-
* @param {number} p.depthAtStart глибина ДО обробки рядка
|
|
224
|
-
* @param {string|null} p.currentImpl тип поточного impl або `null`
|
|
225
|
-
* @param {boolean} p.nextFnExposed чи наступний fn exposure-exposed
|
|
226
|
-
* @param {Array<object>} p.units акумулятор юнітів (мутується)
|
|
227
|
-
* @returns {boolean} новий стан `nextFnExposed`
|
|
228
|
-
*/
|
|
229
|
-
function processItemLine({ src, lines, li, lineOffset, depthAtStart, currentImpl, nextFnExposed, units }) {
|
|
230
|
-
const line = lines[li]
|
|
231
|
-
const trimmed = line.trimStart()
|
|
232
|
-
const pubM = trimmed.match(PUB_PREFIX_RE)
|
|
233
|
-
const m = (pubM ? trimmed.slice(pubM[0].length) : trimmed).match(ITEM_DECL_RE)
|
|
234
|
-
if (!m) {
|
|
235
|
-
// Рядок не є item — скидаємо exposure-флаг якщо не атрибут
|
|
236
|
-
const t = line.trim()
|
|
237
|
-
if (!t.startsWith('#[') && !t.startsWith('#![') && !t.startsWith('///') && t !== '') return false
|
|
238
|
-
return nextFnExposed
|
|
239
|
-
}
|
|
240
|
-
const kind = m[1]
|
|
241
|
-
const isPub = Boolean(pubM) || (kind === 'fn' && nextFnExposed)
|
|
242
|
-
const doc = docBefore(lines, li)
|
|
243
|
-
// Витягуємо тіло через findClosingBrace для fn/struct/enum/trait
|
|
244
|
-
const { body, itemEnd } = extractItemBody(src, lines, li, lineOffset, kind)
|
|
245
|
-
units.push({
|
|
246
|
-
name: m[2],
|
|
247
|
-
kind,
|
|
248
|
-
exported: isPub,
|
|
249
|
-
implName: depthAtStart === 1 ? currentImpl : null,
|
|
250
|
-
span: { start: lineOffset, end: itemEnd },
|
|
251
|
-
body,
|
|
252
|
-
calls: [],
|
|
253
|
-
doc
|
|
254
|
-
})
|
|
255
|
-
return kind === 'fn' ? false : nextFnExposed
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Юніт-екстрактор для `.rs` файлів.
|
|
260
|
-
* Визначає top-level і impl-методи через підрахунок дужок по рядках.
|
|
261
|
-
* Відомі обмеження: рядкові літерали з `{`/`}` всередині `{}` можуть дати
|
|
262
|
-
* хибну глибину (рідкісно в реальному Rust-коді з rustfmt).
|
|
263
|
-
* @param {string} src вміст файлу
|
|
264
|
-
* @param {string} [_relPath] резервний (не використовується)
|
|
265
|
-
* @returns {Array<{name:string, kind:string, exported:boolean, implName:string|null, span:{start:number,end:number}, body:string, calls:string[], doc:string}>|null} юніти файлу (fn та impl-методи) або `null`, якщо юнітів не знайдено
|
|
266
|
-
*/
|
|
267
|
-
export function extractUnitsRs(src, _relPath) {
|
|
268
|
-
const lines = src.split('\n')
|
|
269
|
-
const units = []
|
|
270
|
-
let depth = 0
|
|
271
|
-
let lineOffset = 0
|
|
272
|
-
// Стек відкритих impl: { typeName, openDepth }
|
|
273
|
-
const implStack = []
|
|
274
|
-
// Флаг: наступний fn отримує exposure (через #[tauri::command] тощо)
|
|
275
|
-
let nextFnExposed = false
|
|
276
|
-
|
|
277
|
-
for (let li = 0; li < lines.length; li++) {
|
|
278
|
-
const line = lines[li]
|
|
279
|
-
const depthAtStart = depth
|
|
280
|
-
depth += braceDeltaInLine(line)
|
|
281
|
-
|
|
282
|
-
updateImplStack(implStack, line, line.trimStart(), depth, depthAtStart)
|
|
283
|
-
const currentImpl = implStack.at(-1)?.typeName ?? null
|
|
284
|
-
|
|
285
|
-
// Перевіряємо exposure-атрибути
|
|
286
|
-
if (EXPOSURE_ATTR_RE.test(line)) {
|
|
287
|
-
nextFnExposed = true
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Елементи на глибині 0 (top-level) і 1 (всередині impl)
|
|
291
|
-
if (depthAtStart <= 1) {
|
|
292
|
-
nextFnExposed = processItemLine({
|
|
293
|
-
src,
|
|
294
|
-
lines,
|
|
295
|
-
li,
|
|
296
|
-
lineOffset,
|
|
297
|
-
depthAtStart,
|
|
298
|
-
currentImpl,
|
|
299
|
-
nextFnExposed,
|
|
300
|
-
units
|
|
301
|
-
})
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
lineOffset += line.length + 1 // +1 для '\n'
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Базовий call-graph: виклики інших юнітів цього файлу
|
|
308
|
-
fillCallGraph(units)
|
|
309
|
-
|
|
310
|
-
return units.length > 0 ? units : null
|
|
311
|
-
}
|