@nitra/cursor 1.8.108 → 1.8.110
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/mdc/abie.mdc +3 -3
- package/mdc/bun.mdc +8 -1
- package/mdc/js-lint.mdc +4 -1
- package/package.json +1 -1
- package/scripts/check-abie.mjs +6 -6
- package/scripts/check-bun.mjs +30 -2
- package/scripts/check-js-lint.mjs +59 -13
- package/scripts/check-k8s.mjs +2 -2
- package/scripts/utils/bunyan-imports.mjs +8 -7
package/mdc/abie.mdc
CHANGED
|
@@ -4,7 +4,7 @@ alwaysApply: true
|
|
|
4
4
|
version: '1.15'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Правило **abie** для споживачів **@nitra/cursor**: **k8s** (Deployment + **HealthCheckPolicy** у **`hc.yaml`**, overlay **ua** / **ru** — **nodeSelector**, **HTTPRoute** (будь-який непорожній **`target.name`**, для спільних сервісів **`auth-run-hl`** / **`
|
|
7
|
+
Правило **abie** для споживачів **@nitra/cursor**: **k8s** (Deployment + **HealthCheckPolicy** у **`hc.yaml`**, overlay **ua** / **ru** — **nodeSelector**, **HTTPRoute** (будь-який непорожній **`target.name`**, для спільних сервісів **`auth-run-hl`** / **`file-link-hl`** — **`namespace: dev`** у base та patch **`…/backendRefs/…/namespace`** у **ua** / **ru**), у overlay **ru** — кожен **Service** (у т. ч. **headless** / **`-hl`**) → **`spec.type: NodePort`** через **JSON6902** у **`kustomization.yaml`**, видалення **HealthCheckPolicy** у **ru**), гілки **dev**, **ua**, **ru** у **clean-merged-branch**, а також заборона артефактів **Firebase Hosting** у корені репозиторію.
|
|
8
8
|
|
|
9
9
|
**`npx @nitra/cursor check abie`** виконується лише якщо в **`.n-cursor.json`** у **`rules`** є **`abie`** — інакше вихід **0** без зауважень.
|
|
10
10
|
|
|
@@ -38,7 +38,7 @@ spec:
|
|
|
38
38
|
|
|
39
39
|
За наявності **Deployment** під **k8s** і наявності **Vite** (**`vite.config.js`**, **`vite.config.mjs`** або **`vite.config.ts`** у каталозі пакета) у **`ua/kustomization.yaml`** та **`ru/kustomization.yaml`** цього пакета потрібні **inline JSON6902** у **`patches`**: **target** **`kind: HTTPRoute`**, **непорожній `name`** (як у маніфесті маршруту). Мають бути зміни **`/spec/hostnames`** (домени abie — у скрипті) та **`/spec/parentRefs/0/namespace`** (**`ua`** / **`ru`**). Для **ru** — анотація **`gwin.yandex.cloud/rules.http.upgradeTypes: websocket`** лише якщо в **тому ж** **`ru/kustomization.yaml`** є згадка **`HASURA_GRAPHQL_JWT_SECRET`** (типово patch на **ConfigMap** Hasura). Як обирати **`op`** (**add** / **replace** тощо) у patch — **k8s.mdc** (розділ про JSON patch у kustomization).
|
|
40
40
|
|
|
41
|
-
### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`
|
|
41
|
+
### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`file-link-hl`**
|
|
42
42
|
|
|
43
43
|
Ці **Service** (headless **`-hl`**) живуть у **базовому** неймспейсі **`dev`**. У маніфесті **HTTPRoute** під **`k8s`** (шар без **`ua/`** та **`ru/`** — наприклад **`…/k8s/base/hr.yaml`**) для кожного **`backendRefs`** до такого сервісу явно вкажи **`namespace: dev`** і порт **8080**:
|
|
44
44
|
|
|
@@ -53,7 +53,7 @@ spec:
|
|
|
53
53
|
- name: auth-run-hl
|
|
54
54
|
namespace: dev
|
|
55
55
|
port: 8080
|
|
56
|
-
- name:
|
|
56
|
+
- name: file-link-hl
|
|
57
57
|
namespace: dev
|
|
58
58
|
port: 8080
|
|
59
59
|
```
|
package/mdc/bun.mdc
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Bun як єдиний package manager у монорепо
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.7'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
Проект використовує тільки Bun для керування залежностями та запуску скриптів.
|
|
@@ -44,6 +44,13 @@ Lockfile у репозиторії: `bun.lock`.
|
|
|
44
44
|
Не створювати `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`.
|
|
45
45
|
Видалити якщо вони є. Видалити .yarn та .yarnrc.yml якщо вони є.
|
|
46
46
|
|
|
47
|
+
У корені репозиторію має бути **`bunfig.toml`** з **hoisted** лінкером (пласке `node_modules`, сумісне з інструментами, які не розуміють ізольований layout Bun):
|
|
48
|
+
|
|
49
|
+
```toml title="bunfig.toml"
|
|
50
|
+
[install]
|
|
51
|
+
linker = "hoisted"
|
|
52
|
+
```
|
|
53
|
+
|
|
47
54
|
Для Bun monorepo:
|
|
48
55
|
|
|
49
56
|
- Встановлювати залежності у відповідному пакеті, а не в корені без потреби.
|
package/mdc/js-lint.mdc
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Перевірка JavaScript коду
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.14'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
**oxlint**, **ESLint**, **jscpd**. У скрипті **`lint-js`** і в CI — **`bunx oxlint`**, **`bunx eslint`**, **`bunx jscpd`** (у CI без **`--fix`** для oxlint/eslint — див. приклад workflow нижче). Без **prettier** і **@nitra/prettier-config**. У **`devDependencies`** має бути **`@nitra/eslint-config` мінімум `^3.5.0`** (з ним транзитивно йде **`@e18e/eslint-plugin`** для oxlint); пакет **`@e18e/eslint-plugin`** окремо не додавай. Пакети oxlint/eslint/jscpd не додавай без потреби монорепо.
|
|
@@ -16,8 +16,11 @@ version: '1.13'
|
|
|
16
16
|
}
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
У кожному **`package.json`** проєкту (корінь і всі workspace-пакети) має бути **`"type": "module"`** — весь код у ESM.
|
|
20
|
+
|
|
19
21
|
```json title="package.json"
|
|
20
22
|
{
|
|
23
|
+
"type": "module",
|
|
21
24
|
"scripts": {
|
|
22
25
|
"lint-js": "bunx oxlint --fix && bunx eslint --fix . && bunx jscpd ."
|
|
23
26
|
},
|
package/package.json
CHANGED
package/scripts/check-abie.mjs
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* — тоді в **`ua`/`ru` kustomization** потрібен patch на **`kind: HTTPRoute`**, **непорожній `target.name`**: **`/spec/hostnames`**
|
|
30
30
|
* (домени abie.mdc), **`/spec/parentRefs/0/namespace`** (**ua** / **ru**); для **ru** — **`gwin.yandex.cloud/rules.http.upgradeTypes: websocket`**,
|
|
31
31
|
* якщо в тому ж **`kustomization.yaml`** згадується **`HASURA_GRAPHQL_JWT_SECRET`** (Hasura + JWT).
|
|
32
|
-
* **Спільні бекенди (`auth-run-hl`, `
|
|
32
|
+
* **Спільні бекенди (`auth-run-hl`, `file-link-hl`):** у **HTTPRoute** під **`k8s`** поза overlay **ua** та **ru** (шлях не містить **`k8s/ua/`** чи **`k8s/ru/`**) кожен такий **`backendRefs`** має **`namespace: dev`** і порт **8080**;
|
|
33
33
|
* у patch overlay **ua** та **ru** — по одному **JSON6902** на **`/spec/rules/…/backendRefs/…/namespace`** з **`value`**: **ua** або **ru** (кількість patch-ів = кількість таких **`backendRefs`** у пакеті).
|
|
34
34
|
* Вибір **`op`** — **k8s.mdc**.
|
|
35
35
|
*
|
|
@@ -56,7 +56,7 @@ const HASURA_JWT_SECRET_IN_KUSTOMIZATION = 'HASURA_GRAPHQL_JWT_SECRET'
|
|
|
56
56
|
* Спільні **Service** (**`-hl`**) у **dev**: у base-**HTTPRoute** обов'язково **`namespace: dev`**, у overlay — patch **`…/backendRefs/…/namespace`** (abie.mdc).
|
|
57
57
|
* Експорт для споживачів / тестів.
|
|
58
58
|
*/
|
|
59
|
-
export const ABIE_SHARED_CROSS_NS_BACKEND_NAMES = Object.freeze(['auth-run-hl', '
|
|
59
|
+
export const ABIE_SHARED_CROSS_NS_BACKEND_NAMES = Object.freeze(['auth-run-hl', 'file-link-hl'])
|
|
60
60
|
|
|
61
61
|
const ABIE_SHARED_CROSS_NS_BACKEND_SET = new Set(ABIE_SHARED_CROSS_NS_BACKEND_NAMES)
|
|
62
62
|
|
|
@@ -985,7 +985,7 @@ function checkSharedBackendRef(br, rel, errors) {
|
|
|
985
985
|
}
|
|
986
986
|
|
|
987
987
|
/**
|
|
988
|
-
* З HTTPRoute-документа рахує **`backendRefs`** до **`auth-run-hl`** / **`
|
|
988
|
+
* З HTTPRoute-документа рахує **`backendRefs`** до **`auth-run-hl`** / **`file-link-hl`** і порушення **`namespace: dev`**.
|
|
989
989
|
* @param {unknown} obj корінь YAML
|
|
990
990
|
* @param {string} rel відносний шлях (повідомлення)
|
|
991
991
|
* @returns {{ refCount: number, errors: string[] }} кількість посилань і список порушень
|
|
@@ -1015,7 +1015,7 @@ function httpRouteDocSharedCrossNsBackendStats(obj, rel) {
|
|
|
1015
1015
|
}
|
|
1016
1016
|
|
|
1017
1017
|
/**
|
|
1018
|
-
* З YAML під **k8s** пакета (без overlay **ua** та **ru**) збирає кількість **`backendRefs`** до **`auth-run-hl`** і **`
|
|
1018
|
+
* З YAML під **k8s** пакета (без overlay **ua** та **ru**) збирає кількість **`backendRefs`** до **`auth-run-hl`** і **`file-link-hl`** і порушення **`namespace: dev`**.
|
|
1019
1019
|
* @param {string} root корінь репозиторію
|
|
1020
1020
|
* @param {string} pkgAbs абсолютний шлях до каталогу пакета
|
|
1021
1021
|
* @param {string[]} yamlFilesAbs усі **yaml** під **k8s** (як **findK8sYamlFiles**)
|
|
@@ -1135,7 +1135,7 @@ export function getCombinedNginxRunPatchTextFromKustomization(raw) {
|
|
|
1135
1135
|
* @param {string} combined текст одного або кількох inline **patch**, розділених символом нового рядка
|
|
1136
1136
|
* @param {'ua' | 'ru'} mode **ua** або **ru**
|
|
1137
1137
|
* @param {string} [fullKustomizationRaw] повний текст **kustomization.yaml** — для **ru** визначає, чи потрібна анотація **gwin…websocket** (лише якщо є **`HASURA_GRAPHQL_JWT_SECRET`**)
|
|
1138
|
-
* @param {number} [sharedCrossNsBackendRefCount] скільки **`backendRefs`** до **`auth-run-hl`** і **`
|
|
1138
|
+
* @param {number} [sharedCrossNsBackendRefCount] скільки **`backendRefs`** до **`auth-run-hl`** і **`file-link-hl`** у base **HTTPRoute** пакета — стільки ж patch-ів **`…/backendRefs/…/namespace`** з **`value`** overlay
|
|
1139
1139
|
* @returns {string | null} повідомлення про помилку або **null**
|
|
1140
1140
|
*/
|
|
1141
1141
|
export function validateAbieNginxRunHttpRoutePatches(
|
|
@@ -1173,7 +1173,7 @@ export function validateAbieNginxRunHttpRoutePatches(
|
|
|
1173
1173
|
if (sharedCount > 0) {
|
|
1174
1174
|
const patchHits = countAbieHttpRouteBackendRefNamespacePatchesInCombined(combined, mode)
|
|
1175
1175
|
if (patchHits < sharedCount) {
|
|
1176
|
-
return `HTTPRoute: для backendRefs до спільних сервісів auth-run-hl,
|
|
1176
|
+
return `HTTPRoute: для backendRefs до спільних сервісів auth-run-hl, file-link-hl очікується ${sharedCount} JSON6902 patch(ів) з path /spec/rules/…/backendRefs/…/namespace та value ${mode} (зараз ${patchHits}) — abie.mdc`
|
|
1177
1177
|
}
|
|
1178
1178
|
}
|
|
1179
1179
|
return null
|
package/scripts/check-bun.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Перевіряє відповідність репозиторію правилам Bun (bun.mdc).
|
|
3
3
|
*
|
|
4
|
-
* Очікує наявність `bun.lock`,
|
|
5
|
-
* і поле `packageManager`
|
|
4
|
+
* Очікує наявність `bun.lock`, `bunfig.toml` з `linker = "hoisted"` у секції `[install]`,
|
|
5
|
+
* забороняє lockfile та артефакти yarn/pnpm, директорію `.yarn` і поле `packageManager`
|
|
6
|
+
* у кореневому `package.json`.
|
|
6
7
|
*
|
|
7
8
|
* У кореневому `package.json` не має бути поля **`dependencies`**; у **`devDependencies`** дозволені лише
|
|
8
9
|
* пакети **`@nitra/*`** (наприклад **`@nitra/cspell-dict`**, **`@nitra/eslint-config`**).
|
|
@@ -20,6 +21,31 @@ import { readFile } from 'node:fs/promises'
|
|
|
20
21
|
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
21
22
|
|
|
22
23
|
const OXFMT_END_RE = /&&[ \t]+oxfmt[ \t]+\.[ \t]*$/
|
|
24
|
+
/** Пробіли/таби без `\s` (уникаємо super-linear backtracking у sonarjs/slow-regex). */
|
|
25
|
+
const HOISTED_LINKER_RE = /^[ \t]*linker[ \t]*=[ \t]*"hoisted"[ \t]*$/m
|
|
26
|
+
const INSTALL_SECTION_RE = /^[ \t]*\[install\][ \t]*$/m
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Перевіряє `bunfig.toml` на секцію `[install]` з `linker = "hoisted"`.
|
|
30
|
+
* @param {{ pass: (msg: string) => void, fail: (msg: string) => void }} reporter репортер
|
|
31
|
+
*/
|
|
32
|
+
async function checkBunfigHoisted(reporter) {
|
|
33
|
+
const { pass, fail } = reporter
|
|
34
|
+
if (!existsSync('bunfig.toml')) {
|
|
35
|
+
fail('Відсутній bunfig.toml — створи з [install] linker = "hoisted" (bun.mdc)')
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
const content = await readFile('bunfig.toml', 'utf8')
|
|
39
|
+
if (!INSTALL_SECTION_RE.test(content)) {
|
|
40
|
+
fail('bunfig.toml: відсутня секція [install] (bun.mdc)')
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
if (HOISTED_LINKER_RE.test(content)) {
|
|
44
|
+
pass('bunfig.toml: [install] linker = "hoisted"')
|
|
45
|
+
} else {
|
|
46
|
+
fail('bunfig.toml: у секції [install] має бути linker = "hoisted" (bun.mdc)')
|
|
47
|
+
}
|
|
48
|
+
}
|
|
23
49
|
|
|
24
50
|
/**
|
|
25
51
|
* Чи ім'я пакета дозволене в кореневих `devDependencies` за bun.mdc (лише **`@nitra/*`**).
|
|
@@ -162,6 +188,8 @@ export async function check() {
|
|
|
162
188
|
fail('Відсутній bun.lock — запусти bun i')
|
|
163
189
|
}
|
|
164
190
|
|
|
191
|
+
await checkBunfigHoisted(reporter)
|
|
192
|
+
|
|
165
193
|
const cursorRules = await loadNCursorRules()
|
|
166
194
|
|
|
167
195
|
if (!existsSync('package.json')) {
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* `.oxlintrc.json` з `jsPlugins` (`@e18e/eslint-plugin`) і правилом `e18e/prefer-includes: error`,
|
|
6
6
|
* `@nitra/eslint-config` у devDependencies мінімум **3.5.0** (транзитивний `@e18e/eslint-plugin` для oxlint), `.jscpd.json`
|
|
7
7
|
* (gitignore, exitCode, reporters, minLines), workflow `lint-js.yml` (checkout@v6, setup-bun-deps,
|
|
8
|
-
* bunx без --fix), без prettier, `engines.node` >= 24
|
|
8
|
+
* bunx без --fix), без prettier, `engines.node` >= 24, `"type": "module"` у кореневому
|
|
9
|
+
* і всіх workspace `package.json`. Дубль перевірки JS у `lint.yml` — заборонено.
|
|
9
10
|
*/
|
|
10
11
|
import { existsSync } from 'node:fs'
|
|
11
12
|
import { readFile } from 'node:fs/promises'
|
|
@@ -167,6 +168,57 @@ function checkPackageJsonLintDeps(pkg, passFn, failFn) {
|
|
|
167
168
|
}
|
|
168
169
|
}
|
|
169
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Перевіряє, що package.json має `"type": "module"`.
|
|
173
|
+
* @param {string} label шлях або назва пакета для повідомлень
|
|
174
|
+
* @param {{ type?: string }} pkg parsed package.json
|
|
175
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
176
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
177
|
+
*/
|
|
178
|
+
function checkPackageJsonTypeModule(label, pkg, passFn, failFn) {
|
|
179
|
+
if (pkg.type === 'module') {
|
|
180
|
+
passFn(`${label}: "type": "module"`)
|
|
181
|
+
} else {
|
|
182
|
+
failFn(`${label}: має містити "type": "module" (js-lint.mdc)`)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* `"type": "module"` у кожного workspace з package.json.
|
|
188
|
+
* @param {unknown[]} workspaces поле workspaces з package.json
|
|
189
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
190
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
191
|
+
*/
|
|
192
|
+
async function checkWorkspacePackagesTypeModule(workspaces, passFn, failFn) {
|
|
193
|
+
for (const ws of workspaces) {
|
|
194
|
+
const wsPkgPath = `${ws}/package.json`
|
|
195
|
+
if (existsSync(wsPkgPath)) {
|
|
196
|
+
const wsPkg = JSON.parse(await readFile(wsPkgPath, 'utf8'))
|
|
197
|
+
checkPackageJsonTypeModule(wsPkgPath, wsPkg, passFn, failFn)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* engines.node >= 24.
|
|
204
|
+
* @param {{ engines?: { node?: string } }} pkg розпарсений package.json
|
|
205
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
206
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
207
|
+
*/
|
|
208
|
+
function checkEnginesNode(pkg, passFn, failFn) {
|
|
209
|
+
const nodeEngine = pkg.engines?.node
|
|
210
|
+
if (nodeEngine) {
|
|
211
|
+
const firstNumeric = String(nodeEngine).split(NON_DIGITS_RE).find(Boolean)
|
|
212
|
+
if (firstNumeric && Number(firstNumeric) >= 24) {
|
|
213
|
+
passFn(`engines.node: "${nodeEngine}"`)
|
|
214
|
+
} else {
|
|
215
|
+
failFn(`engines.node: "${nodeEngine}" — має бути >=24`)
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
failFn('package.json не містить engines.node — додай: "engines": { "node": ">=24" }')
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
170
222
|
/**
|
|
171
223
|
* Перевіряє package.json на lint-js, prettier, eslint-config, engines.node.
|
|
172
224
|
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
@@ -176,6 +228,11 @@ async function checkPackageJsonJsLint(passFn, failFn) {
|
|
|
176
228
|
if (!existsSync('package.json')) return
|
|
177
229
|
const pkg = JSON.parse(await readFile('package.json', 'utf8'))
|
|
178
230
|
|
|
231
|
+
checkPackageJsonTypeModule('package.json', pkg, passFn, failFn)
|
|
232
|
+
|
|
233
|
+
const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : []
|
|
234
|
+
await checkWorkspacePackagesTypeModule(workspaces, passFn, failFn)
|
|
235
|
+
|
|
179
236
|
const lintJs = pkg.scripts?.['lint-js']
|
|
180
237
|
if (lintJs) {
|
|
181
238
|
passFn('package.json містить скрипт lint-js')
|
|
@@ -191,18 +248,7 @@ async function checkPackageJsonJsLint(passFn, failFn) {
|
|
|
191
248
|
}
|
|
192
249
|
|
|
193
250
|
checkPackageJsonLintDeps(pkg, passFn, failFn)
|
|
194
|
-
|
|
195
|
-
const nodeEngine = pkg.engines?.node
|
|
196
|
-
if (nodeEngine) {
|
|
197
|
-
const firstNumeric = String(nodeEngine).split(NON_DIGITS_RE).find(Boolean)
|
|
198
|
-
if (firstNumeric && Number(firstNumeric) >= 24) {
|
|
199
|
-
passFn(`engines.node: "${nodeEngine}"`)
|
|
200
|
-
} else {
|
|
201
|
-
failFn(`engines.node: "${nodeEngine}" — має бути >=24`)
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
failFn('package.json не містить engines.node — додай: "engines": { "node": ">=24" }')
|
|
205
|
-
}
|
|
251
|
+
checkEnginesNode(pkg, passFn, failFn)
|
|
206
252
|
}
|
|
207
253
|
|
|
208
254
|
/**
|
package/scripts/check-k8s.mjs
CHANGED
|
@@ -2336,8 +2336,8 @@ function hasuraRuleIsWebsocket(rule, qlPath) {
|
|
|
2336
2336
|
* @returns {{ prefix: string, startIndex: number } | null} виявлений префікс і позиція правила 1 або null
|
|
2337
2337
|
*/
|
|
2338
2338
|
function findHasuraCanonStart(rules) {
|
|
2339
|
-
for (
|
|
2340
|
-
const r = asPlainRecord(
|
|
2339
|
+
for (const [i, rule] of rules.entries()) {
|
|
2340
|
+
const r = asPlainRecord(rule)
|
|
2341
2341
|
const matches = r === null ? null : r.matches
|
|
2342
2342
|
if (!Array.isArray(matches) || matches.length !== 1) {
|
|
2343
2343
|
// наступне правило
|
|
@@ -61,7 +61,7 @@ function normalizeSnippet(s) {
|
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Перевіряє, чи це виклик `require('<module>')` з рядковим аргументом.
|
|
64
|
-
* @param {
|
|
64
|
+
* @param {Record<string, unknown> | null | undefined} node вузол AST
|
|
65
65
|
* @returns {string | null} ім'я модуля з аргументу, інакше `null`
|
|
66
66
|
*/
|
|
67
67
|
function requireCallModule(node) {
|
|
@@ -75,7 +75,7 @@ function requireCallModule(node) {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Перевіряє, чи це динамічний `import('<module>')` з рядковим аргументом.
|
|
78
|
-
* @param {
|
|
78
|
+
* @param {Record<string, unknown> | null | undefined} node вузол AST
|
|
79
79
|
* @returns {string | null} ім'я модуля, інакше `null`
|
|
80
80
|
*/
|
|
81
81
|
function dynamicImportModule(node) {
|
|
@@ -87,8 +87,8 @@ function dynamicImportModule(node) {
|
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
89
|
* Простий рекурсивний обхід AST: заходимо в усі об'єкти/масиви, щоб знайти require/import-вузли.
|
|
90
|
-
* @param {
|
|
91
|
-
* @param {(n:
|
|
90
|
+
* @param {unknown} node корінь або під-вузол AST
|
|
91
|
+
* @param {(n: unknown) => void} visit виклик для кожного об'єкта-вузла
|
|
92
92
|
* @returns {void}
|
|
93
93
|
*/
|
|
94
94
|
function walkAst(node, visit) {
|
|
@@ -101,9 +101,10 @@ function walkAst(node, visit) {
|
|
|
101
101
|
visit(node)
|
|
102
102
|
}
|
|
103
103
|
for (const key of Object.keys(node)) {
|
|
104
|
-
if (key
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
if (key !== 'parent') {
|
|
105
|
+
const v = node[key]
|
|
106
|
+
if (v && typeof v === 'object') walkAst(v, visit)
|
|
107
|
+
}
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
|