@7n/rules-lang-js 0.3.1 → 0.4.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.
Files changed (258) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +14 -4
  3. package/rules/bun/bunfig/bunfig.mdc +17 -0
  4. package/rules/bun/bunfig/bunfig.rego +29 -0
  5. package/rules/bun/bunfig/concern.json +9 -0
  6. package/rules/bun/bunfig/template/bunfig.toml.snippet.toml +2 -0
  7. package/rules/bun/docs/index.md +11 -0
  8. package/rules/bun/layout/concern.json +16 -0
  9. package/rules/bun/layout/docs/fix-layout.md +29 -0
  10. package/rules/bun/layout/docs/main.md +34 -0
  11. package/rules/bun/layout/fix-layout.mjs +63 -0
  12. package/rules/bun/layout/layout.mdc +60 -0
  13. package/rules/bun/layout/main.mjs +53 -0
  14. package/rules/bun/licensee/concern.json +7 -0
  15. package/rules/bun/licensee/docs/fix-licensee.md +27 -0
  16. package/rules/bun/licensee/docs/index.md +12 -0
  17. package/rules/bun/licensee/docs/main.md +30 -0
  18. package/rules/bun/licensee/fix-licensee.mjs +31 -0
  19. package/rules/bun/licensee/main.mjs +68 -0
  20. package/rules/bun/lint-surface/concern.json +3 -0
  21. package/rules/bun/lint-surface/lint-surface.mdc +13 -0
  22. package/rules/bun/main.json +1 -0
  23. package/rules/bun/main.mdc +11 -0
  24. package/rules/bun/package_json/concern.json +9 -0
  25. package/rules/bun/package_json/docs/fix-package_json.md +28 -0
  26. package/rules/bun/package_json/docs/index.md +9 -0
  27. package/rules/bun/package_json/fix-package_json.mjs +311 -0
  28. package/rules/bun/package_json/package_json.mdc +14 -0
  29. package/rules/bun/package_json/package_json.rego +64 -0
  30. package/rules/bun/package_json/template/package.json.deny.json +4 -0
  31. package/rules/js/check/check.mdc +26 -0
  32. package/rules/js/check/concern.json +19 -0
  33. package/rules/js/check/docs/eslint-config.md +56 -0
  34. package/rules/js/check/docs/fix-check.md +48 -0
  35. package/rules/js/check/docs/index.md +13 -0
  36. package/rules/js/check/docs/main.md +48 -0
  37. package/rules/js/check/eslint-config.mjs +262 -0
  38. package/rules/js/check/fix-check.mjs +82 -0
  39. package/rules/js/check/main.mjs +310 -0
  40. package/rules/js/dep-policy/concern.json +4 -0
  41. package/rules/js/dep-policy/dep-policy.mdc +36 -0
  42. package/rules/js/dep-policy/docs/main.md +35 -0
  43. package/rules/js/dep-policy/main.mjs +99 -0
  44. package/rules/js/docs/index.md +11 -0
  45. package/rules/js/eslint/concern.json +8 -0
  46. package/rules/js/eslint/docs/fix-eslint.md +50 -0
  47. package/rules/js/eslint/docs/fix-worker.md +31 -0
  48. package/rules/js/eslint/docs/index.md +11 -0
  49. package/rules/js/eslint/docs/main.md +35 -0
  50. package/rules/js/eslint/fix-eslint.mjs +160 -0
  51. package/rules/js/eslint/fix-worker.mjs +139 -0
  52. package/rules/js/eslint/main.mjs +115 -0
  53. package/rules/js/file-extensions/concern.json +3 -0
  54. package/rules/js/file-extensions/file-extensions.mdc +12 -0
  55. package/rules/js/jscpd_config/concern.json +11 -0
  56. package/rules/js/jscpd_config/docs/fix-jscpd_config.md +25 -0
  57. package/rules/js/jscpd_config/docs/index.md +9 -0
  58. package/rules/js/jscpd_config/fix-jscpd_config.mjs +3 -0
  59. package/rules/js/jscpd_config/jscpd_config.mdc +42 -0
  60. package/rules/js/jscpd_config/jscpd_config.rego +44 -0
  61. package/rules/js/jscpd_config/template/.jscpd.json.snippet.json +7 -0
  62. package/rules/js/jscpd_duplicates/concern.json +7 -0
  63. package/rules/js/jscpd_duplicates/docs/main.md +29 -0
  64. package/rules/js/jscpd_duplicates/main.mjs +69 -0
  65. package/rules/js/knip/concern.json +7 -0
  66. package/rules/js/knip/docs/main.md +31 -0
  67. package/rules/js/knip/knip.mdc +15 -0
  68. package/rules/js/knip/main.mjs +89 -0
  69. package/rules/js/lint-findings/concern.json +3 -0
  70. package/rules/js/lint-findings/docs/main.md +40 -0
  71. package/rules/js/lint-findings/main.mjs +125 -0
  72. package/rules/js/main.json +1 -0
  73. package/rules/js/main.mdc +18 -0
  74. package/rules/js/package_json/concern.json +9 -0
  75. package/rules/js/package_json/docs/fix-package_json.md +25 -0
  76. package/rules/js/package_json/docs/index.md +9 -0
  77. package/rules/js/package_json/fix-package_json.mjs +3 -0
  78. package/rules/js/package_json/package_json.mdc +15 -0
  79. package/rules/js/package_json/package_json.rego +142 -0
  80. package/rules/js/package_json/template/package.json.snippet.json +6 -0
  81. package/rules/js/tooling/concern.json +3 -0
  82. package/rules/js/tooling/data/tooling/knip-canonical.json +30 -0
  83. package/rules/js/tooling/data/tooling/oxlint-canonical.json +400 -0
  84. package/rules/js/tooling/docs/main.md +53 -0
  85. package/rules/js/tooling/main.mjs +183 -0
  86. package/rules/js/utils_imports/concern.json +4 -0
  87. package/rules/js/utils_imports/docs/main.md +50 -0
  88. package/rules/js/utils_imports/main.mjs +185 -0
  89. package/rules/js/utils_imports/utils_imports.mdc +15 -0
  90. package/rules/js/vscode_extensions/concern.json +11 -0
  91. package/rules/js/vscode_extensions/docs/fix-vscode_extensions.md +24 -0
  92. package/rules/js/vscode_extensions/docs/index.md +11 -0
  93. package/rules/js/vscode_extensions/fix-vscode_extensions.mjs +1 -0
  94. package/rules/js/vscode_extensions/template/extensions.json.snippet.json +6 -0
  95. package/rules/js/vscode_extensions/vscode_extensions.mdc +11 -0
  96. package/rules/js/vscode_extensions/vscode_extensions.rego +12 -0
  97. package/rules/js-bun-db/connection/concern.json +3 -0
  98. package/rules/js-bun-db/connection/connection.mdc +42 -0
  99. package/rules/js-bun-db/docs/index.md +11 -0
  100. package/rules/js-bun-db/lib/bun-sql-scan.mjs +1047 -0
  101. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +63 -0
  102. package/rules/js-bun-db/lib/docs/index.md +11 -0
  103. package/rules/js-bun-db/main.json +1 -0
  104. package/rules/js-bun-db/main.mdc +8 -0
  105. package/rules/js-bun-db/package_json/concern.json +9 -0
  106. package/rules/js-bun-db/package_json/package_json.mdc +31 -0
  107. package/rules/js-bun-db/package_json/package_json.rego +15 -0
  108. package/rules/js-bun-db/package_json/template/package.json.deny.json +6 -0
  109. package/rules/js-bun-db/pg_format_identifiers/concern.json +3 -0
  110. package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +104 -0
  111. package/rules/js-bun-db/safety/concern.json +4 -0
  112. package/rules/js-bun-db/safety/docs/main.md +34 -0
  113. package/rules/js-bun-db/safety/main.mjs +430 -0
  114. package/rules/js-bun-db/safety/safety.mdc +458 -0
  115. package/rules/js-bun-redis/docs/index.md +11 -0
  116. package/rules/js-bun-redis/imports/concern.json +4 -0
  117. package/rules/js-bun-redis/imports/docs/main.md +36 -0
  118. package/rules/js-bun-redis/imports/imports.mdc +47 -0
  119. package/rules/js-bun-redis/imports/main.mjs +88 -0
  120. package/rules/js-bun-redis/lib/docs/index.md +11 -0
  121. package/rules/js-bun-redis/lib/docs/redis-imports.md +227 -0
  122. package/rules/js-bun-redis/lib/redis-imports.mjs +130 -0
  123. package/rules/js-bun-redis/main.json +1 -0
  124. package/rules/js-bun-redis/main.mdc +8 -0
  125. package/rules/js-bun-redis/package_json/concern.json +9 -0
  126. package/rules/js-bun-redis/package_json/package_json.mdc +11 -0
  127. package/rules/js-bun-redis/package_json/package_json.rego +15 -0
  128. package/rules/js-bun-redis/package_json/template/package.json.deny.json +12 -0
  129. package/rules/js-mssql/deps/concern.json +4 -0
  130. package/rules/js-mssql/deps/docs/main.md +33 -0
  131. package/rules/js-mssql/deps/main.mjs +297 -0
  132. package/rules/js-mssql/docs/index.md +11 -0
  133. package/rules/js-mssql/lib/docs/index.md +11 -0
  134. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +380 -0
  135. package/rules/js-mssql/lib/mssql-pool-scan.mjs +610 -0
  136. package/rules/js-mssql/main.json +1 -0
  137. package/rules/js-mssql/main.mdc +144 -0
  138. package/rules/js-mssql/mssql-tvp/concern.json +3 -0
  139. package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +77 -0
  140. package/rules/js-mssql/package_json/concern.json +9 -0
  141. package/rules/js-mssql/package_json/package_json.mdc +9 -0
  142. package/rules/js-mssql/package_json/package_json.rego +57 -0
  143. package/rules/js-run/configmap/concern.json +9 -0
  144. package/rules/js-run/configmap/configmap.mdc +37 -0
  145. package/rules/js-run/configmap/configmap.rego +21 -0
  146. package/rules/js-run/configmap/template/configmap.yaml.contains.yml +4 -0
  147. package/rules/js-run/docs/index.md +11 -0
  148. package/rules/js-run/jsconfig/concern.json +9 -0
  149. package/rules/js-run/jsconfig/docs/fix-jsconfig.md +28 -0
  150. package/rules/js-run/jsconfig/docs/index.md +9 -0
  151. package/rules/js-run/jsconfig/fix-jsconfig.mjs +119 -0
  152. package/rules/js-run/jsconfig/jsconfig.mdc +48 -0
  153. package/rules/js-run/jsconfig/jsconfig.rego +59 -0
  154. package/rules/js-run/jsconfig/template/jsconfig.json.snippet.json +10 -0
  155. package/rules/js-run/lib/bunyan-imports.mjs +98 -0
  156. package/rules/js-run/lib/check-env-scan.mjs +338 -0
  157. package/rules/js-run/lib/conn-file-rules.mjs +214 -0
  158. package/rules/js-run/lib/conn-imports-scan.mjs +154 -0
  159. package/rules/js-run/lib/docs/bunyan-imports.md +121 -0
  160. package/rules/js-run/lib/docs/check-env-scan.md +438 -0
  161. package/rules/js-run/lib/docs/conn-file-rules.md +304 -0
  162. package/rules/js-run/lib/docs/conn-imports-scan.md +208 -0
  163. package/rules/js-run/lib/docs/index.md +16 -0
  164. package/rules/js-run/lib/docs/promise-settimeout-scan.md +334 -0
  165. package/rules/js-run/lib/docs/temporal-scan.md +29 -0
  166. package/rules/js-run/lib/promise-settimeout-scan.mjs +128 -0
  167. package/rules/js-run/lib/temporal-scan.mjs +52 -0
  168. package/rules/js-run/main.json +1 -0
  169. package/rules/js-run/main.mdc +16 -0
  170. package/rules/js-run/package_json/concern.json +9 -0
  171. package/rules/js-run/package_json/package_json.mdc +44 -0
  172. package/rules/js-run/package_json/package_json.rego +37 -0
  173. package/rules/js-run/package_json/template/package.json.deny.json +22 -0
  174. package/rules/js-run/project-structure/concern.json +3 -0
  175. package/rules/js-run/project-structure/project-structure.mdc +11 -0
  176. package/rules/js-run/runtime/concern.json +12 -0
  177. package/rules/js-run/runtime/docs/fix-runtime.md +27 -0
  178. package/rules/js-run/runtime/docs/main.md +35 -0
  179. package/rules/js-run/runtime/fix-runtime.mjs +46 -0
  180. package/rules/js-run/runtime/main.mjs +496 -0
  181. package/rules/js-run/runtime/runtime.mdc +184 -0
  182. package/rules/js-run/scope/concern.json +3 -0
  183. package/rules/js-run/scope/scope.mdc +11 -0
  184. package/rules/npm-module/docs/index.md +11 -0
  185. package/rules/npm-module/emit_types_config/concern.json +9 -0
  186. package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +25 -0
  187. package/rules/npm-module/emit_types_config/docs/index.md +9 -0
  188. package/rules/npm-module/emit_types_config/emit_types_config.mdc +43 -0
  189. package/rules/npm-module/emit_types_config/emit_types_config.rego +28 -0
  190. package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +5 -0
  191. package/rules/npm-module/emit_types_config/template/tsconfig.emit-types.json.snippet.json +9 -0
  192. package/rules/npm-module/header_doc_pointer/concern.json +5 -0
  193. package/rules/npm-module/header_doc_pointer/docs/main.md +40 -0
  194. package/rules/npm-module/header_doc_pointer/header_doc_pointer.mdc +18 -0
  195. package/rules/npm-module/header_doc_pointer/main.mjs +131 -0
  196. package/rules/npm-module/main.json +1 -0
  197. package/rules/npm-module/main.mdc +36 -0
  198. package/rules/npm-module/npm_package_json/concern.json +9 -0
  199. package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +26 -0
  200. package/rules/npm-module/npm_package_json/docs/index.md +9 -0
  201. package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +5 -0
  202. package/rules/npm-module/npm_package_json/npm_package_json.mdc +57 -0
  203. package/rules/npm-module/npm_package_json/npm_package_json.rego +73 -0
  204. package/rules/npm-module/npm_package_json/template/package.json.snippet.json +1 -0
  205. package/rules/npm-module/package_structure/concern.json +5 -0
  206. package/rules/npm-module/package_structure/docs/main.md +37 -0
  207. package/rules/npm-module/package_structure/main.mjs +448 -0
  208. package/rules/npm-module/package_structure/package_structure.mdc +63 -0
  209. package/rules/npm-module/root_package_json/concern.json +9 -0
  210. package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +25 -0
  211. package/rules/npm-module/root_package_json/docs/index.md +9 -0
  212. package/rules/npm-module/root_package_json/fix-root_package_json.mjs +5 -0
  213. package/rules/npm-module/root_package_json/root_package_json.mdc +41 -0
  214. package/rules/npm-module/root_package_json/root_package_json.rego +28 -0
  215. package/rules/npm-module/root_package_json/template/package.json.snippet.json +1 -0
  216. package/rules/npm-module/rule_meta/concern.json +8 -0
  217. package/rules/npm-module/rule_meta/docs/main.md +35 -0
  218. package/rules/npm-module/rule_meta/main.mjs +119 -0
  219. package/rules/npm-module/rule_meta/rule_meta.mdc +11 -0
  220. package/rules/npm-module/skill_meta/concern.json +5 -0
  221. package/rules/npm-module/skill_meta/docs/main.md +149 -0
  222. package/rules/npm-module/skill_meta/main.mjs +91 -0
  223. package/rules/npm-module/skill_meta/skill_meta.mdc +11 -0
  224. package/rules/tool-surface/docs/index.md +11 -0
  225. package/rules/tool-surface/main.json +6 -0
  226. package/rules/tool-surface/main.mdc +72 -0
  227. package/rules/vue/composition-api/composition-api.mdc +82 -0
  228. package/rules/vue/composition-api/concern.json +3 -0
  229. package/rules/vue/docs/index.md +11 -0
  230. package/rules/vue/lib/docs/index.md +11 -0
  231. package/rules/vue/lib/docs/vue-forbidden-imports.md +265 -0
  232. package/rules/vue/lib/vue-forbidden-imports.mjs +240 -0
  233. package/rules/vue/main.json +1 -0
  234. package/rules/vue/main.mdc +18 -0
  235. package/rules/vue/nheader-layout/concern.json +3 -0
  236. package/rules/vue/nheader-layout/nheader-layout.mdc +171 -0
  237. package/rules/vue/package_json/concern.json +9 -0
  238. package/rules/vue/package_json/package_json.mdc +30 -0
  239. package/rules/vue/package_json/package_json.rego +140 -0
  240. package/rules/vue/packages/concern.json +6 -0
  241. package/rules/vue/packages/docs/index.md +11 -0
  242. package/rules/vue/packages/docs/main.md +35 -0
  243. package/rules/vue/packages/main.mjs +575 -0
  244. package/rules/vue/packages/packages.mdc +56 -0
  245. package/rules/vue/quasar-ui/concern.json +3 -0
  246. package/rules/vue/quasar-ui/quasar-ui.mdc +32 -0
  247. package/rules/vue/structure/concern.json +3 -0
  248. package/rules/vue/structure/structure.mdc +101 -0
  249. package/rules/vue/testing/concern.json +3 -0
  250. package/rules/vue/testing/testing.mdc +40 -0
  251. package/rules/vue/tfm-translations/concern.json +7 -0
  252. package/rules/vue/tfm-translations/docs/main.md +29 -0
  253. package/rules/vue/tfm-translations/main.mjs +55 -0
  254. package/rules/vue/tfm-translations/tfm-translations.mdc +32 -0
  255. package/rules/vue/vite-config/concern.json +3 -0
  256. package/rules/vue/vite-config/vite-config.mdc +153 -0
  257. package/rules/vue/vite-env/concern.json +3 -0
  258. package/rules/vue/vite-env/vite-env.mdc +61 -0
@@ -0,0 +1,50 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: plugins/lang-js/rules/js/utils_imports/main.mjs
5
+ docgen:
6
+ crc: a705c81d
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ issues: judge:inaccurate:0.98
10
+ judgeModel: openai-codex/gpt-5.4-mini
11
+ ---
12
+
13
+ ## Огляд
14
+
15
+ Огляд
16
+ Сканує всі каталоги `utils` у монорепозиторії, виявляючи джерела коду, уникаючи стандартних артефактів. Аналізує кожен рядковий імпорт, витягнутий із коду, порівнюючи його з шаблоном імпорту з батьківського каталогу. При виявленні невідповідностей фіксує помилки, вказуючи відносний шлях до файлу.
17
+
18
+ Поведінка
19
+
20
+ 1. Ініціалізація перевірки відбувається через виклик функції `main`, яка працює в fail-safe режимі та перехоплює помилки, не викидаючи винятків назовні.
21
+ 2. Робочий каталог системи слугує коренем репозиторію.
22
+ 3. Конфігураційні шляхи, які мають бути виключені з перевірки (наприклад, `.git`, `node_modules`), завантажуються з файлу .n-rules.json.
23
+ 4. Визначаються кореневі каталоги монорепозиторію.
24
+ 5. Проводиться пошук усіх каталогів `utils` у всіх пакетах монорепозиторію, при цьому ігноруються каталоги, які є типовими артефактами (наприклад, `node_modules`, `.git`, `dist`, `coverage`, `.turbo`, `.next`, `__fixtures__`).
25
+ 6. Для кожного знайденого каталогу `utils` збирається повний набір відповідних джерел коду, виходячи за межі підкаталогів `tests/` та `__fixtures__/`.
26
+ 7. Вміст кожного зібраного джерела коду зчитується з файлів.
27
+ 8. Використовується парсер для аналізу синтаксису з метою витягування всіх рядкових імпортів із коду.
28
+ 9. Кожен витягнутий імпорт піддається перевірці на відповідність шаблону, що визначає імпорт з батьківського каталогу.
29
+ 10. Якщо імпорт заборонений, фіксується помилка, яка вказує на відносний шлях до файлу, і реєструється обов'язковий маркер повідомлення (js.mdc).
30
+ 11. Після завершення перевірки всіх файлів, якщо жодних порушень не знайдено, реєструється успішне завершення (js.mdc).
31
+
32
+ ## Поведінка
33
+
34
+ 1. Викликається функція `main` для ініціалізації перевірки.
35
+ 2. Системний робочий каталог використовується для визначення кореня репозиторію.
36
+ 3. Завантажуються конфігураційні шляхи, які ігнорують перевірку, з файлу .n-rules.json.
37
+ 4. Знаходяться кореневі каталоги монорепозиторію.
38
+ 5. Пошукаються всі каталоги з іменем `utils` у всіх пакетах монорепозиторію, при цьому пропускаються типові артефакти (наприклад, node_modules, .git, dist, coverage, .turbo, .next, **fixtures**).
39
+ 6. Для кожного знайденого каталогу `utils` збираються всі відповідні джерела коду, виходячи за межі підкаталогів `tests/` та `__fixtures__/`.
40
+ 7. Для кожного зібраного джерела коду зчитується вміст файлу.
41
+ 8. Витягуються всі рядкові імпорти з коду, використовуючи парсер для аналізу синтаксису.
42
+ 9. Кожен витягнутий імпорт перевіряється на відповідність шаблону, що вказує на імпорт з батьківського каталогу.
43
+ 10. Якщо імпорт заборонений, фіксується помилка, яка вказує на відносний шлях до файлу.
44
+ 11. Після перевірки усіх файлів, якщо порушень не знайдено, реєструється успішне завершення (js.mdc).
45
+
46
+ ## Гарантії поведінки
47
+
48
+ - Read-only: не виконує операцій запису (ФС/БД).
49
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
50
+ - Свідомо пропускає шляхи: `.git`, `node_modules`.
@@ -0,0 +1,185 @@
1
+ /** @see ./docs/utils_imports.md */
2
+ import { readdir, readFile } from 'node:fs/promises'
3
+ import { join, relative, sep } from 'node:path'
4
+
5
+ import { parseSync } from 'oxc-parser'
6
+
7
+ import { createViolationReporter } from '@7n/rules/scripts/lib/lint-surface/violation-reporter.mjs'
8
+ import { loadCursorIgnorePaths } from '@7n/rules/scripts/lib/load-cursor-config.mjs'
9
+ import { getMonorepoPackageRootDirs } from '@7n/rules/scripts/lib/workspaces.mjs'
10
+ import {
11
+ dynamicImportModule,
12
+ langFromPath,
13
+ requireCallModule,
14
+ walkAstWithAncestors
15
+ } from '@7n/rules/scripts/utils/ast-scan-utils.mjs'
16
+
17
+ const JS_SOURCE_RE = /\.(?:[cm]?[jt]sx?)$/u
18
+ const TEST_FILE_RE = /\.test\.[cm]?[jt]sx?$/u
19
+ const PARENT_RELATIVE_RE = /^\.\.(?:\/|$)/u
20
+ const SKIP_DIR_NAMES = new Set(['node_modules', '.git', 'dist', 'coverage', '.turbo', '.next', '__fixtures__'])
21
+
22
+ /**
23
+ * Чи каталог `dir` входить у список ignore (точний збіг або префікс).
24
+ * @param {string} dir абсолютний posix-шлях
25
+ * @param {string[]} ignorePaths абсолютні posix-шляхи з .n-rules.json
26
+ * @returns {boolean} true — пропускаємо
27
+ */
28
+ function isIgnored(dir, ignorePaths) {
29
+ for (const p of ignorePaths) {
30
+ if (dir === p || dir.startsWith(`${p}/`)) return true
31
+ }
32
+ return false
33
+ }
34
+
35
+ /**
36
+ * Рекурсивно знаходить усі каталоги з ім'ям `utils` під `root` (пропускаючи типові артефакти).
37
+ * @param {string} root корінь
38
+ * @param {string[]} ignorePosix абс. posix-шляхи з ignore-конфігу
39
+ * @returns {Promise<string[]>} абсолютні шляхи знайдених `utils/`-каталогів
40
+ */
41
+ async function findUtilsDirs(root, ignorePosix) {
42
+ const found = []
43
+ /**
44
+ * @param {string} dir поточний каталог обходу
45
+ */
46
+ async function walk(dir) {
47
+ let entries
48
+ try {
49
+ entries = await readdir(dir, { withFileTypes: true })
50
+ } catch {
51
+ return
52
+ }
53
+ for (const entry of entries) {
54
+ if (!entry.isDirectory()) continue
55
+ if (SKIP_DIR_NAMES.has(entry.name)) continue
56
+ const full = join(dir, entry.name)
57
+ const posix = full.split(sep).join('/')
58
+ if (isIgnored(posix, ignorePosix)) continue
59
+ if (entry.name === 'utils') {
60
+ found.push(full)
61
+ continue
62
+ }
63
+ await walk(full)
64
+ }
65
+ }
66
+ await walk(root)
67
+ return found
68
+ }
69
+
70
+ /**
71
+ * Збирає всі не-тестові `.[cm]?[jt]sx?` файли під `utilsDir` (включно з підкаталогами,
72
+ * крім `tests/` і `__fixtures__/`).
73
+ * @param {string} utilsDir абсолютний шлях `utils/`
74
+ * @returns {Promise<string[]>} абсолютні шляхи джерел
75
+ */
76
+ async function collectUtilsSources(utilsDir) {
77
+ const out = []
78
+ /**
79
+ * @param {string} dir поточний каталог
80
+ */
81
+ async function walk(dir) {
82
+ let entries
83
+ try {
84
+ entries = await readdir(dir, { withFileTypes: true })
85
+ } catch {
86
+ return
87
+ }
88
+ for (const entry of entries) {
89
+ const full = join(dir, entry.name)
90
+ if (entry.isDirectory()) {
91
+ if (entry.name === 'tests' || entry.name === '__fixtures__' || SKIP_DIR_NAMES.has(entry.name)) continue
92
+ await walk(full)
93
+ continue
94
+ }
95
+ if (entry.isFile() && JS_SOURCE_RE.test(entry.name) && !TEST_FILE_RE.test(entry.name)) {
96
+ out.push(full)
97
+ }
98
+ }
99
+ }
100
+ await walk(utilsDir)
101
+ return out
102
+ }
103
+
104
+ /**
105
+ * Витягає з джерела всі рядкові імпорт-source (статичні, динамічні, require). Помилки парсера
106
+ * не падають — спочатку треба полагодити синтаксис, потім перезапустити концерн.
107
+ * @param {string} source текст файлу
108
+ * @param {string} filePath шлях (для вибору мови)
109
+ * @returns {string[]} список source-рядків, як вони задані в коді
110
+ */
111
+ function extractImportSources(source, filePath) {
112
+ /** @type {string[]} */
113
+ const sources = []
114
+ let parsed
115
+ try {
116
+ parsed = parseSync(filePath, source, { lang: langFromPath(filePath) })
117
+ } catch {
118
+ return sources
119
+ }
120
+ const staticImports = parsed?.module?.staticImports ?? []
121
+ for (const imp of staticImports) {
122
+ if (typeof imp?.moduleRequest?.value === 'string') {
123
+ sources.push(imp.moduleRequest.value)
124
+ }
125
+ }
126
+ const program = parsed?.program
127
+ if (program && typeof program === 'object') {
128
+ walkAstWithAncestors(program, [], node => {
129
+ const dyn = dynamicImportModule(node)
130
+ if (dyn !== null) sources.push(dyn)
131
+ const req = requireCallModule(node)
132
+ if (req !== null) sources.push(req)
133
+ })
134
+ }
135
+ return sources
136
+ }
137
+
138
+ /**
139
+ * @param {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
140
+ * @returns {Promise<import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintResult>} результат лінту
141
+ */
142
+ export async function lint(ctx) {
143
+ const reporter = createViolationReporter(ctx)
144
+ const root = ctx.cwd
145
+ const ignorePaths = await loadCursorIgnorePaths(root)
146
+ const ignorePosix = ignorePaths.map(p => p.split(sep).join('/'))
147
+ const packageRoots = await getMonorepoPackageRootDirs(root)
148
+ /** @type {Set<string>} */
149
+ const utilsDirSet = new Set()
150
+ for (const pkgRel of packageRoots) {
151
+ const pkgAbs = pkgRel === '.' ? root : join(root, pkgRel)
152
+ for (const utilsDir of await findUtilsDirs(pkgAbs, ignorePosix)) {
153
+ utilsDirSet.add(utilsDir)
154
+ }
155
+ }
156
+ if (utilsDirSet.size === 0) {
157
+ reporter.pass('utils-каталогів немає — перевірку пропущено (js.mdc)')
158
+ return reporter.result()
159
+ }
160
+ let violations = 0
161
+ let checkedFiles = 0
162
+ for (const utilsDir of utilsDirSet) {
163
+ const sources = await collectUtilsSources(utilsDir)
164
+ for (const file of sources) {
165
+ checkedFiles += 1
166
+ const content = await readFile(file, 'utf8')
167
+ const imports = extractImportSources(content, file)
168
+ for (const src of imports) {
169
+ if (!PARENT_RELATIVE_RE.test(src)) {
170
+ continue
171
+ }
172
+
173
+ const rel = relative(root, file)
174
+ reporter.fail(`${rel}: заборонений імпорт '${src}' — utils/-файли мають бути generic (js.mdc)`)
175
+ violations += 1
176
+ }
177
+ }
178
+ }
179
+ if (violations === 0) {
180
+ reporter.pass(
181
+ `utils-каталогів: ${utilsDirSet.size}, перевірено ${checkedFiles} файлів — domain-bound імпортів немає (js.mdc)`
182
+ )
183
+ }
184
+ return reporter.result()
185
+ }
@@ -0,0 +1,15 @@
1
+ ## Структура спільних модулів: `utils/` vs `lib/`
2
+
3
+ Коли спільні методи виносяться з кількох JS-файлів в окремий каталог — назву каталога обирай за призначенням модулів усередині.
4
+
5
+ - **`utils/`** — низькорівневі, чисті, generic helpers без бізнес-логіки і без залежностей від домену проєкту. Те, що могло б жити в окремому npm-пакеті. Приклади: `formatDate()`, `chunk(array, size)`, `retry(fn, opts)`, `deepMerge()`, `parseDuration('5m')`, `sleep(ms)`.
6
+
7
+ - **`lib/`** — внутрішні модулі / підсистеми проєкту, які знають про домен. Більші за `utils/`, часто з власним state, конфігом або side effects. Приклади: `lib/gmail-client.js`, `lib/circuit-breaker.js`, `lib/skill-loader.js`, `lib/safety/dry-run.js`.
8
+
9
+ Швидкий тест: якщо файл завтра можна опублікувати окремим npm-пакетом без переписування — це `utils/`; якщо він тримає domain-state, читає конфіг проєкту або викликає зовнішні сервіси/файли — це `lib/`. Не плутай із чужими каталогами на кшталт `shared/` чи `common/`: канонічні назви — лише `utils/` і `lib/`.
10
+
11
+ ### Автоматична перевірка імпортів у `utils/`
12
+
13
+ `npx @7n/rules lint js` (концерн `utils_imports`) обходить кожен `utils/`-каталог у воркспейсах і падає, якщо знаходить relative-імпорт з `..` (вихід за межі каталогу) у будь-якому не-тестовому файлі. Це механічне втілення правила «utils не знає про домен»: тільки same-dir (`./X`), bare-пакети та `node:*` дозволені; cross-rule, конфіги проєкту чи sibling-utils — fail.
14
+
15
+ Якщо потрібна domain-залежність — перенеси файл у `lib/`.
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "fixability": "config",
4
+ "policy": {
5
+ "files": {
6
+ "single": ".vscode/extensions.json",
7
+ "required": true
8
+ },
9
+ "missingMessage": ".vscode/extensions.json не існує — додай recommendations з js.mdc"
10
+ }
11
+ }
@@ -0,0 +1,24 @@
1
+ ---
2
+ type: JS Module
3
+ title: fix-vscode_extensions.mjs
4
+ resource: plugins/lang-js/rules/js/vscode_extensions/fix-vscode_extensions.mjs
5
+ docgen:
6
+ crc: b6cffd44
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ issues: judge:inaccurate:0.99
10
+ judgeModel: openai-codex/gpt-5.4-mini
11
+ ---
12
+
13
+ ## Огляд
14
+
15
+ Файл визначає шаблони, які використовуються для корекції компонентів VS Code. Він застосовує ці шаблони для забезпечення відповідності коду вимогам, визначеним у конфігураціях, а також для корекції помилок, описаних в [ANKOR_ISSUE_1] та [ANKOR_ISSUE_2].
16
+
17
+ ## Поведінка
18
+
19
+ 1. Ідентифікує шаблони, необхідні для корекції розширень VS Code.
20
+ 2. Застосовує ці шаблони для приведення коду до відповідності стандартам.
21
+
22
+ ## Гарантії поведінки
23
+
24
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/js/vscode_extensions
4
+ resource: plugins/lang-js/rules/js/vscode_extensions/
5
+ ---
6
+
7
+ # npm/rules/js/vscode_extensions
8
+
9
+ | Файл | Тип |
10
+ | ----------------------------------------------------- | --------- |
11
+ | [fix-vscode_extensions.mjs](fix-vscode_extensions.md) | JS Module |
@@ -0,0 +1 @@
1
+ export { patterns } from '@7n/rules/scripts/lib/fix/vscode-ext-add.mjs'
@@ -0,0 +1,6 @@
1
+ {
2
+ "recommendations": [
3
+ "dbaeumer.vscode-eslint",
4
+ "oxc.oxc-vscode"
5
+ ]
6
+ }
@@ -0,0 +1,11 @@
1
+ ## Rego-gate `.vscode/extensions.json`
2
+
3
+ Rego-пакет: `js_lint.vscode_extensions`
4
+
5
+ Цільовий файл: `.vscode/extensions.json`.
6
+
7
+ Що перевіряється:
8
+
9
+ - `recommendations` — subset-of: масив має містити всі три канонічні розширення (`dbaeumer.vscode-eslint`, `github.vscode-github-actions`, `oxc.oxc-vscode`)
10
+
11
+ Канон-snippet: [extensions.json.snippet.json](./template/extensions.json.snippet.json)
@@ -0,0 +1,12 @@
1
+ # Перевірка `.vscode/extensions.json` для js (js.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ package js.vscode_extensions
5
+
6
+ import rego.v1
7
+
8
+ deny contains msg if {
9
+ some rec in data.template.snippet.recommendations
10
+ not rec in {r | some r in object.get(input, "recommendations", [])}
11
+ msg := sprintf(".vscode/extensions.json: recommendations має містити %q (js.mdc)", [rec])
12
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json"
3
+ }
@@ -0,0 +1,42 @@
1
+ ## Підключення (singleton + env)
2
+
3
+ Дефолтний експорт `sql` з `'bun'` сам читає змінні середовища (`DATABASE_URL`, `POSTGRES_URL`, `MYSQL_URL`, `PGHOST`/`PGUSER`/... та `MYSQL_HOST`/`MYSQL_USER`/...) і керує пулом — окремий `Pool` як у `pg` створювати не треба.
4
+
5
+ Для явного конфігу — `new SQL(...)` як **singleton** на рівні модуля, а не на кожен запит. Файл кладеться у `src/conn/db.mjs` і експортує іменовані константи `pgWrite` (основний запис) та `pgRead` (read-only replica), щоб glob `**/src/conn/**` у правилах покривав ці файли:
6
+
7
+ ```javascript
8
+ // src/conn/db.mjs
9
+ import { SQL } from 'bun'
10
+
11
+ export const pgWrite = new SQL({
12
+ url: process.env.DATABASE_URL,
13
+ max: 20,
14
+ idleTimeout: 30,
15
+ connectionTimeout: 10
16
+ })
17
+
18
+ export const pgRead = new SQL({
19
+ url: process.env.PG_CONN_READ ?? process.env.DATABASE_URL,
20
+ max: 10,
21
+ idleTimeout: 30,
22
+ connectionTimeout: 10
23
+ })
24
+ ```
25
+
26
+ Connection string обирає адаптер автоматично:
27
+
28
+ - `postgres://...` / `postgresql://...` → PostgreSQL
29
+ - `mysql://...` / `mysql2://...` → MySQL/MariaDB
30
+ - `sqlite://...` / `file://...` / `:memory:` → SQLite
31
+
32
+ ### Не створювати підключення на кожен запит
33
+
34
+ ```javascript
35
+ // ❌ нове підключення/інстанс на кожен виклик
36
+ function getUser(id) {
37
+ const pgWrite = new SQL(process.env.DATABASE_URL)
38
+ return pgWrite`SELECT * FROM users WHERE id = ${id}`
39
+ }
40
+ ```
41
+
42
+ `new SQL(...)` має створюватись **один раз** на рівні модуля. Bun сам тримає пул (`max`, `idleTimeout`, `maxLifetime`) — окремих `Pool`/`Client` як у `pg` не потрібно. Автоматична перевірка (`lib/bun-sql-scan.mjs`, `findBunSqlPerRequestConnectionInText`, підключена з `safety/main.mjs`) фейлить будь-який `new SQL(...)` усередині функції — це єдина частина цієї сторінки, яку лінтер справді перевіряє; сам singleton-паттерн (файл, іменування `pgWrite`/`pgRead`) — рекомендація без автоматичної перевірки.
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/js-bun-db
4
+ resource: plugins/lang-js/rules/js-bun-db/
5
+ ---
6
+
7
+ # npm/rules/js-bun-db
8
+
9
+ | Файл | Тип |
10
+ | ------------------- | --------- |
11
+ | [main.mjs](main.md) | JS Module |