@nitra/cursor 1.15.0 → 1.16.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.
Files changed (121) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/bin/n-cursor.js +4 -4
  3. package/package.json +1 -1
  4. package/rules/abie/fix.mjs +3 -3
  5. package/rules/abie/js/applies.mjs +2 -2
  6. package/rules/abie/js/env_dns.mjs +3 -3
  7. package/rules/abie/js/firebase_hosting.mjs +1 -1
  8. package/rules/abie/js/hc_pairing.mjs +4 -4
  9. package/rules/abie/js/ua_http_route.mjs +6 -6
  10. package/rules/abie/js/ua_node_selector.mjs +5 -5
  11. package/rules/adr/adr.mdc +1 -1
  12. package/rules/adr/fix.mjs +3 -3
  13. package/rules/adr/js/hooks.mjs +7 -7
  14. package/rules/bun/fix.mjs +3 -3
  15. package/rules/bun/js/layout.mjs +1 -1
  16. package/rules/capacitor/fix.mjs +3 -3
  17. package/rules/capacitor/js/platforms.mjs +1 -1
  18. package/rules/changelog/fix.mjs +3 -3
  19. package/rules/changelog/js/consistency.mjs +2 -2
  20. package/rules/changelog/{utils → lib}/package-manifest.mjs +1 -1
  21. package/rules/ci4/fix.mjs +3 -3
  22. package/rules/docker/fix.mjs +3 -3
  23. package/rules/docker/js/lint.mjs +4 -4
  24. package/rules/docker/lint/lint.mjs +4 -4
  25. package/rules/efes/fix.mjs +3 -3
  26. package/rules/feedback/fix.mjs +3 -3
  27. package/rules/ga/fix.mjs +3 -3
  28. package/rules/ga/js/workflows.mjs +4 -4
  29. package/rules/ga/lint/lint.mjs +2 -2
  30. package/rules/graphql/fix.mjs +3 -3
  31. package/rules/graphql/js/tooling.mjs +4 -4
  32. package/rules/hasura/fix.mjs +3 -3
  33. package/rules/hasura/js/internal_urls.mjs +2 -2
  34. package/rules/image-avif/fix.mjs +3 -3
  35. package/rules/image-avif/js/avif_generation.mjs +3 -3
  36. package/rules/image-compress/fix.mjs +3 -3
  37. package/rules/image-compress/js/package_setup.mjs +1 -1
  38. package/rules/js-bun-db/fix.mjs +3 -3
  39. package/rules/js-bun-db/js/safety.mjs +3 -3
  40. package/rules/js-bun-redis/fix.mjs +3 -3
  41. package/rules/js-bun-redis/js/imports.mjs +3 -3
  42. package/{scripts/utils → rules/js-bun-redis/lib}/redis-imports.mjs +1 -1
  43. package/rules/js-lint/fix.mjs +3 -3
  44. package/rules/js-lint/js/tooling.mjs +1 -1
  45. package/rules/js-lint/js/utils_imports.mjs +199 -0
  46. package/rules/js-lint/js-lint.mdc +2 -0
  47. package/rules/js-lint/{utils → lib}/rebuild-oxlint-canonical.mjs +1 -1
  48. package/rules/js-mssql/fix.mjs +3 -3
  49. package/rules/js-mssql/js/deps.mjs +3 -3
  50. package/rules/js-run/fix.mjs +3 -3
  51. package/rules/js-run/js/runtime.mjs +9 -9
  52. package/rules/k8s/fix.mjs +3 -3
  53. package/rules/k8s/js/manifests.mjs +3 -3
  54. package/rules/k8s/lint/lint.mjs +2 -2
  55. package/rules/nginx-default-tpl/fix.mjs +3 -3
  56. package/rules/nginx-default-tpl/js/template.mjs +3 -3
  57. package/rules/npm-module/fix.mjs +3 -3
  58. package/rules/npm-module/js/package_structure.mjs +2 -2
  59. package/rules/php/fix.mjs +3 -3
  60. package/rules/php/js/tooling.mjs +1 -1
  61. package/rules/php/lint/lint.mjs +1 -1
  62. package/rules/rego/fix.mjs +3 -3
  63. package/rules/rego/js/applies.mjs +2 -2
  64. package/rules/rego/lint/lint.mjs +1 -1
  65. package/rules/rust/fix.mjs +3 -3
  66. package/rules/rust/js/applies.mjs +2 -2
  67. package/rules/rust/{utils → lib}/has-cargo-toml.mjs +1 -1
  68. package/rules/security/fix.mjs +3 -3
  69. package/rules/security/js/sample_secret.mjs +1 -1
  70. package/rules/security/js/trufflehog.mjs +2 -2
  71. package/rules/style-lint/fix.mjs +3 -3
  72. package/rules/style-lint/js/tooling.mjs +2 -2
  73. package/rules/tauri/fix.mjs +3 -3
  74. package/rules/tauri/js/tooling.mjs +2 -2
  75. package/rules/test/fix.mjs +3 -3
  76. package/rules/test/js/location.mjs +2 -2
  77. package/rules/text/fix.mjs +3 -3
  78. package/rules/text/js/formatting.mjs +2 -2
  79. package/rules/text/lint/lint.mjs +2 -2
  80. package/rules/vue/fix.mjs +3 -3
  81. package/rules/vue/js/packages.mjs +4 -4
  82. package/scripts/auto-rules.mjs +3 -3
  83. package/scripts/{utils → lib}/check-reporter.mjs +1 -1
  84. package/scripts/{utils → lib}/resolve-target-files.mjs +1 -1
  85. package/scripts/{utils → lib}/run-conftest-batch.mjs +1 -1
  86. package/scripts/{utils → lib}/run-lint-step.mjs +1 -1
  87. package/scripts/{utils → lib}/run-rule-cli.mjs +1 -1
  88. package/scripts/{utils → lib}/run-standard-lint.mjs +2 -2
  89. package/scripts/{utils → lib}/run-standard-rule.mjs +2 -2
  90. package/scripts/rename-yaml-extensions.mjs +1 -1
  91. /package/rules/abie/{utils → lib}/enabled.mjs +0 -0
  92. /package/rules/abie/{utils → lib}/env-dns.mjs +0 -0
  93. /package/rules/abie/{utils → lib}/hc-yaml.mjs +0 -0
  94. /package/rules/abie/{utils → lib}/http-route.mjs +0 -0
  95. /package/rules/abie/{utils → lib}/k8s-tree.mjs +0 -0
  96. /package/rules/abie/{utils → lib}/kustomization-patches.mjs +0 -0
  97. /package/rules/abie/{utils → lib}/overlay-paths.mjs +0 -0
  98. /package/rules/abie/{utils → lib}/yaml.mjs +0 -0
  99. /package/rules/docker/{utils → lib}/docker-hadolint.mjs +0 -0
  100. /package/rules/docker/{utils → lib}/docker-mirror.mjs +0 -0
  101. /package/rules/graphql/{utils → lib}/graphql-gql-scan.mjs +0 -0
  102. /package/rules/js-bun-db/{utils → lib}/bun-sql-scan.mjs +0 -0
  103. /package/rules/js-mssql/{utils → lib}/mssql-pool-scan.mjs +0 -0
  104. /package/rules/js-run/{utils → lib}/bunyan-imports.mjs +0 -0
  105. /package/rules/js-run/{utils → lib}/check-env-scan.mjs +0 -0
  106. /package/rules/js-run/{utils → lib}/conn-file-rules.mjs +0 -0
  107. /package/rules/js-run/{utils → lib}/conn-imports-scan.mjs +0 -0
  108. /package/rules/js-run/{utils → lib}/promise-settimeout-scan.mjs +0 -0
  109. /package/rules/vue/{utils → lib}/vue-forbidden-imports.mjs +0 -0
  110. /package/scripts/{utils → lib}/check-mdc-template-refs.mjs +0 -0
  111. /package/scripts/{utils → lib}/discover-check-rules-from-cursor.mjs +0 -0
  112. /package/scripts/{utils → lib}/discover-checkable-rules.mjs +0 -0
  113. /package/scripts/{utils → lib}/generated-markdown.mjs +0 -0
  114. /package/scripts/{utils → lib}/gha-workflow.mjs +0 -0
  115. /package/scripts/{utils → lib}/inline-template-links.mjs +0 -0
  116. /package/scripts/{utils → lib}/list-rule-ids.mjs +0 -0
  117. /package/scripts/{utils → lib}/load-cursor-config.mjs +0 -0
  118. /package/scripts/{utils → lib}/read-n-cursor-config-lite.mjs +0 -0
  119. /package/scripts/{utils → lib}/run-rule.mjs +0 -0
  120. /package/scripts/{utils → lib}/template.mjs +0 -0
  121. /package/scripts/{utils → lib}/workspaces.mjs +0 -0
@@ -10,14 +10,14 @@ import { existsSync } from 'node:fs'
10
10
  import { readFile } from 'node:fs/promises'
11
11
  import { relative } from 'node:path'
12
12
 
13
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
13
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
14
14
  import {
15
15
  isGqlScanSourceFile,
16
16
  shouldSkipFileForGqlScan,
17
17
  sourceFileHasGqlTaggedTemplate
18
- } from '../utils/graphql-gql-scan.mjs'
19
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
20
- import { runConftestBatch } from '../../../scripts/utils/run-conftest-batch.mjs'
18
+ } from '../lib/graphql-gql-scan.mjs'
19
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
20
+ import { runConftestBatch } from '../../../scripts/lib/run-conftest-batch.mjs'
21
21
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
22
22
 
23
23
  /** Очікуваний файл GraphQL Config у корені (graphql.mdc). */
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -29,8 +29,8 @@ import { basename, join, relative } from 'node:path'
29
29
  import { parseAllDocuments } from 'yaml'
30
30
 
31
31
  import { getRepositoryUrl } from '../../../scripts/auto-rules.mjs'
32
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
33
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
32
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
33
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
34
34
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
35
35
 
36
36
  const NITRA_REPOSITORY_URL_MARKER = 'https://github.com/nitra/'
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -30,11 +30,11 @@ import { join, relative } from 'node:path'
30
30
  import { spawnSync } from 'node:child_process'
31
31
  import { env } from 'node:process'
32
32
 
33
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
34
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
33
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
34
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
35
35
  import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
36
36
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
37
- import { getMonorepoPackageRootDirs } from '../../../scripts/utils/workspaces.mjs'
37
+ import { getMonorepoPackageRootDirs } from '../../../scripts/lib/workspaces.mjs'
38
38
 
39
39
  /** Імʼя CLI-пакета, який генерує AVIF. */
40
40
  const MINIFY_PACKAGE_NAME = '@nitra/minify-image'
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -19,7 +19,7 @@
19
19
  import { existsSync } from 'node:fs'
20
20
  import { readFile } from 'node:fs/promises'
21
21
 
22
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
22
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
23
23
 
24
24
  /** Імʼя committed-кешу (sha1 + originalSize + size) у `@nitra/minify-image` ≥ 3.2.0. */
25
25
  const HASH_CACHE_FILENAME = '.n-minify-image.tsv'
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -33,7 +33,7 @@ import { existsSync } from 'node:fs'
33
33
  import { readFile } from 'node:fs/promises'
34
34
  import { join, relative } from 'node:path'
35
35
 
36
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
36
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
37
37
  import {
38
38
  findBunSqlPerRequestConnectionInText,
39
39
  findBunSqlPgLeftoverCallInText,
@@ -48,9 +48,9 @@ import {
48
48
  isBunSqlScanSourceFile,
49
49
  textHasBunSqlImport,
50
50
  textHasPgLibImport
51
- } from '../utils/bun-sql-scan.mjs'
51
+ } from '../lib/bun-sql-scan.mjs'
52
52
  import { findAllPackageJsonPaths } from '../../../scripts/utils/find-package-json-paths.mjs'
53
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
53
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
54
54
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
55
55
 
56
56
  // Дешеві pre-filter regex'и для AST-сканера LISTEN/NOTIFY: уникаємо парсингу
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -14,13 +14,13 @@ import { existsSync } from 'node:fs'
14
14
  import { readFile } from 'node:fs/promises'
15
15
  import { join, relative } from 'node:path'
16
16
 
17
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
18
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
17
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
18
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
19
19
  import {
20
20
  findRedisImportsInText,
21
21
  isRedisScanSourceFile,
22
22
  shouldSkipFileForRedisScan
23
- } from '../../../scripts/utils/redis-imports.mjs'
23
+ } from '../lib/redis-imports.mjs'
24
24
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
25
25
 
26
26
  /**
@@ -26,7 +26,7 @@ import {
26
26
  offsetToLine,
27
27
  requireCallModule,
28
28
  walkAstWithAncestors
29
- } from './ast-scan-utils.mjs'
29
+ } from '../../../scripts/utils/ast-scan-utils.mjs'
30
30
 
31
31
  const SOURCE_FILE_RE = /\.([cm]?[jt]sx?)$/u
32
32
  const FORBIDDEN_MODULE_NAMES = new Set([
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -15,7 +15,7 @@ import { copyFile, readFile } from 'node:fs/promises'
15
15
  import { dirname, join } from 'node:path'
16
16
  import { fileURLToPath } from 'node:url'
17
17
 
18
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
18
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
19
19
 
20
20
  /** Шлях до канонічного oxlint JSON у цьому пакеті (для перевірки та тестів). */
21
21
  export const OXLINT_CANONICAL_JSON_PATH = join(
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Перевіряє, що файли всередині будь-якого `utils/` каталогу не мають імпортів за межі
3
+ * самого каталогу (relative-import з `..`). За правилом `js-lint.mdc` каталог `utils/` — це
4
+ * generic helpers без бізнес-логіки і без залежностей від домену; якщо файлу потрібен
5
+ * сусідній модуль, конфіг проєкту чи cross-rule helper — він має жити в `lib/`, а не в `utils/`.
6
+ *
7
+ * Перевіряються лише не-тестові `.mjs|.mts|.cjs|.cts|.js|.ts|.jsx|.tsx` під будь-яким
8
+ * `utils/`-каталогом у monorepo-воркспейсах. Файли всередині `utils/tests/` (тести)
9
+ * і будь-які `__fixtures__/` ігноруються — тести легально імпортують свій модуль через `../X`.
10
+ *
11
+ * Дозволені імпорти:
12
+ * - `./X`, `./sub/X` — same-dir чи глибше всередині самої `utils/`
13
+ * - bare-package (`oxc-parser`, `@scope/pkg`) — npm-залежність
14
+ * - `node:fs`, `fs` тощо — Node-builtin
15
+ *
16
+ * Заборонено:
17
+ * - будь-який `..`-шлях (`../X`, `../../X`) — це порушення granular `utils/`-кордону
18
+ */
19
+ import { readdir, readFile } from 'node:fs/promises'
20
+ import { join, relative, sep } from 'node:path'
21
+
22
+ import { parseSync } from 'oxc-parser'
23
+
24
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
25
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
26
+ import { getMonorepoPackageRootDirs } from '../../../scripts/lib/workspaces.mjs'
27
+ import {
28
+ dynamicImportModule,
29
+ langFromPath,
30
+ requireCallModule,
31
+ walkAstWithAncestors
32
+ } from '../../../scripts/utils/ast-scan-utils.mjs'
33
+
34
+ const JS_SOURCE_RE = /\.(?:[cm]?[jt]sx?)$/u
35
+ const TEST_FILE_RE = /\.test\.[cm]?[jt]sx?$/u
36
+ const PARENT_RELATIVE_RE = /^\.\.(?:\/|$)/u
37
+ const SKIP_DIR_NAMES = new Set(['node_modules', '.git', 'dist', 'coverage', '.turbo', '.next', '__fixtures__'])
38
+
39
+ /**
40
+ * Чи каталог `dir` входить у список ignore (точний збіг або префікс).
41
+ * @param {string} dir абсолютний posix-шлях
42
+ * @param {string[]} ignorePaths абсолютні posix-шляхи з .n-cursor.json
43
+ * @returns {boolean} true — пропускаємо
44
+ */
45
+ function isIgnored(dir, ignorePaths) {
46
+ for (const p of ignorePaths) {
47
+ if (dir === p || dir.startsWith(`${p}/`)) return true
48
+ }
49
+ return false
50
+ }
51
+
52
+ /**
53
+ * Рекурсивно знаходить усі каталоги з ім'ям `utils` під `root` (пропускаючи типові артефакти).
54
+ * @param {string} root корінь
55
+ * @param {string[]} ignorePosix абс. posix-шляхи з ignore-конфігу
56
+ * @returns {Promise<string[]>} абсолютні шляхи знайдених `utils/`-каталогів
57
+ */
58
+ async function findUtilsDirs(root, ignorePosix) {
59
+ const found = []
60
+ /**
61
+ * @param {string} dir поточний каталог обходу
62
+ */
63
+ async function walk(dir) {
64
+ let entries
65
+ try {
66
+ entries = await readdir(dir, { withFileTypes: true })
67
+ } catch {
68
+ return
69
+ }
70
+ for (const entry of entries) {
71
+ if (!entry.isDirectory()) continue
72
+ if (SKIP_DIR_NAMES.has(entry.name)) continue
73
+ const full = join(dir, entry.name)
74
+ const posix = full.split(sep).join('/')
75
+ if (isIgnored(posix, ignorePosix)) continue
76
+ if (entry.name === 'utils') {
77
+ found.push(full)
78
+ continue
79
+ }
80
+ await walk(full)
81
+ }
82
+ }
83
+ await walk(root)
84
+ return found
85
+ }
86
+
87
+ /**
88
+ * Збирає всі не-тестові `.[cm]?[jt]sx?` файли під `utilsDir` (включно з підкаталогами,
89
+ * крім `tests/` і `__fixtures__/`).
90
+ * @param {string} utilsDir абсолютний шлях `utils/`
91
+ * @returns {Promise<string[]>} абсолютні шляхи джерел
92
+ */
93
+ async function collectUtilsSources(utilsDir) {
94
+ const out = []
95
+ /**
96
+ * @param {string} dir поточний каталог
97
+ */
98
+ async function walk(dir) {
99
+ let entries
100
+ try {
101
+ entries = await readdir(dir, { withFileTypes: true })
102
+ } catch {
103
+ return
104
+ }
105
+ for (const entry of entries) {
106
+ const full = join(dir, entry.name)
107
+ if (entry.isDirectory()) {
108
+ if (entry.name === 'tests' || entry.name === '__fixtures__' || SKIP_DIR_NAMES.has(entry.name)) continue
109
+ await walk(full)
110
+ continue
111
+ }
112
+ if (entry.isFile() && JS_SOURCE_RE.test(entry.name) && !TEST_FILE_RE.test(entry.name)) {
113
+ out.push(full)
114
+ }
115
+ }
116
+ }
117
+ await walk(utilsDir)
118
+ return out
119
+ }
120
+
121
+ /**
122
+ * Витягає з джерела всі рядкові імпорт-source (статичні, динамічні, require). Помилки парсера
123
+ * не падають — спочатку треба полагодити синтаксис, потім перезапустити концерн.
124
+ * @param {string} source текст файлу
125
+ * @param {string} filePath шлях (для вибору мови)
126
+ * @returns {string[]} список source-рядків, як вони задані в коді
127
+ */
128
+ function extractImportSources(source, filePath) {
129
+ /** @type {string[]} */
130
+ const sources = []
131
+ let parsed
132
+ try {
133
+ parsed = parseSync(filePath, source, { lang: langFromPath(filePath) })
134
+ } catch {
135
+ return sources
136
+ }
137
+ const staticImports = parsed?.module?.staticImports ?? []
138
+ for (const imp of staticImports) {
139
+ if (typeof imp?.moduleRequest?.value === 'string') {
140
+ sources.push(imp.moduleRequest.value)
141
+ }
142
+ }
143
+ const program = parsed?.program
144
+ if (program && typeof program === 'object') {
145
+ walkAstWithAncestors(program, [], node => {
146
+ const dyn = dynamicImportModule(node)
147
+ if (dyn !== null) sources.push(dyn)
148
+ const req = requireCallModule(node)
149
+ if (req !== null) sources.push(req)
150
+ })
151
+ }
152
+ return sources
153
+ }
154
+
155
+ /**
156
+ * @returns {Promise<number>} 0 — усе чисто, 1 — знайдено заборонені імпорти
157
+ */
158
+ export async function check() {
159
+ const reporter = createCheckReporter()
160
+ const root = process.cwd()
161
+ const ignorePaths = await loadCursorIgnorePaths(root)
162
+ const ignorePosix = ignorePaths.map(p => p.split(sep).join('/'))
163
+ const packageRoots = await getMonorepoPackageRootDirs(root)
164
+ /** @type {Set<string>} */
165
+ const utilsDirSet = new Set()
166
+ for (const pkgRel of packageRoots) {
167
+ const pkgAbs = pkgRel === '.' ? root : join(root, pkgRel)
168
+ for (const utilsDir of await findUtilsDirs(pkgAbs, ignorePosix)) {
169
+ utilsDirSet.add(utilsDir)
170
+ }
171
+ }
172
+ if (utilsDirSet.size === 0) {
173
+ reporter.pass('utils-каталогів немає — перевірку пропущено (js-lint.mdc)')
174
+ return reporter.getExitCode()
175
+ }
176
+ let violations = 0
177
+ let checkedFiles = 0
178
+ for (const utilsDir of utilsDirSet) {
179
+ const sources = await collectUtilsSources(utilsDir)
180
+ for (const file of sources) {
181
+ checkedFiles += 1
182
+ const content = await readFile(file, 'utf8')
183
+ const imports = extractImportSources(content, file)
184
+ for (const src of imports) {
185
+ if (PARENT_RELATIVE_RE.test(src)) {
186
+ const rel = relative(root, file)
187
+ reporter.fail(`${rel}: заборонений імпорт '${src}' — utils/-файли мають бути generic (js-lint.mdc)`)
188
+ violations += 1
189
+ }
190
+ }
191
+ }
192
+ }
193
+ if (violations === 0) {
194
+ reporter.pass(
195
+ `utils-каталогів: ${utilsDirSet.size}, перевірено ${checkedFiles} файлів — domain-bound імпортів немає (js-lint.mdc)`
196
+ )
197
+ }
198
+ return reporter.getExitCode()
199
+ }
@@ -89,6 +89,8 @@ version: '1.24'
89
89
 
90
90
  Швидкий тест: якщо файл завтра можна опублікувати окремим npm-пакетом без переписування — це `utils/`; якщо він тримає domain-state, читає конфіг проєкту або викликає зовнішні сервіси/файли — це `lib/`. Не плутай із чужими каталогами на кшталт `shared/` чи `common/`: канонічні назви — лише `utils/` і `lib/`.
91
91
 
92
+ Автоматично гарантується: `npx @nitra/cursor fix js-lint` (концерн `utils_imports`) обходить кожен `utils/`-каталог у воркспейсах і падає, якщо знаходить relative-імпорт з `..` (вихід за межі каталогу) у будь-якому не-тестовому файлі. Це механічне втілення правила «utils не знає про домен»: тільки same-dir (`./X`), bare-пакети та `node:*` дозволені; cross-rule, конфіги проєкту чи sibling-utils — fail. Якщо потрібна domain-залежність — перенеси файл у `lib/`.
93
+
92
94
  Додай workflow:
93
95
 
94
96
  ```yaml title=".github/workflows/lint-js.yml"
@@ -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 ./rules/js-lint/utils/rebuild-oxlint-canonical.mjs`,
5
+ * Після змін у TSV або скелеті запускай з каталогу пакета: `bun ./rules/js-lint/lib/rebuild-oxlint-canonical.mjs`,
6
6
  * потім скопіюй оновлений канон у корінь споживача як `.oxlintrc.json` за потреби.
7
7
  */
8
8
  import { readFileSync, writeFileSync } from 'node:fs'
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -12,7 +12,7 @@ import { existsSync } from 'node:fs'
12
12
  import { readFile } from 'node:fs/promises'
13
13
  import { join, relative } from 'node:path'
14
14
 
15
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
15
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
16
16
  import { findAllPackageJsonPaths } from '../../../scripts/utils/find-package-json-paths.mjs'
17
17
  import {
18
18
  findMssqlPerRequestConnectionInText,
@@ -22,8 +22,8 @@ import {
22
22
  findUnsafeMssqlInListUnparsedInText,
23
23
  findUnsafeMssqlInListMissingEmptyGuardInText,
24
24
  isMssqlScanSourceFile
25
- } from '../utils/mssql-pool-scan.mjs'
26
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
25
+ } from '../lib/mssql-pool-scan.mjs'
26
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
27
27
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
28
28
 
29
29
  const VERSION_PREFIX_RE = /^[\^~>=<]+\s*/u
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -40,24 +40,24 @@ import {
40
40
  findBunyanImportsInText,
41
41
  isBunyanScanSourceFile,
42
42
  shouldSkipFileForBunyanScan
43
- } from '../utils/bunyan-imports.mjs'
44
- import { findUncheckedProcessEnvInText, isCheckEnvScanSourceFile } from '../utils/check-env-scan.mjs'
45
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
46
- import { runConftestBatch } from '../../../scripts/utils/run-conftest-batch.mjs'
47
- import { findConnFileRuleViolations, isConnFileRulesSourceFile } from '../utils/conn-file-rules.mjs'
43
+ } from '../lib/bunyan-imports.mjs'
44
+ import { findUncheckedProcessEnvInText, isCheckEnvScanSourceFile } from '../lib/check-env-scan.mjs'
45
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
46
+ import { runConftestBatch } from '../../../scripts/lib/run-conftest-batch.mjs'
47
+ import { findConnFileRuleViolations, isConnFileRulesSourceFile } from '../lib/conn-file-rules.mjs'
48
48
  import {
49
49
  findConnFactoryImportsInText,
50
50
  isConnImportsScanSourceFile,
51
51
  isInsideConnDir,
52
52
  resolveConnDirFromPackageJson
53
- } from '../utils/conn-imports-scan.mjs'
54
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
53
+ } from '../lib/conn-imports-scan.mjs'
54
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
55
55
  import {
56
56
  findPromiseSetTimeoutInText,
57
57
  isPromiseSetTimeoutScanSourceFile
58
- } from '../utils/promise-settimeout-scan.mjs'
58
+ } from '../lib/promise-settimeout-scan.mjs'
59
59
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
60
- import { getMonorepoPackageRootDirs } from '../../../scripts/utils/workspaces.mjs'
60
+ import { getMonorepoPackageRootDirs } from '../../../scripts/lib/workspaces.mjs'
61
61
 
62
62
  /**
63
63
  * Чи існує непорожній за змістом маркер каталогу `src/` (рекомендована структура js-run).
package/rules/k8s/fix.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
1
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
2
2
 
3
3
  /**
4
4
  * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
5
  * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
- * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
7
  * @returns {Promise<number>} 0 — OK, 1 — порушення
8
8
  */
9
9
  export function run(ctx) {
@@ -13,7 +13,7 @@ export function run(ctx) {
13
13
  if (import.meta.main) {
14
14
  // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
15
  // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
- const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
16
+ const { runRuleCli } = await import('../../scripts/lib/run-rule-cli.mjs')
17
17
  // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
18
  process.exit(await runRuleCli(import.meta.dirname))
19
19
  }
@@ -140,9 +140,9 @@ import { basename, dirname, join, relative, resolve } from 'node:path'
140
140
 
141
141
  import { isSeq, parseAllDocuments, parseDocument } from 'yaml'
142
142
 
143
- import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
144
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
145
- import { runConftestBatch } from '../../../scripts/utils/run-conftest-batch.mjs'
143
+ import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
144
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
145
+ import { runConftestBatch } from '../../../scripts/lib/run-conftest-batch.mjs'
146
146
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
147
147
 
148
148
  /** Версія набору схем yannh — узгоджено з k8s.mdc */
@@ -24,10 +24,10 @@ import { basename, dirname, join, relative } from 'node:path'
24
24
  import { parse } from 'yaml'
25
25
 
26
26
  import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
27
- import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
27
+ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
28
28
  import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
29
29
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
30
- import { runStandardLint } from '../../../scripts/utils/run-standard-lint.mjs'
30
+ import { runStandardLint } from '../../../scripts/lib/run-standard-lint.mjs'
31
31
 
32
32
  /** Per-project kubescape exceptions file; підмішується через --exceptions, якщо існує в корені. */
33
33
  const KUBESCAPE_EXCEPTIONS_FILE = '.kubescape-exceptions.json'