@nitra/cursor 1.27.9 → 1.28.1
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 +25 -0
- package/package.json +1 -1
- package/rules/abie/js/applies.mjs +3 -2
- package/rules/abie/js/env_dns.mjs +4 -2
- package/rules/abie/js/firebase_hosting.mjs +3 -2
- package/rules/abie/js/hc_pairing.mjs +3 -2
- package/rules/abie/js/ua_http_route.mjs +4 -2
- package/rules/abie/js/ua_node_selector.mjs +4 -2
- package/rules/adr/js/hooks.mjs +36 -28
- package/rules/bun/js/layout.mjs +16 -11
- package/rules/capacitor/js/platforms.mjs +3 -2
- package/rules/changelog/js/consistency.mjs +85 -63
- package/rules/changelog/lib/package-manifest.mjs +5 -4
- package/rules/docker/js/lint.mjs +3 -2
- package/rules/ga/js/workflows.mjs +41 -32
- package/rules/graphql/js/tooling.mjs +15 -11
- package/rules/hasura/js/internal_urls.mjs +14 -10
- package/rules/image-avif/js/avif_generation.mjs +36 -23
- package/rules/image-compress/js/package_setup.mjs +18 -12
- package/rules/js-bun-db/js/safety.mjs +3 -2
- package/rules/js-lint/js/tooling.mjs +45 -32
- package/rules/js-run/js/runtime.mjs +21 -15
- package/rules/k8s/js/manifests.mjs +3 -2
- package/rules/nginx-default-tpl/js/template.mjs +7 -6
- package/rules/npm-module/js/package_structure.mjs +82 -57
- package/rules/rego/js/applies.mjs +4 -4
- package/rules/rust/js/applies.mjs +5 -3
- package/rules/security/js/sample_secret.mjs +2 -2
- package/rules/security/js/trufflehog.mjs +6 -4
- package/rules/style-lint/js/tooling.mjs +15 -8
- package/rules/test/coverage/coverage.mjs +1 -1
- package/rules/test/js/data/vitest_config/vitest.config.baseline.js +7 -0
- package/rules/test/js/location.mjs +3 -2
- package/rules/test/js/no-process-chdir.mjs +89 -0
- package/rules/test/js/no-relative-fs-path.mjs +259 -0
- package/rules/test/js/vitest-config-pool-forks.mjs +52 -0
- package/rules/test/test.mdc +21 -0
- package/rules/text/js/forbidden-prettier.mjs +4 -2
- package/rules/text/js/formatting.mjs +25 -16
- package/rules/vue/js/packages.mjs +33 -25
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.28.0] - 2026-05-27
|
|
8
|
+
|
|
9
|
+
### BREAKING
|
|
10
|
+
|
|
11
|
+
- **`scripts/utils/test-helpers.mjs#withTmpCwd` видалено**. Замість нього — `withTmpDir(async dir => …)`, що **НЕ** мутує `process.cwd()`, а передає абсолютний шлях `dir` у callback. Усі callers (43 тестових файли у пакеті) переписано: `await writeJson(join(dir, …), …)`, `await ensureDir(join(dir, …))`, `execFile('git', […], { cwd: dir })`, `await check(dir)`. `writeJson` і `ensureDir` тепер вимагають абсолютних шляхів (кидають помилку на relative). Споживачі пакета — мігруйте за конвенцією з `test.mdc` (секція "Заборона `process.chdir` у тестах").
|
|
12
|
+
- **Production-API: 24 `check()`/`applies()` JS concerns приймають `cwd = process.cwd()` параметром** у `rules/{abie,adr,bun,capacitor,changelog,docker,ga,graphql,hasura,image-avif,image-compress,js-bun-db,js-lint,js-run,k8s,nginx-default-tpl,npm-module,rego,rust,security,style-lint,test,text,vue}/js/*.mjs`. Default зберігає CLI-сумісність (runner викликає без аргументів — default `process.cwd()` спрацює).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **Race у `process.cwd()` між паралельними vitest workers** (root cause). У default `pool: 'threads'` усі workers ділять один процес. Паралельні `withTmpCwd` ламали один одному cwd: `git init`+`git commit` із фікстури `rules/changelog/.../check.test.mjs` (з `user.name=test`, `user.email=test@test`, `-m 'init'`) потрапляли в реальний робочий репозиторій, де відбувався vitest run, і знищували `npm/package.json` (зменшували до `{"name":"mono"}`) + `npm/CHANGELOG.md` (зрізали до плейсхолдера). Race множив rogue commits/branches (`feat/x`, `feat/docs`, `feat/sync`) щоразу при запуску `bun run coverage`/Stryker. Усунено повним переписуванням `withTmpCwd` → `withTmpDir` (без `chdir`), `cwd` параметром у production функціях і defense-in-depth `pool: 'forks'`.
|
|
17
|
+
- **`rules/test/coverage/coverage.mjs:192`** — dynamic import шлях `../../scripts/coverage-fix.mjs` резолвив у неіснуючий `npm/rules/scripts/coverage-fix.mjs`. Виправлено на `../../../scripts/coverage-fix.mjs` (реальний файл — у `npm/scripts/`). Усуває ERR_MODULE_NOT_FOUND у `n-cursor coverage --fix` і прибирає потребу у stub-стратегії, що створювала race-небезпечні fs-артефакти у production tree `rules/scripts/`.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **`rules/test/js/no-process-chdir.mjs`** — JS concern: сканує `**/*.test.{js,mjs}` і падає на `process.chdir(`. Token-based regex (`/process\.chdir\s*\(/u`) — не зачіпає згадки у JSDoc. 8 unit-тестів.
|
|
22
|
+
- **`rules/test/js/no-relative-fs-path.mjs`** — AST-сканер (`oxc-parser`) для `**/*.test.{js,mjs}`: знаходить виклики FS-функцій з `node:fs`/`node:fs/promises` (`writeFile`, `copyFile`, `mkdir`, `readFile`, `existsSync`, `rename`, `symlink`, `cp`, `*Sync`-варіанти + `writeJson`/`ensureDir`), де path-аргумент — string literal без префікса `/`, `\`, `file:`, `http(s):`, `data:`, чи Windows-disk-letter. Покриває обидва path-arg позиції у `copyFile`/`rename`/`symlink`/`link`/`cp`. Виловив би інцидент 1.28.0 (`tests/check-rule-fixtures.test.mjs`): `copyFile(src, 'default.conf.template')` / `copyFile(src, 'values-dev.ini')` зливали fixture у production tree `npm/`. 17 unit-тестів; на власному репо знайдено 0 порушень серед 106 test files.
|
|
23
|
+
- **`rules/test/js/vitest-config-pool-forks.mjs`** — JS concern: substring-перевірка `pool: 'forks'` у `vitest.config.js`. Defense-in-depth. 6 unit-тестів.
|
|
24
|
+
- **`rules/test/js/data/vitest_config/vitest.config.baseline.js`** — canonical baseline тепер містить `pool: 'forks'` з обґрунтуванням race-bug у docstring.
|
|
25
|
+
- **`rules/test/test.mdc` — секція "Заборона `process.chdir` у тестах"** із описом інциденту, контрактом `withTmpDir`, посиланнями на нові concern'и.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **`scripts/utils/test-helpers.mjs`** — `withTmpDir(fn)` без `chdir`; `writeJson(absPath, data)` і `ensureDir(absPath)` валідують `isAbsolute`. Docstring описує інцидент.
|
|
30
|
+
- **`vitest.config.js`** — `pool: 'forks'` як defense-in-depth з повним коментарем.
|
|
31
|
+
|
|
7
32
|
## [1.27.9] - 2026-05-27
|
|
8
33
|
|
|
9
34
|
### Added
|
package/package.json
CHANGED
|
@@ -8,10 +8,11 @@ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
|
8
8
|
import { isAbieRuleEnabled } from '../lib/enabled.mjs'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
+
* @param {string} [cwd] корінь репозиторію
|
|
11
12
|
* @returns {Promise<boolean>} `true` — правило застосовне; `false` — пропустити
|
|
12
13
|
*/
|
|
13
|
-
export function applies() {
|
|
14
|
-
return isAbieRuleEnabled(
|
|
14
|
+
export function applies(cwd = process.cwd()) {
|
|
15
|
+
return isAbieRuleEnabled(cwd)
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* - `ua.env` → `abie-ua.internal` + `ua-*` namespace
|
|
6
6
|
*
|
|
7
7
|
* Файл `.env` без імені (локальний для розробника) — виключено.
|
|
8
|
+
* @param {string} [cwd] корінь репозиторію
|
|
8
9
|
*/
|
|
9
10
|
import { readFile } from 'node:fs/promises'
|
|
10
11
|
import { basename, relative } from 'node:path'
|
|
@@ -16,11 +17,12 @@ import { abieEnvNameFromBasename, collectAbieEnvFiles, validateAbieEnvInternalUr
|
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* @returns {Promise<number>} результат
|
|
20
|
+
* @param {string} [cwd] корінь репозиторію
|
|
19
21
|
*/
|
|
20
|
-
export async function check() {
|
|
22
|
+
export async function check(cwd = process.cwd()) {
|
|
21
23
|
const reporter = createCheckReporter()
|
|
22
24
|
const { pass, fail } = reporter
|
|
23
|
-
const root =
|
|
25
|
+
const root = cwd
|
|
24
26
|
|
|
25
27
|
const ignorePaths = await loadCursorIgnorePaths(root)
|
|
26
28
|
const envFiles = await collectAbieEnvFiles(root, ignorePaths)
|
|
@@ -12,12 +12,13 @@ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
|
12
12
|
const SKIP_TOP_DIR_NAMES = new Set(['.git', 'node_modules'])
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
+
* @param {string} [cwd] корінь репозиторію
|
|
15
16
|
* @returns {Promise<number>} результат
|
|
16
17
|
*/
|
|
17
|
-
export async function check() {
|
|
18
|
+
export async function check(cwd = process.cwd()) {
|
|
18
19
|
const reporter = createCheckReporter()
|
|
19
20
|
const { pass, fail } = reporter
|
|
20
|
-
const root =
|
|
21
|
+
const root = cwd
|
|
21
22
|
|
|
22
23
|
let entries
|
|
23
24
|
try {
|
|
@@ -17,12 +17,13 @@ import { validateAbieHcModeline } from '../lib/hc-yaml.mjs'
|
|
|
17
17
|
import { collectDeploymentDirs, findK8sYamlFiles } from '../lib/k8s-tree.mjs'
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
+
* @param {string} [cwd] корінь репозиторію
|
|
20
21
|
* @returns {Promise<number>} результат
|
|
21
22
|
*/
|
|
22
|
-
export async function check() {
|
|
23
|
+
export async function check(cwd = process.cwd()) {
|
|
23
24
|
const reporter = createCheckReporter()
|
|
24
25
|
const { pass, fail } = reporter
|
|
25
|
-
const root =
|
|
26
|
+
const root = cwd
|
|
26
27
|
|
|
27
28
|
const ignorePaths = await loadCursorIgnorePaths(root)
|
|
28
29
|
const yamls = await findK8sYamlFiles(root, ignorePaths)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Для спільних сервісів (`auth-run-hl`, `file-link-hl`) у base-HTTPRoute пакета — кожен `backendRef`
|
|
7
7
|
* має `namespace: dev`; в overlay patch — JSON6902 на `/spec/rules/…/backendRefs/…/namespace` зі
|
|
8
8
|
* `value: ua`. Кількість patch-ів = кількість таких посилань у base.
|
|
9
|
+
* @param {string} [cwd] корінь репозиторію
|
|
9
10
|
*/
|
|
10
11
|
import { readFile } from 'node:fs/promises'
|
|
11
12
|
import { relative } from 'node:path'
|
|
@@ -27,11 +28,12 @@ import {
|
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* @returns {Promise<number>} результат
|
|
31
|
+
* @param {string} [cwd] корінь репозиторію
|
|
30
32
|
*/
|
|
31
|
-
export async function check() {
|
|
33
|
+
export async function check(cwd = process.cwd()) {
|
|
32
34
|
const reporter = createCheckReporter()
|
|
33
35
|
const { pass, fail } = reporter
|
|
34
|
-
const root =
|
|
36
|
+
const root = cwd
|
|
35
37
|
|
|
36
38
|
const ignorePaths = await loadCursorIgnorePaths(root)
|
|
37
39
|
const yamls = await findK8sYamlFiles(root, ignorePaths)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Структурні обмеження JSON6902 (заборона `remove + add` на той самий path) перевіряє k8s.mdc /
|
|
6
6
|
* `k8s.kustomization` rego — тут лише abie-специфічне.
|
|
7
|
+
* @param {string} [cwd] корінь репозиторію
|
|
7
8
|
*/
|
|
8
9
|
import { readFile } from 'node:fs/promises'
|
|
9
10
|
import { relative } from 'node:path'
|
|
@@ -17,11 +18,12 @@ import { abieOverlayK8sTreeHasDeployment, isUaKustomizationPath } from '../lib/o
|
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* @returns {Promise<number>} результат
|
|
21
|
+
* @param {string} [cwd] корінь репозиторію
|
|
20
22
|
*/
|
|
21
|
-
export async function check() {
|
|
23
|
+
export async function check(cwd = process.cwd()) {
|
|
22
24
|
const reporter = createCheckReporter()
|
|
23
25
|
const { pass, fail } = reporter
|
|
24
|
-
const root =
|
|
26
|
+
const root = cwd
|
|
25
27
|
|
|
26
28
|
const ignorePaths = await loadCursorIgnorePaths(root)
|
|
27
29
|
const yamls = await findK8sYamlFiles(root, ignorePaths)
|
package/rules/adr/js/hooks.mjs
CHANGED
|
@@ -32,8 +32,8 @@ const HOOK_ARTIFACTS = /** @type {const} */ ([
|
|
|
32
32
|
{ scriptName: 'normalize-decisions.sh', logName: 'normalize-decisions.log' }
|
|
33
33
|
])
|
|
34
34
|
|
|
35
|
-
const
|
|
36
|
-
const
|
|
35
|
+
const PROJECT_SETTINGS_REL = '.claude/settings.json'
|
|
36
|
+
const CURSOR_HOOKS_REL = '.cursor/hooks.json'
|
|
37
37
|
const EOL_RE = /\r?\n/u
|
|
38
38
|
|
|
39
39
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
@@ -83,26 +83,28 @@ function gitignoreLineCoversHookLog(line, logPath) {
|
|
|
83
83
|
/**
|
|
84
84
|
* Перевіряє наявність і канонічність одного hook-скрипта.
|
|
85
85
|
* @param {import('../../../scripts/lib/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
86
|
+
* @param {string} cwd корінь репозиторію
|
|
86
87
|
* @param {string} scriptName базове ім'я скрипта (наприклад `capture-decisions.sh`)
|
|
87
88
|
* @returns {Promise<void>}
|
|
88
89
|
*/
|
|
89
|
-
async function checkHookScript(reporter, scriptName) {
|
|
90
|
+
async function checkHookScript(reporter, cwd, scriptName) {
|
|
90
91
|
const { pass, fail } = reporter
|
|
91
|
-
const
|
|
92
|
+
const projectRel = projectHookPath(scriptName)
|
|
93
|
+
const projectAbs = join(cwd, projectRel)
|
|
92
94
|
const bundledPath = join(BUNDLED_HOOKS_DIR, scriptName)
|
|
93
|
-
if (!existsSync(
|
|
94
|
-
fail(`${
|
|
95
|
+
if (!existsSync(projectAbs)) {
|
|
96
|
+
fail(`${projectRel} не існує — запусти \`npx @nitra/cursor\` (правило adr копіює канонічний скрипт)`)
|
|
95
97
|
return
|
|
96
98
|
}
|
|
97
99
|
if (!existsSync(bundledPath)) {
|
|
98
100
|
fail(`канонічний скрипт у пакеті не знайдено: ${bundledPath} — перевстанови @nitra/cursor`)
|
|
99
101
|
return
|
|
100
102
|
}
|
|
101
|
-
const [project, bundled] = await Promise.all([readFile(
|
|
103
|
+
const [project, bundled] = await Promise.all([readFile(projectAbs, 'utf8'), readFile(bundledPath, 'utf8')])
|
|
102
104
|
if (project === bundled) {
|
|
103
|
-
pass(`${
|
|
105
|
+
pass(`${projectRel} збігається з канонічним`)
|
|
104
106
|
} else {
|
|
105
|
-
fail(`${
|
|
107
|
+
fail(`${projectRel} відрізняється від канонічного — запусти \`npx @nitra/cursor\` для повторного синку`)
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
|
|
@@ -112,13 +114,14 @@ async function checkHookScript(reporter, scriptName) {
|
|
|
112
114
|
* `capture-decisions.sh`; `settings.local.json` не дублює) валідують
|
|
113
115
|
* `npm/policy/adr/settings_json/` і `npm/policy/adr/settings_local_json/`.
|
|
114
116
|
* @param {import('../../../scripts/lib/check-reporter.mjs').CheckReporter} reporter репортер
|
|
117
|
+
* @param {string} cwd корінь репозиторію
|
|
115
118
|
*/
|
|
116
|
-
function checkProjectSettings(reporter) {
|
|
119
|
+
function checkProjectSettings(reporter, cwd) {
|
|
117
120
|
const { pass, fail } = reporter
|
|
118
|
-
if (existsSync(
|
|
119
|
-
pass(`${
|
|
121
|
+
if (existsSync(join(cwd, PROJECT_SETTINGS_REL))) {
|
|
122
|
+
pass(`${PROJECT_SETTINGS_REL} є (Stop-hook перевіряє npx @nitra/cursor fix → adr.settings_json)`)
|
|
120
123
|
} else {
|
|
121
|
-
fail(`${
|
|
124
|
+
fail(`${PROJECT_SETTINGS_REL} не існує — запусти \`npx @nitra/cursor\``)
|
|
122
125
|
}
|
|
123
126
|
}
|
|
124
127
|
|
|
@@ -165,25 +168,27 @@ function cursorConfigHasStopHook(config, marker) {
|
|
|
165
168
|
/**
|
|
166
169
|
* Перевіряє project-level Cursor hooks config для ADR stop-hooks.
|
|
167
170
|
* @param {import('../../../scripts/lib/check-reporter.mjs').CheckReporter} reporter репортер
|
|
171
|
+
* @param {string} cwd корінь репозиторію
|
|
168
172
|
* @returns {Promise<void>}
|
|
169
173
|
*/
|
|
170
|
-
async function checkCursorHooks(reporter) {
|
|
174
|
+
async function checkCursorHooks(reporter, cwd) {
|
|
171
175
|
const { pass, fail } = reporter
|
|
172
|
-
|
|
173
|
-
|
|
176
|
+
const cursorHooksAbs = join(cwd, CURSOR_HOOKS_REL)
|
|
177
|
+
if (!existsSync(cursorHooksAbs)) {
|
|
178
|
+
fail(`${CURSOR_HOOKS_REL} не існує — запусти \`npx @nitra/cursor\``)
|
|
174
179
|
return
|
|
175
180
|
}
|
|
176
|
-
const config = await readJsonSafe(
|
|
181
|
+
const config = await readJsonSafe(cursorHooksAbs)
|
|
177
182
|
if (config === null) {
|
|
178
|
-
fail(`${
|
|
183
|
+
fail(`${CURSOR_HOOKS_REL} не парситься як JSON — запусти \`npx @nitra/cursor\` або виправ файл`)
|
|
179
184
|
return
|
|
180
185
|
}
|
|
181
186
|
for (const { scriptName } of HOOK_ARTIFACTS) {
|
|
182
187
|
const marker = projectHookPath(scriptName)
|
|
183
188
|
if (cursorConfigHasStopHook(config, marker)) {
|
|
184
|
-
pass(`${
|
|
189
|
+
pass(`${CURSOR_HOOKS_REL} має stop-hook для ${marker}`)
|
|
185
190
|
} else {
|
|
186
|
-
fail(`${
|
|
191
|
+
fail(`${CURSOR_HOOKS_REL}: відсутній stop-hook для \`${marker}\` (adr.mdc)`)
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
194
|
}
|
|
@@ -212,17 +217,19 @@ function checkGitignoreForLog(reporter, logName, gitignoreContent) {
|
|
|
212
217
|
/**
|
|
213
218
|
* Перевіряє `.gitignore` для всіх hook-логів одним проходом.
|
|
214
219
|
* @param {import('../../../scripts/lib/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
220
|
+
* @param {string} cwd корінь репозиторію
|
|
215
221
|
* @returns {Promise<void>}
|
|
216
222
|
*/
|
|
217
|
-
async function checkGitignore(reporter) {
|
|
223
|
+
async function checkGitignore(reporter, cwd) {
|
|
218
224
|
const { fail } = reporter
|
|
219
|
-
|
|
225
|
+
const gitignoreAbs = join(cwd, '.gitignore')
|
|
226
|
+
if (!existsSync(gitignoreAbs)) {
|
|
220
227
|
for (const { logName } of HOOK_ARTIFACTS) {
|
|
221
228
|
fail(`.gitignore не існує — додай рядок \`${projectLogPath(logName)}\``)
|
|
222
229
|
}
|
|
223
230
|
return
|
|
224
231
|
}
|
|
225
|
-
const content = await readFile(
|
|
232
|
+
const content = await readFile(gitignoreAbs, 'utf8')
|
|
226
233
|
for (const { logName } of HOOK_ARTIFACTS) {
|
|
227
234
|
checkGitignoreForLog(reporter, logName, content)
|
|
228
235
|
}
|
|
@@ -273,16 +280,17 @@ function checkLlmCliAvailable(reporter) {
|
|
|
273
280
|
|
|
274
281
|
/**
|
|
275
282
|
* Перевіряє відповідність проєкту правилам adr.mdc.
|
|
283
|
+
* @param {string} [cwd] корінь репозиторію
|
|
276
284
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
277
285
|
*/
|
|
278
|
-
export async function check() {
|
|
286
|
+
export async function check(cwd = process.cwd()) {
|
|
279
287
|
const reporter = createCheckReporter()
|
|
280
288
|
for (const { scriptName } of HOOK_ARTIFACTS) {
|
|
281
|
-
await checkHookScript(reporter, scriptName)
|
|
289
|
+
await checkHookScript(reporter, cwd, scriptName)
|
|
282
290
|
}
|
|
283
|
-
checkProjectSettings(reporter)
|
|
284
|
-
await checkCursorHooks(reporter)
|
|
285
|
-
await checkGitignore(reporter)
|
|
291
|
+
checkProjectSettings(reporter, cwd)
|
|
292
|
+
await checkCursorHooks(reporter, cwd)
|
|
293
|
+
await checkGitignore(reporter, cwd)
|
|
286
294
|
checkLlmCliAvailable(reporter)
|
|
287
295
|
return reporter.getExitCode()
|
|
288
296
|
}
|
package/rules/bun/js/layout.mjs
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { existsSync } from 'node:fs'
|
|
21
21
|
import { readFile } from 'node:fs/promises'
|
|
22
|
+
import { join } from 'node:path'
|
|
22
23
|
|
|
23
24
|
import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
24
25
|
|
|
@@ -31,13 +32,15 @@ const WHITESPACE_RE = /\s+/u
|
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Зчитує `rules` та `disable-rules` з `.n-cursor.json`.
|
|
35
|
+
* @param {string} cwd корінь репозиторію
|
|
34
36
|
* @returns {Promise<{ rules: Set<string>, disabled: Set<string> }>} активні правила і явно вимкнені
|
|
35
37
|
*/
|
|
36
|
-
async function loadNCursorRules() {
|
|
38
|
+
async function loadNCursorRules(cwd) {
|
|
37
39
|
const empty = { rules: new Set(), disabled: new Set() }
|
|
38
|
-
|
|
40
|
+
const cfgPath = join(cwd, '.n-cursor.json')
|
|
41
|
+
if (!existsSync(cfgPath)) return empty
|
|
39
42
|
try {
|
|
40
|
-
const raw = JSON.parse(await readFile(
|
|
43
|
+
const raw = JSON.parse(await readFile(cfgPath, 'utf8'))
|
|
41
44
|
const list = Array.isArray(raw?.rules) ? raw.rules.map(String) : []
|
|
42
45
|
const disabled = Array.isArray(raw?.['disable-rules']) ? raw['disable-rules'].map(String) : []
|
|
43
46
|
return { rules: new Set(list), disabled: new Set(disabled) }
|
|
@@ -157,45 +160,47 @@ function checkCursorRuleScripts(reporter, scripts, cursorRules) {
|
|
|
157
160
|
|
|
158
161
|
/**
|
|
159
162
|
* Перевіряє відповідність проєкту правилам bun.mdc
|
|
163
|
+
* @param {string} [cwd] корінь репозиторію
|
|
160
164
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
161
165
|
*/
|
|
162
|
-
export async function check() {
|
|
166
|
+
export async function check(cwd = process.cwd()) {
|
|
163
167
|
const reporter = createCheckReporter()
|
|
164
168
|
const { pass, fail } = reporter
|
|
165
169
|
|
|
166
170
|
for (const f of ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', '.yarnrc.yml']) {
|
|
167
|
-
if (existsSync(f)) {
|
|
171
|
+
if (existsSync(join(cwd, f))) {
|
|
168
172
|
fail(`Знайдено заборонений файл: ${f} — видали його`)
|
|
169
173
|
} else {
|
|
170
174
|
pass(`Немає ${f}`)
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
177
|
|
|
174
|
-
if (existsSync('.yarn')) {
|
|
178
|
+
if (existsSync(join(cwd, '.yarn'))) {
|
|
175
179
|
fail('Знайдено директорію .yarn — видали її')
|
|
176
180
|
} else {
|
|
177
181
|
pass('Немає .yarn/')
|
|
178
182
|
}
|
|
179
|
-
if (existsSync('bun.lock')) {
|
|
183
|
+
if (existsSync(join(cwd, 'bun.lock'))) {
|
|
180
184
|
pass('bun.lock є')
|
|
181
185
|
} else {
|
|
182
186
|
fail('Відсутній bun.lock — запусти bun i')
|
|
183
187
|
}
|
|
184
188
|
|
|
185
|
-
if (existsSync('bunfig.toml')) {
|
|
189
|
+
if (existsSync(join(cwd, 'bunfig.toml'))) {
|
|
186
190
|
pass('bunfig.toml є (структуру перевіряє npx @nitra/cursor fix → bun.bunfig)')
|
|
187
191
|
} else {
|
|
188
192
|
fail('Відсутній bunfig.toml — створи з [install] linker = "hoisted" (bun.mdc)')
|
|
189
193
|
}
|
|
190
194
|
|
|
191
|
-
const cursorRules = await loadNCursorRules()
|
|
195
|
+
const cursorRules = await loadNCursorRules(cwd)
|
|
192
196
|
|
|
193
|
-
|
|
197
|
+
const pkgPath = join(cwd, 'package.json')
|
|
198
|
+
if (!existsSync(pkgPath)) {
|
|
194
199
|
fail('Відсутній package.json у корені')
|
|
195
200
|
return reporter.getExitCode()
|
|
196
201
|
}
|
|
197
202
|
|
|
198
|
-
const pkg = JSON.parse(await readFile(
|
|
203
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
|
|
199
204
|
const scripts = pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {}
|
|
200
205
|
checkCursorRuleScripts(reporter, scripts, cursorRules)
|
|
201
206
|
|
|
@@ -429,12 +429,13 @@ async function isIosCocoaPodsExemptByNitraConfig(root) {
|
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
/**
|
|
432
|
+
* @param {string} [cwd] корінь репозиторію
|
|
432
433
|
* @returns {Promise<number>} **0** — **ok**; **1** — **fail** (див. **capacitor.mdc**)
|
|
433
434
|
*/
|
|
434
|
-
export async function check() {
|
|
435
|
+
export async function check(cwd = process.cwd()) {
|
|
435
436
|
const reporter = createCheckReporter()
|
|
436
437
|
const { pass, fail, getExitCode } = reporter
|
|
437
|
-
const root =
|
|
438
|
+
const root = cwd
|
|
438
439
|
|
|
439
440
|
const acc = { byPath: new Map(), anyCapacitor: false }
|
|
440
441
|
await collectCapacitorDataFromAllPackageJson(root, acc)
|