@nitra/cursor 1.13.76 → 1.13.83
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 +89 -0
- package/README.md +5 -5
- package/bin/n-cursor.js +16 -21
- package/package.json +1 -1
- package/rules/abie/abie.mdc +12 -4
- package/rules/abie/fix.mjs +15 -0
- package/rules/abie/policy/base_deployment_preem/base_deployment_preem.rego +9 -11
- package/rules/abie/policy/health_check_policy/health_check_policy.rego +9 -10
- package/rules/abie/policy/http_route_base/http_route_base.rego +7 -8
- package/rules/abie/utils/k8s-tree.mjs +1 -1
- package/rules/adr/adr.mdc +1 -1
- package/rules/adr/fix.mjs +15 -0
- package/rules/bun/fix.mjs +15 -0
- package/rules/capacitor/fix.mjs +15 -0
- package/rules/changelog/changelog.mdc +1 -1
- package/rules/changelog/fix.mjs +15 -0
- package/rules/changelog/{fix → js}/consistency/check.mjs +16 -16
- package/{scripts/utils → rules/changelog/js/consistency}/package-manifest.mjs +1 -1
- package/rules/ci4/fix.mjs +15 -0
- package/rules/docker/docker.mdc +3 -3
- package/rules/docker/fix.mjs +15 -0
- package/rules/docker/{fix → js}/lint/check.mjs +3 -3
- package/{scripts/utils → rules/docker/js/lint}/docker-hadolint.mjs +3 -3
- package/rules/docker/lint/lint.mjs +2 -2
- package/rules/efes/fix.mjs +15 -0
- package/rules/feedback/fix.mjs +15 -0
- package/rules/ga/fix.mjs +15 -0
- package/rules/ga/lint/lint.mjs +5 -2
- package/rules/graphql/fix.mjs +15 -0
- package/rules/graphql/{fix → js}/tooling/check.mjs +1 -1
- package/{scripts/utils → rules/graphql/js/tooling}/graphql-gql-scan.mjs +47 -12
- package/rules/hasura/fix.mjs +15 -0
- package/rules/hasura/{fix → js}/internal_urls/check.mjs +1 -1
- package/rules/hasura/policy/svc_hl/svc_hl.rego +1 -1
- package/rules/image-avif/fix.mjs +15 -0
- package/rules/image-compress/fix.mjs +15 -0
- package/rules/js-bun-db/fix.mjs +15 -0
- package/{scripts/utils → rules/js-bun-db/js/safety}/bun-sql-scan.mjs +1 -1
- package/rules/js-bun-db/{fix → js}/safety/check.mjs +1 -1
- package/rules/js-bun-redis/fix.mjs +15 -0
- package/rules/js-bun-redis/policy/package_json/package_json.rego +1 -1
- package/rules/js-lint/fix.mjs +15 -0
- package/rules/js-lint/{fix → js}/tooling/check.mjs +3 -15
- package/{scripts/utils → rules/js-lint/js/tooling}/rebuild-oxlint-canonical.mjs +1 -1
- package/rules/js-lint/js-lint.mdc +2 -2
- package/rules/js-mssql/fix.mjs +15 -0
- package/rules/js-mssql/{fix → js}/deps/check.mjs +1 -1
- package/{scripts/utils → rules/js-mssql/js/deps}/mssql-pool-scan.mjs +1 -1
- package/rules/js-run/fix.mjs +15 -0
- package/{scripts/utils → rules/js-run/js/runtime}/bunyan-imports.mjs +1 -1
- package/{scripts/utils → rules/js-run/js/runtime}/check-env-scan.mjs +1 -1
- package/rules/js-run/{fix → js}/runtime/check.mjs +5 -5
- package/{scripts/utils → rules/js-run/js/runtime}/conn-file-rules.mjs +1 -1
- package/{scripts/utils → rules/js-run/js/runtime}/conn-imports-scan.mjs +1 -1
- package/{scripts/utils → rules/js-run/js/runtime}/promise-settimeout-scan.mjs +1 -1
- package/rules/k8s/fix.mjs +15 -0
- package/rules/k8s/k8s.mdc +2 -2
- package/rules/nginx-default-tpl/fix.mjs +15 -0
- package/rules/nginx-default-tpl/{fix → js}/template/check.mjs +1 -1
- package/rules/npm-module/fix.mjs +15 -0
- package/rules/npm-module/{fix → js}/package_structure/check.mjs +11 -1
- package/rules/php/fix.mjs +15 -0
- package/rules/rego/fix.mjs +15 -0
- package/rules/security/fix.mjs +15 -0
- package/rules/security/security.mdc +2 -2
- package/rules/style-lint/fix.mjs +15 -0
- package/rules/tauri/fix.mjs +15 -0
- package/rules/test/auto.md +1 -0
- package/rules/test/fix.mjs +15 -0
- package/rules/test/js/location/check.mjs +77 -0
- package/rules/test/test.mdc +60 -0
- package/rules/text/fix.mjs +15 -0
- package/rules/vue/fix.mjs +15 -0
- package/rules/vue/{fix → js}/packages/check.mjs +2 -2
- package/rules/vue/vue.mdc +1 -1
- package/scripts/auto-rules.mjs +3 -3
- package/scripts/sync-claude-config.mjs +2 -2
- package/scripts/utils/ast-scan-utils.mjs +3 -2
- package/scripts/utils/discover-checkable-rules.mjs +30 -18
- package/scripts/utils/list-rule-ids.mjs +23 -0
- package/scripts/utils/run-rule.mjs +7 -7
- package/scripts/utils/run-standard-rule.mjs +34 -0
- package/scripts/utils/walk-cache.mjs +24 -0
- package/scripts/utils/with-lock.mjs +120 -0
- package/scripts/utils/workspaces.mjs +1 -1
- package/scripts/utils/worktree-fingerprint.mjs +30 -0
- /package/rules/abie/{fix → js}/applies/check.mjs +0 -0
- /package/rules/abie/{fix → js}/env_dns/check.mjs +0 -0
- /package/rules/abie/{fix → js}/firebase_hosting/check.mjs +0 -0
- /package/rules/abie/{fix → js}/hc_pairing/check.mjs +0 -0
- /package/rules/abie/{fix → js}/ua_http_route/check.mjs +0 -0
- /package/rules/abie/{fix → js}/ua_node_selector/check.mjs +0 -0
- /package/rules/adr/{fix → js}/hooks/check.mjs +0 -0
- /package/rules/adr/{fix → js}/hooks/template/.gitignore.snippet +0 -0
- /package/rules/bun/{fix → js}/layout/check.mjs +0 -0
- /package/rules/capacitor/{fix → js}/platforms/check.mjs +0 -0
- /package/{scripts/utils → rules/docker/js/lint}/docker-mirror.mjs +0 -0
- /package/rules/ga/{fix → js}/workflows/check.mjs +0 -0
- /package/rules/image-avif/{fix → js}/avif_generation/check.mjs +0 -0
- /package/rules/image-compress/{fix → js}/package_setup/check.mjs +0 -0
- /package/rules/js-bun-redis/{fix → js}/imports/check.mjs +0 -0
- /package/{scripts/utils → rules/js-lint/js/tooling}/knip-canonical.json +0 -0
- /package/{scripts/utils → rules/js-lint/js/tooling}/oxlint-canonical-skeleton.json +0 -0
- /package/{scripts/utils → rules/js-lint/js/tooling}/oxlint-canonical.json +0 -0
- /package/{scripts/utils → rules/js-lint/js/tooling}/oxlint-rules.tsv +0 -0
- /package/rules/k8s/{fix → js}/kubescape_exceptions/template/.kubescape-exceptions.json.snippet.json +0 -0
- /package/rules/k8s/{fix → js}/manifests/check.mjs +0 -0
- /package/rules/php/{fix → js}/tooling/check.mjs +0 -0
- /package/rules/rego/{fix → js}/applies/check.mjs +0 -0
- /package/rules/security/{fix → js}/sample_secret/check.mjs +0 -0
- /package/rules/security/{fix → js}/trufflehog/check.mjs +0 -0
- /package/rules/security/{fix → js}/trufflehog/template/.trufflehog-exclude.snippet.txt +0 -0
- /package/rules/style-lint/{fix → js}/tooling/check.mjs +0 -0
- /package/rules/tauri/{fix → js}/tooling/check.mjs +0 -0
- /package/rules/text/{fix → js}/formatting/check.mjs +0 -0
- /package/{scripts/utils → rules/vue/js/packages}/vue-forbidden-imports.mjs +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Перевіряє лінт JavaScript за правилом js-lint.mdc.
|
|
3
3
|
*
|
|
4
4
|
* Flat ESLint з getConfig і ignore для auto-imports,
|
|
5
|
-
* `.oxlintrc.json` має збігатися з каноном oxlint у пакеті (`npm/
|
|
5
|
+
* `.oxlintrc.json` має збігатися з каноном oxlint у пакеті (`npm/rules/js-lint/js/tooling/oxlint-canonical.json`):
|
|
6
6
|
* plugins, jsPlugins, categories, усі правила з канону (додаткові записи в `rules` дозволені), settings, env,
|
|
7
7
|
* globals, ignorePatterns. Також перевіряє workspace `package.json` на `type: "module"`
|
|
8
8
|
* і `engines`, workflow-дубль у `lint.yml`, `knip.json` autofill і застарілі `.eslintrc*`.
|
|
@@ -20,24 +20,12 @@ import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mj
|
|
|
20
20
|
/** Шлях до канонічного oxlint JSON у цьому пакеті (для перевірки та тестів). */
|
|
21
21
|
export const OXLINT_CANONICAL_JSON_PATH = join(
|
|
22
22
|
dirname(fileURLToPath(import.meta.url)),
|
|
23
|
-
'..',
|
|
24
|
-
'..',
|
|
25
|
-
'..',
|
|
26
|
-
'..',
|
|
27
|
-
'scripts',
|
|
28
|
-
'utils',
|
|
29
23
|
'oxlint-canonical.json'
|
|
30
24
|
)
|
|
31
25
|
|
|
32
26
|
/** Шлях до канонічного knip JSON у цьому пакеті — копіюється у корінь проєкту-споживача, якщо відсутній. */
|
|
33
27
|
export const KNIP_CANONICAL_JSON_PATH = join(
|
|
34
28
|
dirname(fileURLToPath(import.meta.url)),
|
|
35
|
-
'..',
|
|
36
|
-
'..',
|
|
37
|
-
'..',
|
|
38
|
-
'..',
|
|
39
|
-
'scripts',
|
|
40
|
-
'utils',
|
|
41
29
|
'knip-canonical.json'
|
|
42
30
|
)
|
|
43
31
|
|
|
@@ -166,7 +154,7 @@ export function verifyOxlintRcAgainstCanonical(cfg, canonical) {
|
|
|
166
154
|
|
|
167
155
|
if (!deepEqualOxlintCanonical(actual, expected)) {
|
|
168
156
|
failures.push(
|
|
169
|
-
`.oxlintrc.json: поле "${key}" має збігатися з каноном пакета @nitra/cursor (npm/
|
|
157
|
+
`.oxlintrc.json: поле "${key}" має збігатися з каноном пакета @nitra/cursor (npm/rules/js-lint/js/tooling/oxlint-canonical.json)`
|
|
170
158
|
)
|
|
171
159
|
}
|
|
172
160
|
}
|
|
@@ -396,7 +384,7 @@ async function checkKnipConfig(passFn, failFn) {
|
|
|
396
384
|
return
|
|
397
385
|
}
|
|
398
386
|
await copyFile(KNIP_CANONICAL_JSON_PATH, 'knip.json')
|
|
399
|
-
passFn('knip.json створено з канонічного npm/
|
|
387
|
+
passFn('knip.json створено з канонічного npm/rules/js-lint/js/tooling/knip-canonical.json (js-lint.mdc)')
|
|
400
388
|
}
|
|
401
389
|
|
|
402
390
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Збирає `oxlint-canonical.json` з `oxlint-canonical-skeleton.json` (без поля rules) та списку
|
|
3
3
|
* правил у `oxlint-rules.tsv` (колонки: ім’я правила, TAB, severity: deny | off | error).
|
|
4
4
|
*
|
|
5
|
-
* Після змін у TSV або скелеті запускай з каталогу пакета: `bun ./
|
|
5
|
+
* Після змін у TSV або скелеті запускай з каталогу пакета: `bun ./rules/js-lint/js/tooling/rebuild-oxlint-canonical.mjs`,
|
|
6
6
|
* потім скопіюй оновлений канон у корінь споживача як `.oxlintrc.json` за потреби.
|
|
7
7
|
*/
|
|
8
8
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
@@ -25,7 +25,7 @@ version: '1.23'
|
|
|
25
25
|
|
|
26
26
|
У `.vscode/extensions.json` `recommendations` мають містити `dbaeumer.vscode-eslint`, `github.vscode-github-actions`, `oxc.oxc-vscode`: [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
|
|
27
27
|
|
|
28
|
-
У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/
|
|
28
|
+
У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/rules/js-lint/js/tooling/oxlint-canonical.json`** (plugins, jsPlugins з **`@e18e/eslint-plugin`**, categories, повний набір **rules** із канону — додаткові записи в **`rules`** дозволені; також **`settings`**, **`env`**, **`globals`**). Поле **`ignorePatterns`** працює як **`rules`**: канонічні патерни з **`oxlint-canonical.json`** (наразі **`**/schema.graphql`**, **`**/auto-imports.d.ts`**) мають бути присутні, додаткові локальні glob-и дозволені. Оновити канон можна з репозиторію пакета або скопіювавши файл після **`bun ./rules/js-lint/js/tooling/rebuild-oxlint-canonical.mjs`** (джерело правил — **`oxlint-rules.tsv`** + скелет **`oxlint-canonical-skeleton.json`**). Модуль **`@e18e/eslint-plugin`** не оголошуй окремо в **`package.json`** — він уже в залежностях **`@nitra/eslint-config`** (з **3.8.0**), oxlint підвантажує його з **`node_modules`**.
|
|
29
29
|
|
|
30
30
|
Мінімум для розуміння структури (реальний корінь конфігу має збігатися з каноном повністю):
|
|
31
31
|
|
|
@@ -62,7 +62,7 @@ version: '1.23'
|
|
|
62
62
|
|
|
63
63
|
Перевірку невикористаних залежностей і експортів виконує **knip** (заміна `depcheck`). Викликається у скрипті `lint-js` і в CI разом з oxlint/eslint/jscpd — окремий крок у CI не потрібен.
|
|
64
64
|
|
|
65
|
-
У корені проєкту має бути **`knip.json`**, який стартує з канонічного baseline з пакета `@nitra/cursor` — файл [`npm/
|
|
65
|
+
У корені проєкту має бути **`knip.json`**, який стартує з канонічного baseline з пакета `@nitra/cursor` — файл [`npm/rules/js-lint/js/tooling/knip-canonical.json`](./js/tooling/knip-canonical.json). Він покриває типові false-positives для наших правил: `entry` зі CLI-конфігами (eslint, stylelint, oxlint, jscpd, markdownlint-cli2, `commitlint`), `project` для `**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}`, `ignore` для `**/__fixtures__/**`, `ignoreDependencies` для пакетів, посилання на які є лише в не-JS-конфігах (`@nitra/cspell-dict`, `/@cspell\/dict-.+/`), і `ignoreBinaries` для CLI, які канон вимагає викликати через `npx`/`bunx` і яких заборонено додавати в `devDependencies` (`actionlint`, `cspell`, `depcheck`, `eslint`, `git-ai`, `jscpd`, `markdownlint-cli2`, `oxfmt`, `oxlint`, `shellcheck`, `uvx`, `v8r`, `zizmor`).
|
|
66
66
|
|
|
67
67
|
Якщо `knip.json` відсутній — `npx @nitra/cursor check js-lint` копіює канон у корінь проєкту (side effect). Після створення модифікуй файл під свій проєкт як завгодно: перевіряємо лише наявність, зміст подальших змін не валідується.
|
|
68
68
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
findUnsafeMssqlInListUnparsedInText,
|
|
23
23
|
findUnsafeMssqlInListMissingEmptyGuardInText,
|
|
24
24
|
isMssqlScanSourceFile
|
|
25
|
-
} from '
|
|
25
|
+
} from './mssql-pool-scan.mjs'
|
|
26
26
|
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
27
27
|
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
normalizeSnippet,
|
|
31
31
|
offsetToLine,
|
|
32
32
|
walkAstWithAncestors
|
|
33
|
-
} from '
|
|
33
|
+
} from '../../../../scripts/utils/ast-scan-utils.mjs'
|
|
34
34
|
|
|
35
35
|
const SOURCE_FILE_RE = /\.([cm]?[jt]sx?)$/
|
|
36
36
|
const IN_PLACEHOLDER_END_RE = /\bin\s*\(\s*$/iu
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
offsetToLine,
|
|
19
19
|
requireCallModule,
|
|
20
20
|
walkAstWithAncestors
|
|
21
|
-
} from '
|
|
21
|
+
} from '../../../../scripts/utils/ast-scan-utils.mjs'
|
|
22
22
|
|
|
23
23
|
const SOURCE_FILE_RE = /\.([cm]?[jt]sx?)$/u
|
|
24
24
|
const FORBIDDEN_MODULES = new Set(['@nitra/bunyan', 'bunyan'])
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* Якщо ключ обчислюваний (`process.env[varName]`) — пропускаємо без помилки,
|
|
30
30
|
* бо за статичним AST неможливо встановити, яка саме змінна оточення використовується.
|
|
31
31
|
*/
|
|
32
|
-
import { offsetToLine, parseProgramOrNull, walkAstWithAncestors } from '
|
|
32
|
+
import { offsetToLine, parseProgramOrNull, walkAstWithAncestors } from '../../../../scripts/utils/ast-scan-utils.mjs'
|
|
33
33
|
|
|
34
34
|
const SOURCE_FILE_RE = /\.([cm]?[jt]sx?)$/u
|
|
35
35
|
const IGNORE_DIRECTIVE_RE = /\/\/\s*@nitra\/cursor\s+ignore-next-line\s+checkEnv\b/u
|
|
@@ -40,22 +40,22 @@ import {
|
|
|
40
40
|
findBunyanImportsInText,
|
|
41
41
|
isBunyanScanSourceFile,
|
|
42
42
|
shouldSkipFileForBunyanScan
|
|
43
|
-
} from '
|
|
44
|
-
import { findUncheckedProcessEnvInText, isCheckEnvScanSourceFile } from '
|
|
43
|
+
} from './bunyan-imports.mjs'
|
|
44
|
+
import { findUncheckedProcessEnvInText, isCheckEnvScanSourceFile } from './check-env-scan.mjs'
|
|
45
45
|
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
46
46
|
import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
|
|
47
|
-
import { findConnFileRuleViolations, isConnFileRulesSourceFile } from '
|
|
47
|
+
import { findConnFileRuleViolations, isConnFileRulesSourceFile } from './conn-file-rules.mjs'
|
|
48
48
|
import {
|
|
49
49
|
findConnFactoryImportsInText,
|
|
50
50
|
isConnImportsScanSourceFile,
|
|
51
51
|
isInsideConnDir,
|
|
52
52
|
resolveConnDirFromPackageJson
|
|
53
|
-
} from '
|
|
53
|
+
} from './conn-imports-scan.mjs'
|
|
54
54
|
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
55
55
|
import {
|
|
56
56
|
findPromiseSetTimeoutInText,
|
|
57
57
|
isPromiseSetTimeoutScanSourceFile
|
|
58
|
-
} from '
|
|
58
|
+
} from './promise-settimeout-scan.mjs'
|
|
59
59
|
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
60
60
|
import { getMonorepoPackageRootDirs } from '../../../../scripts/utils/workspaces.mjs'
|
|
61
61
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Парсимо через oxc-parser; коли файл не парситься — повертаємо порожні результати, щоб
|
|
15
15
|
* не змішувати помилки синтаксису з порушеннями цього правила.
|
|
16
16
|
*/
|
|
17
|
-
import { parseProgramOrNull } from '
|
|
17
|
+
import { parseProgramOrNull } from '../../../../scripts/utils/ast-scan-utils.mjs'
|
|
18
18
|
|
|
19
19
|
const SOURCE_FILE_RE = /\.([cm]?[jt]sx?)$/u
|
|
20
20
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* використовується. Якщо файл не парситься — повертаємо порожній результат, спочатку
|
|
17
17
|
* треба полагодити синтаксис.
|
|
18
18
|
*/
|
|
19
|
-
import { langFromPath, normalizeSnippet, offsetToLine } from '
|
|
19
|
+
import { langFromPath, normalizeSnippet, offsetToLine } from '../../../../scripts/utils/ast-scan-utils.mjs'
|
|
20
20
|
import { parseSync } from 'oxc-parser'
|
|
21
21
|
|
|
22
22
|
const SOURCE_FILE_RE = /\.([cm]?[jt]sx?)$/u
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Сканер не вимагає, щоб файл компілювався: при синтаксичних помилках повертається
|
|
13
13
|
* порожній результат (як інші сканери — спочатку треба полагодити синтаксис).
|
|
14
14
|
*/
|
|
15
|
-
import { normalizeSnippet, offsetToLine, parseProgramOrNull } from '
|
|
15
|
+
import { normalizeSnippet, offsetToLine, parseProgramOrNull } from '../../../../scripts/utils/ast-scan-utils.mjs'
|
|
16
16
|
|
|
17
17
|
const SOURCE_FILE_RE = /\.([cm]?[jt]sx?)$/
|
|
18
18
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
package/rules/k8s/k8s.mdc
CHANGED
|
@@ -39,7 +39,7 @@ alwaysApply: false
|
|
|
39
39
|
|
|
40
40
|
Якщо в **корені проєкту** є файл **`.kubescape-exceptions.json`** — `lint-k8s` автоматично передає його в `kubescape scan` через **`--exceptions`** ([postureExceptionPolicy](https://github.com/kubescape/kubescape/blob/master/docs/exceptions.md)). Файл — JSON-масив об'єктів з полями `name`, `policyType: "postureExceptionPolicy"`, `actions` (`["alertOnly"]` — знижує fail до alert, не блокує lint), `resources` (resource designator) і `posturePolicies` (масив `controlID`).
|
|
41
41
|
|
|
42
|
-
Канонічний кейс — **C-0012** (`Applications credentials in configuration files`, High): control тригериться на **імʼя** env, що містить підрядок `secret`/`password`/`key`/`token`, а **не** на значення. Для `HASURA_GRAPHQL_JWT_SECRET` у ConfigMap значення — публічний JWT-конфіг (`jwk_url`, `issuer`), не credentials, але kubescape падає лише через імʼя. Точкове виключення для ConfigMap із цим env — канон: [.kubescape-exceptions.json.snippet.json](./
|
|
42
|
+
Канонічний кейс — **C-0012** (`Applications credentials in configuration files`, High): control тригериться на **імʼя** env, що містить підрядок `secret`/`password`/`key`/`token`, а **не** на значення. Для `HASURA_GRAPHQL_JWT_SECRET` у ConfigMap значення — публічний JWT-конфіг (`jwk_url`, `issuer`), не credentials, але kubescape падає лише через імʼя. Точкове виключення для ConfigMap із цим env — канон: [.kubescape-exceptions.json.snippet.json](./js/kubescape_exceptions/template/.kubescape-exceptions.json.snippet.json)
|
|
43
43
|
|
|
44
44
|
Підстав свою `attributes.name` (рядок або regex), якщо ConfigMap зветься інакше; виключай контрольно, а не глобально (не додавай винятки без `attributes.name`/`labels`, бо тоді C-0012 знімається для усіх ConfigMap-ів проєкту і реальні витоки credentials теж пройдуть).
|
|
45
45
|
|
|
@@ -375,7 +375,7 @@ images:
|
|
|
375
375
|
|
|
376
376
|
Для `$schema` у першому рядку див. приклад **HealthCheckPolicy** у тому ж розділі (datree CRDs-catalog).
|
|
377
377
|
|
|
378
|
-
**`spec.targetRef`** (типово **`kind: Service`**) має вказувати на **headless** сервіс — ім’я з суфіксом **`-hl`** (див. **«Service: `svc.yaml` і `svc-hl.yaml`»**); для проєктів **abie** точні умови — **`
|
|
378
|
+
**`spec.targetRef`** (типово **`kind: Service`**) має вказувати на **headless** сервіс — ім’я з суфіксом **`-hl`** (див. **«Service: `svc.yaml` і `svc-hl.yaml`»**); для проєктів **abie** точні умови — Rego-пакет **`abie.health_check_policy`** + **abie.mdc**.
|
|
379
379
|
|
|
380
380
|
За потреби розшир **`target`** (`name`, `namespace`), щоб однозначно вказати об’єкт.
|
|
381
381
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -17,7 +17,7 @@ import { existsSync } from 'node:fs'
|
|
|
17
17
|
import { readdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'
|
|
18
18
|
import { basename, dirname, join, relative } from 'node:path'
|
|
19
19
|
|
|
20
|
-
import { findDockerfilePaths } from '../../../docker/
|
|
20
|
+
import { findDockerfilePaths } from '../../../docker/js/lint/check.mjs'
|
|
21
21
|
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
22
22
|
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
23
23
|
import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -453,6 +453,11 @@ export function findTestFrameworkImport(content, virtualPath) {
|
|
|
453
453
|
* Класифікує опублікований файл як test/fixture, якщо хоча б одна з ознак:
|
|
454
454
|
* (1) у шляху є каталог із `TEST_DIR_NAMES`; (2) basename відповідає
|
|
455
455
|
* `TEST_FILE_PATTERNS`; (3) для JS/TS-розширень — імпорт test-фреймворку.
|
|
456
|
+
*
|
|
457
|
+
* Carve-out: для шляху `rules/<rule-name>/...` сегмент `<rule-name>` (індекс 1)
|
|
458
|
+
* — це ім'я правила, а не каталог. Зокрема, правило з id `test` (або `tests`)
|
|
459
|
+
* описує конвенцію розміщення тестів і саме по собі не є test-fixture'ом.
|
|
460
|
+
* Подальші сегменти (наприклад, `rules/<r>/js/<c>/tests/`) продовжують перевірятись.
|
|
456
461
|
* @param {string} relPath posix-шлях відносно `npm/`
|
|
457
462
|
* @returns {Promise<string | null>} причина порушення або `null`
|
|
458
463
|
*/
|
|
@@ -460,7 +465,12 @@ export async function classifyPublishedFileAsTest(relPath) {
|
|
|
460
465
|
const segments = relPath.split('/')
|
|
461
466
|
const base = segments.at(-1)
|
|
462
467
|
const dirs = segments.slice(0, -1)
|
|
463
|
-
const testDir = dirs.find(seg =>
|
|
468
|
+
const testDir = dirs.find((seg, idx) => {
|
|
469
|
+
if (idx === 1 && dirs[0] === 'rules') {
|
|
470
|
+
return false
|
|
471
|
+
}
|
|
472
|
+
return TEST_DIR_NAMES.has(seg.toLowerCase())
|
|
473
|
+
})
|
|
464
474
|
if (testDir) return `test-style каталог "${testDir}/"`
|
|
465
475
|
if (TEST_FILE_PATTERNS.some(re => re.test(base))) return `test-style ім'я файлу`
|
|
466
476
|
if (JS_LIKE_EXT_RE.test(base)) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -24,7 +24,7 @@ version: '2.1'
|
|
|
24
24
|
|
|
25
25
|
## `.trufflehog-exclude` (рекомендована основа)
|
|
26
26
|
|
|
27
|
-
Канон (допускає розширення): [.trufflehog-exclude.snippet.txt](./
|
|
27
|
+
Канон (допускає розширення): [.trufflehog-exclude.snippet.txt](./js/trufflehog/template/.trufflehog-exclude.snippet.txt)
|
|
28
28
|
|
|
29
29
|
**Важливо:** один regex-pattern на рядок, без TOML-обгортки; коментарі починаються з `#`.
|
|
30
30
|
|
|
@@ -45,7 +45,7 @@ Workflow обовʼязковий — забезпечує незалежний
|
|
|
45
45
|
- Правильно: `DB_PASSWORD=sample-secret`, `password: "sample-secret"`
|
|
46
46
|
- Неправильно: `DB_PASSWORD=secret`, `password: "secret"`
|
|
47
47
|
|
|
48
|
-
Перевіряється лише `secret` у позиції значення (після `=`, `:`, `=>`); імена ключів на кшталт `client_secret` не чіпаються. Concern `security.sample_secret` — деталі скану в `
|
|
48
|
+
Перевіряється лише `secret` у позиції значення (після `=`, `:`, `=>`); імена ключів на кшталт `client_secret` не чіпаються. Concern `security.sample_secret` — деталі скану в `js/sample_secret/check.mjs`.
|
|
49
49
|
|
|
50
50
|
## Перевірка
|
|
51
51
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
якщо у проекті є хоча б один файл `*.test.mjs`
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Перевіряє, що всі `*.test.mjs` лежать у каталозі `tests/` (а не поряд із джерельним файлом).
|
|
3
|
+
*
|
|
4
|
+
* Конвенція (test.mdc): `dir/foo.mjs` → тест у `dir/tests/foo.test.mjs`.
|
|
5
|
+
* `*_test.rego` виключені: Rego unit-тести живуть поряд із полісі (OPA community convention).
|
|
6
|
+
*
|
|
7
|
+
* Пропускає: `node_modules`, `.git`, `dist`, `build`, `.venv`, `venv` (через `walkDir`)
|
|
8
|
+
* і шляхи з `.n-cursor.json:ignore`.
|
|
9
|
+
*/
|
|
10
|
+
import { basename, dirname, relative } from 'node:path'
|
|
11
|
+
|
|
12
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
13
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
14
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
15
|
+
|
|
16
|
+
const TESTS_DIR_NAME = 'tests'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Чи файл є JS-тестом (`*.test.mjs`).
|
|
20
|
+
* @param {string} absPath абсолютний шлях
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
function isTestFile(absPath) {
|
|
24
|
+
return basename(absPath).endsWith('.test.mjs')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Перевіряє, чи лежить тест у каталозі з іменем `tests`.
|
|
29
|
+
* @param {string} absPath абсолютний шлях до тесту
|
|
30
|
+
* @returns {boolean} `true`, якщо басенейм батьківської директорії — `tests`
|
|
31
|
+
*/
|
|
32
|
+
function isInsideTestsDir(absPath) {
|
|
33
|
+
return basename(dirname(absPath)) === TESTS_DIR_NAME
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Перевіряє розміщення тестових файлів у каталозі `tests/` (test.mdc).
|
|
38
|
+
* @returns {Promise<number>} 0 — всі тести у `tests/`, 1 — є порушення
|
|
39
|
+
*/
|
|
40
|
+
export async function check() {
|
|
41
|
+
const reporter = createCheckReporter()
|
|
42
|
+
const { pass, fail } = reporter
|
|
43
|
+
|
|
44
|
+
const cwd = process.cwd()
|
|
45
|
+
const ignorePaths = await loadCursorIgnorePaths(cwd)
|
|
46
|
+
|
|
47
|
+
/** @type {string[]} */
|
|
48
|
+
const offenders = []
|
|
49
|
+
let totalTests = 0
|
|
50
|
+
|
|
51
|
+
await walkDir(
|
|
52
|
+
cwd,
|
|
53
|
+
absPath => {
|
|
54
|
+
if (!isTestFile(absPath)) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
totalTests++
|
|
58
|
+
if (!isInsideTestsDir(absPath)) {
|
|
59
|
+
offenders.push(relative(cwd, absPath))
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
ignorePaths
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if (offenders.length === 0) {
|
|
66
|
+
pass(`Всі ${totalTests} файлів *.test.mjs у каталозі tests/ (test.mdc)`)
|
|
67
|
+
return reporter.getExitCode()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const offenderPath of offenders) {
|
|
71
|
+
const parentDir = dirname(offenderPath)
|
|
72
|
+
const base = basename(offenderPath)
|
|
73
|
+
fail(`${offenderPath}: тест має лежати у tests/ — перенеси у ${parentDir}/${TESTS_DIR_NAME}/${base} (test.mdc)`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return reporter.getExitCode()
|
|
77
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: JS-тести (*.test.mjs) живуть у каталозі tests/ поряд із джерельним файлом, а не безпосередньо в тій же директорії
|
|
3
|
+
version: '1.1'
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Конвенція розміщення тестів
|
|
8
|
+
|
|
9
|
+
JS-тести у пакеті лежать у **піддиректорії `tests/`** поряд із кодом, який тестується, а **не безпосередньо біля джерельного файлу**.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
rules/foo/js/bar/
|
|
13
|
+
├── check.mjs ← JS-джерело
|
|
14
|
+
└── tests/
|
|
15
|
+
└── check.test.mjs ← тест check.mjs
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Чому так:**
|
|
19
|
+
|
|
20
|
+
- Каталог-з-кодом залишається чистим: продакшен-модулі, политики, темплейти не миготять серед тестових файлів.
|
|
21
|
+
- Один погляд на дерево показує, що піде у npm tarball (все, крім `tests/` і `__fixtures__/` — їх ловить `package.json#files` через негативні globs).
|
|
22
|
+
- Якщо тест переростає у мульти-файловий — він уже у власному каталозі, без хаотичного розкидання.
|
|
23
|
+
|
|
24
|
+
**Виняток — Rego unit-тести (`*_test.rego`):** лишаються **поряд із полісі-файлом** відповідно до загальноприйнятого OPA/Conftest community-патерну. `conftest verify -p <dir>` рекурсивно підхоплює їх незалежно від місця в каталозі.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
rules/foo/policy/package_json/
|
|
28
|
+
├── package_json.rego ← Rego-правило
|
|
29
|
+
├── package_json_test.rego ← Rego unit-тести (поряд, не у tests/)
|
|
30
|
+
└── target.json
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Спеціальні випадки:**
|
|
34
|
+
|
|
35
|
+
- **Integration-тести на рівні пакета** — у `<root>/tests/` (наприклад `npm/tests/`). Це не «біля файлу», а виокремлений каталог для тестів, що покривають весь пакет.
|
|
36
|
+
- **Fixtures**: `__fixtures__/` (для shared) і `fixtures/` (для rule-specific) також живуть **усередині `tests/`**: `tests/__fixtures__/...` і `tests/fixtures/...`.
|
|
37
|
+
- **Test helpers** (`test-helpers.mjs`) можуть лишатися у `scripts/utils/` як shared infra — конвенція стосується файлів `*.test.mjs`.
|
|
38
|
+
|
|
39
|
+
**Гарантії tarball-чистоти** через `package.json#files`:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
"files": [
|
|
43
|
+
"...",
|
|
44
|
+
"!**/*.test.mjs",
|
|
45
|
+
"!**/*_test.rego",
|
|
46
|
+
"!**/__fixtures__/**",
|
|
47
|
+
"!**/fixtures/**",
|
|
48
|
+
"!**/test-helpers.mjs"
|
|
49
|
+
]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Recursive globs ловлять файли всередині `tests/` так само, як ловили б їх біля джерела.
|
|
53
|
+
|
|
54
|
+
## Що перевіряє правило
|
|
55
|
+
|
|
56
|
+
`npx @nitra/cursor check test` (concern `location`) проходить деревом пакета й перевіряє: **кожен `*.test.mjs` файл лежить усередині каталогу з ім'ям `tests`** (точне співпадіння басенейму батьківської директорії). Файли поза цим каталогом репортуються з порадою куди їх перенести.
|
|
57
|
+
|
|
58
|
+
`*_test.rego` перевіркою **не охоплюються** — вони не переміщуються.
|
|
59
|
+
|
|
60
|
+
Пропускаються: `node_modules`, `.git`, `dist`, `build`, `.venv`, `venv`, шляхи з `.n-cursor.json:ignore`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
|
+
*/
|
|
8
|
+
export function run(ctx) {
|
|
9
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (import.meta.main) {
|
|
13
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
+
process.exit(await run())
|
|
15
|
+
}
|