@7n/rules 1.27.0 → 1.28.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 (267) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +1 -5
  3. package/scripts/auto-rules.mjs +2 -3
  4. package/scripts/docs/auto-rules.md +1 -1
  5. package/scripts/lib/docs/glob-to-regex.md +31 -0
  6. package/scripts/lib/docs/index.md +2 -0
  7. package/scripts/lib/docs/js-source-signals.md +32 -0
  8. package/scripts/lib/glob-to-regex.mjs +79 -0
  9. package/scripts/lib/js-source-signals.mjs +50 -0
  10. package/scripts/utils/ast-scan-utils.mjs +2 -2
  11. package/scripts/utils/docs/ast-scan-utils.md +1 -1
  12. package/rules/bun/bunfig/bunfig.mdc +0 -17
  13. package/rules/bun/bunfig/bunfig.rego +0 -29
  14. package/rules/bun/bunfig/concern.json +0 -9
  15. package/rules/bun/bunfig/template/bunfig.toml.snippet.toml +0 -2
  16. package/rules/bun/docs/index.md +0 -11
  17. package/rules/bun/layout/concern.json +0 -16
  18. package/rules/bun/layout/docs/fix-layout.md +0 -29
  19. package/rules/bun/layout/docs/main.md +0 -34
  20. package/rules/bun/layout/fix-layout.mjs +0 -63
  21. package/rules/bun/layout/layout.mdc +0 -60
  22. package/rules/bun/layout/main.mjs +0 -53
  23. package/rules/bun/licensee/concern.json +0 -7
  24. package/rules/bun/licensee/docs/fix-licensee.md +0 -27
  25. package/rules/bun/licensee/docs/index.md +0 -12
  26. package/rules/bun/licensee/docs/main.md +0 -30
  27. package/rules/bun/licensee/fix-licensee.mjs +0 -31
  28. package/rules/bun/licensee/main.mjs +0 -68
  29. package/rules/bun/lint-surface/concern.json +0 -3
  30. package/rules/bun/lint-surface/lint-surface.mdc +0 -13
  31. package/rules/bun/main.json +0 -1
  32. package/rules/bun/main.mdc +0 -11
  33. package/rules/bun/package_json/concern.json +0 -9
  34. package/rules/bun/package_json/docs/fix-package_json.md +0 -28
  35. package/rules/bun/package_json/docs/index.md +0 -9
  36. package/rules/bun/package_json/fix-package_json.mjs +0 -311
  37. package/rules/bun/package_json/package_json.mdc +0 -14
  38. package/rules/bun/package_json/package_json.rego +0 -64
  39. package/rules/bun/package_json/template/package.json.deny.json +0 -4
  40. package/rules/js/check/check.mdc +0 -26
  41. package/rules/js/check/concern.json +0 -19
  42. package/rules/js/check/docs/eslint-config.md +0 -56
  43. package/rules/js/check/docs/fix-check.md +0 -48
  44. package/rules/js/check/docs/index.md +0 -13
  45. package/rules/js/check/docs/main.md +0 -48
  46. package/rules/js/check/eslint-config.mjs +0 -262
  47. package/rules/js/check/fix-check.mjs +0 -82
  48. package/rules/js/check/main.mjs +0 -310
  49. package/rules/js/dep-policy/concern.json +0 -4
  50. package/rules/js/dep-policy/dep-policy.mdc +0 -36
  51. package/rules/js/dep-policy/docs/main.md +0 -35
  52. package/rules/js/dep-policy/main.mjs +0 -99
  53. package/rules/js/docs/index.md +0 -11
  54. package/rules/js/eslint/concern.json +0 -8
  55. package/rules/js/eslint/docs/fix-eslint.md +0 -50
  56. package/rules/js/eslint/docs/fix-worker.md +0 -31
  57. package/rules/js/eslint/docs/index.md +0 -11
  58. package/rules/js/eslint/docs/main.md +0 -35
  59. package/rules/js/eslint/fix-eslint.mjs +0 -160
  60. package/rules/js/eslint/fix-worker.mjs +0 -139
  61. package/rules/js/eslint/main.mjs +0 -115
  62. package/rules/js/file-extensions/concern.json +0 -3
  63. package/rules/js/file-extensions/file-extensions.mdc +0 -12
  64. package/rules/js/jscpd_config/concern.json +0 -11
  65. package/rules/js/jscpd_config/docs/fix-jscpd_config.md +0 -26
  66. package/rules/js/jscpd_config/docs/index.md +0 -9
  67. package/rules/js/jscpd_config/fix-jscpd_config.mjs +0 -3
  68. package/rules/js/jscpd_config/jscpd_config.mdc +0 -42
  69. package/rules/js/jscpd_config/jscpd_config.rego +0 -44
  70. package/rules/js/jscpd_config/template/.jscpd.json.snippet.json +0 -7
  71. package/rules/js/jscpd_duplicates/concern.json +0 -7
  72. package/rules/js/jscpd_duplicates/docs/main.md +0 -29
  73. package/rules/js/jscpd_duplicates/main.mjs +0 -69
  74. package/rules/js/knip/concern.json +0 -7
  75. package/rules/js/knip/docs/main.md +0 -32
  76. package/rules/js/knip/knip.mdc +0 -15
  77. package/rules/js/knip/main.mjs +0 -68
  78. package/rules/js/lint-findings/concern.json +0 -3
  79. package/rules/js/lint-findings/docs/main.md +0 -41
  80. package/rules/js/lint-findings/main.mjs +0 -125
  81. package/rules/js/main.json +0 -1
  82. package/rules/js/main.mdc +0 -18
  83. package/rules/js/package_json/concern.json +0 -9
  84. package/rules/js/package_json/docs/fix-package_json.md +0 -26
  85. package/rules/js/package_json/docs/index.md +0 -9
  86. package/rules/js/package_json/fix-package_json.mjs +0 -3
  87. package/rules/js/package_json/package_json.mdc +0 -15
  88. package/rules/js/package_json/package_json.rego +0 -142
  89. package/rules/js/package_json/template/package.json.snippet.json +0 -6
  90. package/rules/js/tooling/concern.json +0 -3
  91. package/rules/js/tooling/data/tooling/knip-canonical.json +0 -30
  92. package/rules/js/tooling/data/tooling/oxlint-canonical.json +0 -400
  93. package/rules/js/tooling/docs/main.md +0 -53
  94. package/rules/js/tooling/main.mjs +0 -183
  95. package/rules/js/utils_imports/concern.json +0 -4
  96. package/rules/js/utils_imports/docs/main.md +0 -50
  97. package/rules/js/utils_imports/main.mjs +0 -185
  98. package/rules/js/utils_imports/utils_imports.mdc +0 -15
  99. package/rules/js/vscode_extensions/concern.json +0 -11
  100. package/rules/js/vscode_extensions/docs/fix-vscode_extensions.md +0 -25
  101. package/rules/js/vscode_extensions/docs/index.md +0 -11
  102. package/rules/js/vscode_extensions/fix-vscode_extensions.mjs +0 -1
  103. package/rules/js/vscode_extensions/template/extensions.json.snippet.json +0 -6
  104. package/rules/js/vscode_extensions/vscode_extensions.mdc +0 -11
  105. package/rules/js/vscode_extensions/vscode_extensions.rego +0 -12
  106. package/rules/js-bun-db/connection/concern.json +0 -3
  107. package/rules/js-bun-db/connection/connection.mdc +0 -42
  108. package/rules/js-bun-db/docs/index.md +0 -11
  109. package/rules/js-bun-db/lib/bun-sql-scan.mjs +0 -1055
  110. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +0 -63
  111. package/rules/js-bun-db/lib/docs/index.md +0 -11
  112. package/rules/js-bun-db/main.json +0 -1
  113. package/rules/js-bun-db/main.mdc +0 -8
  114. package/rules/js-bun-db/package_json/concern.json +0 -9
  115. package/rules/js-bun-db/package_json/package_json.mdc +0 -31
  116. package/rules/js-bun-db/package_json/package_json.rego +0 -15
  117. package/rules/js-bun-db/package_json/template/package.json.deny.json +0 -6
  118. package/rules/js-bun-db/pg_format_identifiers/concern.json +0 -3
  119. package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +0 -104
  120. package/rules/js-bun-db/safety/concern.json +0 -4
  121. package/rules/js-bun-db/safety/docs/main.md +0 -34
  122. package/rules/js-bun-db/safety/main.mjs +0 -430
  123. package/rules/js-bun-db/safety/safety.mdc +0 -458
  124. package/rules/js-bun-redis/docs/index.md +0 -11
  125. package/rules/js-bun-redis/imports/concern.json +0 -4
  126. package/rules/js-bun-redis/imports/docs/main.md +0 -36
  127. package/rules/js-bun-redis/imports/imports.mdc +0 -47
  128. package/rules/js-bun-redis/imports/main.mjs +0 -88
  129. package/rules/js-bun-redis/lib/docs/index.md +0 -11
  130. package/rules/js-bun-redis/lib/docs/redis-imports.md +0 -227
  131. package/rules/js-bun-redis/lib/redis-imports.mjs +0 -130
  132. package/rules/js-bun-redis/main.json +0 -1
  133. package/rules/js-bun-redis/main.mdc +0 -8
  134. package/rules/js-bun-redis/package_json/concern.json +0 -9
  135. package/rules/js-bun-redis/package_json/package_json.mdc +0 -11
  136. package/rules/js-bun-redis/package_json/package_json.rego +0 -15
  137. package/rules/js-bun-redis/package_json/template/package.json.deny.json +0 -12
  138. package/rules/js-mssql/deps/concern.json +0 -4
  139. package/rules/js-mssql/deps/docs/main.md +0 -33
  140. package/rules/js-mssql/deps/main.mjs +0 -297
  141. package/rules/js-mssql/docs/index.md +0 -11
  142. package/rules/js-mssql/lib/docs/index.md +0 -11
  143. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +0 -380
  144. package/rules/js-mssql/lib/mssql-pool-scan.mjs +0 -610
  145. package/rules/js-mssql/main.json +0 -1
  146. package/rules/js-mssql/main.mdc +0 -144
  147. package/rules/js-mssql/mssql-tvp/concern.json +0 -3
  148. package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +0 -77
  149. package/rules/js-mssql/package_json/concern.json +0 -9
  150. package/rules/js-mssql/package_json/package_json.mdc +0 -9
  151. package/rules/js-mssql/package_json/package_json.rego +0 -57
  152. package/rules/js-run/configmap/concern.json +0 -9
  153. package/rules/js-run/configmap/configmap.mdc +0 -37
  154. package/rules/js-run/configmap/configmap.rego +0 -21
  155. package/rules/js-run/configmap/template/configmap.yaml.contains.yml +0 -4
  156. package/rules/js-run/docs/index.md +0 -11
  157. package/rules/js-run/jsconfig/concern.json +0 -9
  158. package/rules/js-run/jsconfig/docs/fix-jsconfig.md +0 -29
  159. package/rules/js-run/jsconfig/docs/index.md +0 -9
  160. package/rules/js-run/jsconfig/fix-jsconfig.mjs +0 -119
  161. package/rules/js-run/jsconfig/jsconfig.mdc +0 -48
  162. package/rules/js-run/jsconfig/jsconfig.rego +0 -59
  163. package/rules/js-run/jsconfig/template/jsconfig.json.snippet.json +0 -10
  164. package/rules/js-run/lib/bunyan-imports.mjs +0 -98
  165. package/rules/js-run/lib/check-env-scan.mjs +0 -338
  166. package/rules/js-run/lib/conn-file-rules.mjs +0 -214
  167. package/rules/js-run/lib/conn-imports-scan.mjs +0 -154
  168. package/rules/js-run/lib/docs/bunyan-imports.md +0 -121
  169. package/rules/js-run/lib/docs/check-env-scan.md +0 -438
  170. package/rules/js-run/lib/docs/conn-file-rules.md +0 -304
  171. package/rules/js-run/lib/docs/conn-imports-scan.md +0 -208
  172. package/rules/js-run/lib/docs/index.md +0 -16
  173. package/rules/js-run/lib/docs/promise-settimeout-scan.md +0 -334
  174. package/rules/js-run/lib/docs/temporal-scan.md +0 -29
  175. package/rules/js-run/lib/promise-settimeout-scan.mjs +0 -128
  176. package/rules/js-run/lib/temporal-scan.mjs +0 -52
  177. package/rules/js-run/main.json +0 -1
  178. package/rules/js-run/main.mdc +0 -16
  179. package/rules/js-run/package_json/concern.json +0 -9
  180. package/rules/js-run/package_json/package_json.mdc +0 -44
  181. package/rules/js-run/package_json/package_json.rego +0 -37
  182. package/rules/js-run/package_json/template/package.json.deny.json +0 -22
  183. package/rules/js-run/project-structure/concern.json +0 -3
  184. package/rules/js-run/project-structure/project-structure.mdc +0 -11
  185. package/rules/js-run/runtime/concern.json +0 -12
  186. package/rules/js-run/runtime/docs/fix-runtime.md +0 -27
  187. package/rules/js-run/runtime/docs/main.md +0 -35
  188. package/rules/js-run/runtime/fix-runtime.mjs +0 -46
  189. package/rules/js-run/runtime/main.mjs +0 -494
  190. package/rules/js-run/runtime/runtime.mdc +0 -184
  191. package/rules/js-run/scope/concern.json +0 -3
  192. package/rules/js-run/scope/scope.mdc +0 -11
  193. package/rules/npm-module/docs/index.md +0 -11
  194. package/rules/npm-module/emit_types_config/concern.json +0 -9
  195. package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +0 -25
  196. package/rules/npm-module/emit_types_config/docs/index.md +0 -9
  197. package/rules/npm-module/emit_types_config/emit_types_config.mdc +0 -43
  198. package/rules/npm-module/emit_types_config/emit_types_config.rego +0 -28
  199. package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +0 -5
  200. package/rules/npm-module/emit_types_config/template/tsconfig.emit-types.json.snippet.json +0 -9
  201. package/rules/npm-module/header_doc_pointer/concern.json +0 -5
  202. package/rules/npm-module/header_doc_pointer/docs/main.md +0 -40
  203. package/rules/npm-module/header_doc_pointer/header_doc_pointer.mdc +0 -18
  204. package/rules/npm-module/header_doc_pointer/main.mjs +0 -131
  205. package/rules/npm-module/main.json +0 -1
  206. package/rules/npm-module/main.mdc +0 -36
  207. package/rules/npm-module/npm_package_json/concern.json +0 -9
  208. package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +0 -27
  209. package/rules/npm-module/npm_package_json/docs/index.md +0 -9
  210. package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +0 -5
  211. package/rules/npm-module/npm_package_json/npm_package_json.mdc +0 -57
  212. package/rules/npm-module/npm_package_json/npm_package_json.rego +0 -73
  213. package/rules/npm-module/npm_package_json/template/package.json.snippet.json +0 -1
  214. package/rules/npm-module/package_structure/concern.json +0 -5
  215. package/rules/npm-module/package_structure/docs/main.md +0 -37
  216. package/rules/npm-module/package_structure/main.mjs +0 -515
  217. package/rules/npm-module/package_structure/package_structure.mdc +0 -63
  218. package/rules/npm-module/root_package_json/concern.json +0 -9
  219. package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +0 -26
  220. package/rules/npm-module/root_package_json/docs/index.md +0 -9
  221. package/rules/npm-module/root_package_json/fix-root_package_json.mjs +0 -5
  222. package/rules/npm-module/root_package_json/root_package_json.mdc +0 -41
  223. package/rules/npm-module/root_package_json/root_package_json.rego +0 -28
  224. package/rules/npm-module/root_package_json/template/package.json.snippet.json +0 -1
  225. package/rules/npm-module/rule_meta/concern.json +0 -8
  226. package/rules/npm-module/rule_meta/docs/main.md +0 -35
  227. package/rules/npm-module/rule_meta/main.mjs +0 -119
  228. package/rules/npm-module/rule_meta/rule_meta.mdc +0 -11
  229. package/rules/npm-module/skill_meta/concern.json +0 -5
  230. package/rules/npm-module/skill_meta/docs/main.md +0 -149
  231. package/rules/npm-module/skill_meta/main.mjs +0 -91
  232. package/rules/npm-module/skill_meta/skill_meta.mdc +0 -11
  233. package/rules/tool-surface/docs/index.md +0 -11
  234. package/rules/tool-surface/main.json +0 -6
  235. package/rules/tool-surface/main.mdc +0 -72
  236. package/rules/vue/composition-api/composition-api.mdc +0 -82
  237. package/rules/vue/composition-api/concern.json +0 -3
  238. package/rules/vue/docs/index.md +0 -11
  239. package/rules/vue/lib/docs/index.md +0 -11
  240. package/rules/vue/lib/docs/vue-forbidden-imports.md +0 -265
  241. package/rules/vue/lib/vue-forbidden-imports.mjs +0 -264
  242. package/rules/vue/main.json +0 -1
  243. package/rules/vue/main.mdc +0 -18
  244. package/rules/vue/nheader-layout/concern.json +0 -3
  245. package/rules/vue/nheader-layout/nheader-layout.mdc +0 -171
  246. package/rules/vue/package_json/concern.json +0 -9
  247. package/rules/vue/package_json/package_json.mdc +0 -30
  248. package/rules/vue/package_json/package_json.rego +0 -140
  249. package/rules/vue/packages/concern.json +0 -6
  250. package/rules/vue/packages/docs/index.md +0 -11
  251. package/rules/vue/packages/docs/main.md +0 -35
  252. package/rules/vue/packages/main.mjs +0 -575
  253. package/rules/vue/packages/packages.mdc +0 -56
  254. package/rules/vue/quasar-ui/concern.json +0 -3
  255. package/rules/vue/quasar-ui/quasar-ui.mdc +0 -32
  256. package/rules/vue/structure/concern.json +0 -3
  257. package/rules/vue/structure/structure.mdc +0 -101
  258. package/rules/vue/testing/concern.json +0 -3
  259. package/rules/vue/testing/testing.mdc +0 -40
  260. package/rules/vue/tfm-translations/concern.json +0 -7
  261. package/rules/vue/tfm-translations/docs/main.md +0 -29
  262. package/rules/vue/tfm-translations/main.mjs +0 -55
  263. package/rules/vue/tfm-translations/tfm-translations.mdc +0 -32
  264. package/rules/vue/vite-config/concern.json +0 -3
  265. package/rules/vue/vite-config/vite-config.mdc +0 -153
  266. package/rules/vue/vite-env/concern.json +0 -3
  267. package/rules/vue/vite-env/vite-env.mdc +0 -61
@@ -1,515 +0,0 @@
1
- /** @see ./docs/package_structure.md */
2
- import { existsSync } from 'node:fs'
3
- import { readFile, stat } from 'node:fs/promises'
4
- import { join, sep } from 'node:path'
5
-
6
- import { parseSync } from 'oxc-parser'
7
-
8
- import {
9
- dynamicImportModule,
10
- langFromPath,
11
- requireCallModule,
12
- walkAstWithAncestors
13
- } from '../../../scripts/utils/ast-scan-utils.mjs'
14
- import { createViolationReporter } from '../../../scripts/lib/lint-surface/violation-reporter.mjs'
15
- import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
16
- import { walkDir } from '../../../scripts/utils/walkDir.mjs'
17
-
18
- /** Файл проєкту TypeScript для emit без каталогу `src` (див. npm-module.mdc) */
19
- const EMIT_TYPES_CONFIG = 'npm/tsconfig.emit-types.json'
20
-
21
- /** Каталоги, які за конвенцією тримають тести / фікстури і не повинні публікуватися. */
22
- const TEST_DIR_NAMES = new Set(['tests', '__tests__', 'fixtures', '__fixtures__', 'spec', 'test'])
23
-
24
- /**
25
- * Імена файлів за патернами test/spec (тільки basename, без path). Rego
26
- * (`*_test.rego`) свідомо не входить: за конвенцією conftest юніт-тест лежить
27
- * поруч з полісі у тому самому `package` — і це дозволений виняток усередині
28
- * опублікованого `policy/`-каталогу (npm-module.mdc).
29
- * @param {string} cwd корінь репозиторію
30
- */
31
- const TEST_FILE_PATTERNS = [/^.+\.(test|spec)\.[cm]?[jt]sx?$/iu]
32
-
33
- /** Розширення, у яких ловимо імпорти test-фреймворків. */
34
- const JS_LIKE_EXT_RE = /\.[cm]?[jt]sx?$/iu
35
-
36
- /** Імпорти/require/dynamic-import, які видають test-файл. */
37
- const TEST_FRAMEWORK_MODULES = new Set([
38
- 'bun:test',
39
- 'node:test',
40
- 'vitest',
41
- '@jest/globals',
42
- 'jest',
43
- 'mocha',
44
- 'ava',
45
- 'tap',
46
- 'tape'
47
- ])
48
-
49
- /** Символи у glob-сегменті, які треба екранувати для RegExp (без `*` / `?` — їх обробляємо окремо). */
50
- const REGEX_SPECIAL_IN_GLOB = new Set(['.', '+', '^', '$', '{', '}', '(', ')', '|', '[', ']', '\\'])
51
-
52
- /** Збіги для post-обробки glob → regex після злиття сегментів через `/` (див. `globToRegex`). */
53
- const GLOBSTAR_LEADING_RE = /^__GLOBSTAR__\//u
54
- const GLOBSTAR_TRAILING_RE = /\/__GLOBSTAR__$/u
55
-
56
- /**
57
- * Чи є під `npm/src` хоча б один `.js` (рекурсивно).
58
- * @param {string} cwd корінь репозиторію
59
- * @param {string[]} [ignorePaths] абсолютні шляхи каталогів, повністю виключених з обходу
60
- * @returns {Promise<boolean>} `true`, якщо знайдено хоча б один `.js`
61
- */
62
- async function npmSrcTreeHasJsFile(cwd, ignorePaths = []) {
63
- const root = join(cwd, 'npm/src')
64
- if (!existsSync(root)) {
65
- return false
66
- }
67
- let found = false
68
- await walkDir(
69
- root,
70
- p => {
71
- if (p.endsWith('.js')) {
72
- found = true
73
- }
74
- },
75
- ignorePaths
76
- )
77
- return found
78
- }
79
-
80
- /**
81
- * Знаходить текстовий вміст конфігурації hk для перевірки npm-module.
82
- * @returns {Promise<{ path: string, text: string } | null>} знайдений файл або `null`
83
- * @param {string} cwd корінь репозиторію
84
- */
85
- async function readHkConfig(cwd) {
86
- const candidates = ['hk.pkl', '.config/hk.pkl']
87
- for (const p of candidates) {
88
- const abs = join(cwd, p)
89
- if (existsSync(abs)) {
90
- const text = await readFile(abs, 'utf8')
91
- return { path: p, text }
92
- }
93
- }
94
- return null
95
- }
96
-
97
- /**
98
- * Підрядки для hk при layout з каталогом `npm/src` і glob `src` + `.js` у команді (див. npm-module.mdc).
99
- * @param {string} hkText текст конфігурації hk
100
- * @returns {string[]} відсутні фрагменти
101
- */
102
- function missingHkSrcLayoutFragments(hkText) {
103
- const need = [
104
- '["pre-commit"]',
105
- 'bunx -p typescript tsc',
106
- 'src/**/*.js',
107
- '--declaration',
108
- '--allowJs',
109
- '--emitDeclarationOnly',
110
- '--outDir types',
111
- '--skipLibCheck'
112
- ]
113
- return need.filter(s => !hkText.includes(s))
114
- }
115
-
116
- /**
117
- * Підрядки для hk при layout з `tsconfig.emit-types.json` (див. npm-module.mdc).
118
- * @param {string} hkText текст конфігурації hk
119
- * @returns {string[]} відсутні фрагменти
120
- */
121
- function missingHkEmitTypesConfigFragments(hkText) {
122
- const need = ['["pre-commit"]', 'bunx -p typescript tsc', 'tsconfig.emit-types.json']
123
- return need.filter(s => !hkText.includes(s))
124
- }
125
-
126
- /**
127
- * Застаріла інвокація команди `check`, прибраної в v14 (уніфікована поверхня `lint`).
128
- * Матч на підрядок "check changelog" незалежно від того, яким бінарем/шляхом викликано.
129
- */
130
- const DEPRECATED_CHECK_CHANGELOG_RE = /\bcheck\s+changelog\b/u
131
-
132
- /**
133
- * Підрядки канонічного кроку `npm-changelog` у hk.pkl (fix-only, autofix через env
134
- * `N_RULES_CHANGELOG_AUTOFIX=1`; сам крок ставить change-файл у git-індекс, `stage`
135
- * не потрібен). Див. npm-module.mdc.
136
- * @param {string} hkText текст конфігурації hk
137
- * @returns {string[]} відсутні фрагменти
138
- */
139
- function missingHkNpmChangelogFragments(hkText) {
140
- const need = ['["npm-changelog"]', 'N_RULES_CHANGELOG_AUTOFIX=1', 'lint changelog']
141
- return need.filter(s => !hkText.includes(s))
142
- }
143
-
144
- /**
145
- * Шлях на дискі до файлу з поля `types` у `npm/package.json` (значення на кшталт `./types/bin/x.d.ts`).
146
- * @param {string} typesField значення поля `types` з `package.json`
147
- * @returns {string | null} абсолютний шлях або `null`
148
- */
149
- function npmTypesFileFromPackageField(typesField) {
150
- if (typeof typesField !== 'string' || !typesField.startsWith('./types/')) {
151
- return null
152
- }
153
- const rel = typesField.slice(2)
154
- return join('npm', rel)
155
- }
156
-
157
- /**
158
- * Перевіряє наявність на диску файлу зі значення `types` у `npm/package.json`
159
- * (cross-file: JSON-поле + FS). Структуру самого поля валідує
160
- * `npm/policy/npm_module/npm_package_json/`; тут — лише чи файл реально існує.
161
- * @param {boolean} useSrcJsLayout чи використовується layout з npm/src
162
- * @param {(msg: string) => void} passFn callback при успішній перевірці
163
- * @param {(msg: string) => void} failFn callback при помилці
164
- * @param {string} cwd корінь репозиторію
165
- */
166
- async function checkNpmPackageJson(useSrcJsLayout, passFn, failFn, cwd) {
167
- const npmPkgPath = join(cwd, 'npm/package.json')
168
- if (!existsSync(npmPkgPath)) return
169
- const npmPkg = JSON.parse(await readFile(npmPkgPath, 'utf8'))
170
- const typesField = npmPkg.types
171
-
172
- const typesRel = useSrcJsLayout ? join('npm', 'types', 'index.d.ts') : npmTypesFileFromPackageField(typesField)
173
- const missingTypesMsg = useSrcJsLayout
174
- ? `Відсутній ${join('npm', 'types', 'index.d.ts')} (згенеруй tsc з npm-module.mdc)`
175
- : `Файл для поля types не знайдено або шлях не під ./types/ — ${String(typesField)}`
176
- if (typesRel && existsSync(join(cwd, typesRel))) {
177
- passFn(`${typesRel} існує`)
178
- } else {
179
- failFn(missingTypesMsg)
180
- }
181
- }
182
-
183
- /**
184
- * FS-existence для `npm/tsconfig.emit-types.json` (структуру `compilerOptions`
185
- * валідує `npm/policy/npm_module/emit_types_config/`).
186
- * @param {(msg: string) => void} passFn callback при успішній перевірці
187
- * @param {(msg: string) => void} failFn callback при помилці
188
- * @param {string} cwd корінь репозиторію
189
- */
190
- function checkEmitTypesConfig(passFn, failFn, cwd) {
191
- if (!existsSync(join(cwd, EMIT_TYPES_CONFIG))) {
192
- failFn(
193
- `Без .js під npm/src потрібен ${EMIT_TYPES_CONFIG} (див. npm-module.mdc: emit через tsconfig, без штучного src/index.js)`
194
- )
195
- return
196
- }
197
- passFn(`${EMIT_TYPES_CONFIG} є (структуру перевіряє npx @7n/rules fix → npm_module.emit_types_config)`)
198
- }
199
-
200
- /**
201
- * FS-existence для `npm-publish.yml` workflow. Поля workflow (`on.push.paths`,
202
- * `branches`, `id-token: write`, JS-DevTools/npm-publish step) валідує
203
- * `npm/policy/npm_module/npm_publish_yml/`.
204
- * @param {(msg: string) => void} passFn callback при успішній перевірці
205
- * @param {(msg: string) => void} failFn callback при виявленому порушенні
206
- * @param {string} cwd корінь репозиторію
207
- */
208
- function checkPublishWorkflow(passFn, failFn, cwd) {
209
- const publishWf = '.github/workflows/npm-publish.yml'
210
- if (existsSync(join(cwd, publishWf))) {
211
- passFn(`${publishWf} є (структуру перевіряє npx @7n/rules fix → npm_module.npm_publish_yml)`)
212
- } else {
213
- failFn(`Відсутній ${publishWf} (npm-module.mdc: npm publish)`)
214
- }
215
- }
216
-
217
- /**
218
- * Перетворює glob-патерн (як у npm `files` чи `meta.json:auto.glob`) у `RegExp`
219
- * з якорями `^` / `$`. Підтримує globstar (нуль або більше сегментів), `*`
220
- * (символи без `/`), `?` (один символ без `/`) і brace-альтернативи `{a,b,c}`
221
- * (наприклад `*.{png,jpg,svg}` → `(?:png|jpg|svg)`). Клас `[…]` не
222
- * підтримується — у негативних патернах `files` цього достатньо.
223
- * @param {string} glob posix-шлях у glob-нотації
224
- * @returns {RegExp} `RegExp` з якорями `^` / `$`
225
- */
226
- export function globToRegex(glob) {
227
- const parts = glob.split('/')
228
- const tokens = parts.map(p => {
229
- if (p === '**') return '__GLOBSTAR__'
230
- let out = ''
231
- let braceDepth = 0
232
- for (const c of p) {
233
- switch (c) {
234
- case '*': {
235
- out += '[^/]*'
236
- continue
237
- }
238
- case '?': {
239
- out += '[^/]'
240
- continue
241
- }
242
- case '{': {
243
- out += '(?:'
244
- braceDepth++
245
- continue
246
- }
247
- case '}': {
248
- if (braceDepth > 0) {
249
- out += ')'
250
- braceDepth--
251
- continue
252
- }
253
- break
254
- }
255
- case ',': {
256
- if (braceDepth > 0) {
257
- out += '|'
258
- continue
259
- }
260
- break
261
- }
262
- default: {
263
- break
264
- }
265
- }
266
- out += REGEX_SPECIAL_IN_GLOB.has(c) ? `\\${c}` : c
267
- }
268
- return out
269
- })
270
- let re = tokens.join('/')
271
- re = re.replaceAll('/__GLOBSTAR__/', '(?:/.*/|/)')
272
- re = re.replace(GLOBSTAR_LEADING_RE, '(?:.*/)?')
273
- re = re.replace(GLOBSTAR_TRAILING_RE, '(?:/.*)?')
274
- re = re.replaceAll('__GLOBSTAR__', '.*')
275
- // Дозволено: уся функція існує саме для конструкції RegExp з glob-pattern
276
- // у `files` (значення з npm/package.json, не від кінцевого користувача), і
277
- // спецсимволи вже екрановано через `REGEX_SPECIAL_IN_GLOB` вище.
278
- return new RegExp(`^${re}$`, 'u')
279
- }
280
-
281
- /**
282
- * Збирає список файлів, що потраплять у tarball, виходячи з `files` у
283
- * `npm/package.json`. Підтримує позитивні patterns (директорії або файли) і
284
- * негативні (`!…`). Шляхи повертаються у posix-формі, відносно `npm/`.
285
- * Не пробує дублювати всю логіку `npm pack` (license/readme/тощо) — тут лише
286
- * простір імен `files`, бо саме його сканує check.
287
- * @param {string[]} filesField значення поля `files`
288
- * @returns {Promise<string[]>} відсортовані posix-шляхи без `npm/` префікса
289
- * @param {string} cwd корінь репозиторію
290
- */
291
- async function collectPublishedFiles(filesField, cwd) {
292
- const positives = filesField.filter(p => typeof p === 'string' && !p.startsWith('!'))
293
- const negatives = filesField.filter(p => typeof p === 'string' && p.startsWith('!')).map(p => globToRegex(p.slice(1)))
294
- /** @type {Set<string>} */
295
- const collected = new Set()
296
- const npmRoot = join(cwd, 'npm')
297
- for (const entry of positives) {
298
- const fullPath = join(npmRoot, entry)
299
- if (!existsSync(fullPath)) continue
300
- const s = await stat(fullPath)
301
- if (s.isFile()) {
302
- collected.add(entry)
303
- continue
304
- }
305
- if (!s.isDirectory()) continue
306
- await walkDir(fullPath, p => {
307
- const rel = p
308
- .slice(npmRoot.length + 1)
309
- .split(sep)
310
- .join('/')
311
- collected.add(rel)
312
- })
313
- }
314
- const filtered = [...collected].filter(rel => negatives.every(re => !re.test(rel)))
315
- filtered.sort()
316
- return filtered
317
- }
318
-
319
- /**
320
- * Чи є у файлі імпорт/require/dynamic-import з модуля тест-фреймворку.
321
- * Парсимо через oxc-parser (як `bunyan-imports`/`redis-imports`). При помилці
322
- * парсингу повертаємо `null` — це не наш checker для синтаксису.
323
- * @param {string} content повний текст файлу
324
- * @param {string} virtualPath шлях для вибору `lang`
325
- * @returns {string | null} модуль, через який видно тест, або `null`
326
- */
327
- export function findTestFrameworkImport(content, virtualPath) {
328
- const lang = langFromPath(virtualPath)
329
- let result
330
- try {
331
- result = parseSync(virtualPath, content, { lang, sourceType: 'module' })
332
- } catch {
333
- return null
334
- }
335
- if (result.errors?.length) return null
336
- for (const imp of result.module?.staticImports ?? []) {
337
- const mod = imp.moduleRequest?.value
338
- if (typeof mod === 'string' && TEST_FRAMEWORK_MODULES.has(mod)) return mod
339
- }
340
- /** @type {string | null} */
341
- let found = null
342
- walkAstWithAncestors(result.program, [], node => {
343
- if (found) return
344
- const reqMod = requireCallModule(node)
345
- if (reqMod && TEST_FRAMEWORK_MODULES.has(reqMod)) {
346
- found = reqMod
347
- return
348
- }
349
- const dynMod = dynamicImportModule(node)
350
- if (dynMod && TEST_FRAMEWORK_MODULES.has(dynMod)) {
351
- found = dynMod
352
- }
353
- })
354
- return found
355
- }
356
-
357
- /**
358
- * Класифікує опублікований файл як test/fixture, якщо хоча б одна з ознак:
359
- * (1) у шляху є каталог із `TEST_DIR_NAMES`; (2) basename відповідає
360
- * `TEST_FILE_PATTERNS`; (3) для JS/TS-розширень — імпорт test-фреймворку.
361
- *
362
- * Carve-out: для шляху `rules/<rule-name>/...` сегмент `<rule-name>` (індекс 1)
363
- * — це ім'я правила, а не каталог. Зокрема, правило з id `test` (або `tests`)
364
- * описує конвенцію розміщення тестів і саме по собі не є test-fixture'ом.
365
- * Подальші сегменти (наприклад, `rules/<r>/js/<c>/tests/`) продовжують перевірятись.
366
- * @param {string} relPath posix-шлях відносно `npm/`
367
- * @returns {Promise<string | null>} причина порушення або `null`
368
- * @param {string} [cwd] корінь репозиторію
369
- */
370
- export async function classifyPublishedFileAsTest(relPath, cwd = process.cwd()) {
371
- const segments = relPath.split('/')
372
- const base = segments.at(-1)
373
- const dirs = segments.slice(0, -1)
374
- const testDir = dirs.find((seg, idx) => {
375
- if (idx === 1 && dirs[0] === 'rules') {
376
- return false
377
- }
378
- return TEST_DIR_NAMES.has(seg.toLowerCase())
379
- })
380
- if (testDir) return `test-style каталог "${testDir}/"`
381
- if (TEST_FILE_PATTERNS.some(re => re.test(base))) return `test-style ім'я файлу`
382
- if (JS_LIKE_EXT_RE.test(base)) {
383
- const content = await readFile(join(cwd, 'npm', relPath), 'utf8')
384
- const mod = findTestFrameworkImport(content, relPath)
385
- if (mod) return `імпорт test-фреймворку "${mod}"`
386
- }
387
- return null
388
- }
389
-
390
- /**
391
- * Для всіх файлів, що потрапили б у tarball (positive `files` мінус negative
392
- * patterns), забороняє test-style каталоги/імена/імпорти. Так пакет лишається
393
- * компактним і не везе користувачам тести й фікстури.
394
- * @param {(msg: string) => void} pass callback при успіху
395
- * @param {(msg: string) => void} fail callback при порушенні
396
- * @returns {Promise<void>}
397
- * @param {string} cwd корінь репозиторію
398
- */
399
- async function checkNoTestsInPublishedFiles(pass, fail, cwd) {
400
- if (!existsSync(join(cwd, 'npm/package.json'))) return
401
- const pkg = JSON.parse(await readFile(join(cwd, 'npm/package.json'), 'utf8'))
402
- if (!Array.isArray(pkg.files)) return
403
- const files = await collectPublishedFiles(pkg.files, cwd)
404
- /** @type {{ file: string, reason: string }[]} */
405
- const violations = []
406
- for (const rel of files) {
407
- const reason = await classifyPublishedFileAsTest(rel, cwd)
408
- if (reason) violations.push({ file: rel, reason })
409
- }
410
- if (violations.length === 0) {
411
- pass(`npm/: усі ${files.length} опублікованих файли без тестів і fixtures`)
412
- return
413
- }
414
- for (const v of violations) {
415
- fail(
416
- `npm/${v.file}: ${v.reason} — додай у "files" у npm/package.json негативний glob, ` +
417
- 'що виключає цей файл з tarball (наприклад "!**/*.test.mjs", "!**/fixtures/**", "!**/*_test.rego") (npm-module.mdc)'
418
- )
419
- }
420
- }
421
-
422
- /**
423
- * Перевіряє базову структуру монорепо: наявність каталогу `npm/` і
424
- * `npm/package.json`. Поле `workspaces ∋ "npm"` у кореневому `package.json`
425
- * валідує `npm/policy/npm_module/root_package_json/`.
426
- * @param {(msg: string) => void} pass callback при успішній перевірці
427
- * @param {(msg: string) => void} fail callback при помилці
428
- * @param {string} cwd корінь репозиторію
429
- */
430
- async function checkNpmModuleBasicStructure(pass, fail, cwd) {
431
- if (existsSync(join(cwd, 'package.json'))) {
432
- pass('package.json існує')
433
- } else {
434
- fail('package.json не існує')
435
- }
436
-
437
- const npmDir = join(cwd, 'npm')
438
- if (existsSync(npmDir)) {
439
- const s = await stat(npmDir)
440
- if (s.isDirectory()) {
441
- pass('npm/ директорія існує')
442
- } else {
443
- fail('npm має бути директорією')
444
- }
445
- } else {
446
- fail('npm/ директорія не існує')
447
- }
448
-
449
- if (existsSync(join(cwd, 'npm/package.json'))) {
450
- pass('npm/package.json існує')
451
- } else {
452
- fail('npm/package.json не існує — створи package.json для npm модуля')
453
- }
454
- }
455
-
456
- /**
457
- * Перевіряє відповідність проєкту правилам npm-module.mdc
458
- * @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
459
- * @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} результат лінту
460
- */
461
- export async function lint(ctx) {
462
- const cwd = ctx.cwd
463
- const reporter = createViolationReporter(ctx)
464
- const { pass, fail } = reporter
465
-
466
- await checkNpmModuleBasicStructure(pass, fail, cwd)
467
- await checkNoTestsInPublishedFiles(pass, fail, cwd)
468
-
469
- const ignorePaths = await loadCursorIgnorePaths(cwd)
470
- const useSrcJsLayout = await npmSrcTreeHasJsFile(cwd, ignorePaths)
471
-
472
- await checkNpmPackageJson(useSrcJsLayout, pass, fail, cwd)
473
-
474
- if (!useSrcJsLayout) {
475
- await checkEmitTypesConfig(pass, fail, cwd)
476
- }
477
-
478
- const layoutLabel = useSrcJsLayout ? 'layout src' : 'tsconfig emit-types'
479
- const hk = await readHkConfig(cwd)
480
- if (hk) {
481
- pass(`${hk.path} існує`)
482
- const missing = useSrcJsLayout ? missingHkSrcLayoutFragments(hk.text) : missingHkEmitTypesConfigFragments(hk.text)
483
- if (missing.length === 0) {
484
- pass(`${hk.path}: pre-commit містить очікуваний виклик tsc (${layoutLabel})`)
485
- } else {
486
- fail(`${hk.path}: онови pre-commit крок (npm-module.mdc); не знайдено: ${missing.join(', ')}`)
487
- }
488
-
489
- if (DEPRECATED_CHECK_CHANGELOG_RE.test(hk.text)) {
490
- fail(
491
- `${hk.path}: крок містить застарілий виклик "check changelog" — команду \`check\` прибрано в v14 ` +
492
- '(уніфікована поверхня `lint`). Заміни на "npx @7n/rules lint changelog" (npm-module.mdc)'
493
- )
494
- } else {
495
- const missingChangelogStep = missingHkNpmChangelogFragments(hk.text)
496
- if (missingChangelogStep.length === 0) {
497
- pass(`${hk.path}: pre-commit містить крок npm-changelog (npm-module.mdc)`)
498
- } else {
499
- fail(`${hk.path}: онови крок npm-changelog (npm-module.mdc); не знайдено: ${missingChangelogStep.join(', ')}`)
500
- }
501
- }
502
- } else {
503
- fail('Очікується hk.pkl або .config/hk.pkl з pre-commit і tsc (npm-module.mdc)')
504
- }
505
-
506
- if (existsSync(join(cwd, '.github/workflows'))) {
507
- pass('.github/workflows/ існує')
508
- } else {
509
- fail('.github/workflows/ не існує')
510
- }
511
-
512
- await checkPublishWorkflow(pass, fail, cwd)
513
-
514
- return reporter.result()
515
- }
@@ -1,63 +0,0 @@
1
- ## Структура монорепо та компактний пакет
2
-
3
- Bun monorepo: workspace **`npm/`**, кореневий **`package.json`**, **`.github/workflows/`**; опційно **`demo/`**.
4
-
5
- Мета — **максимально компактний** опублікований пакет: у npm потрапляє тільки те, що потрібно під час `require`/`import` користувачем.
6
-
7
- - **`"files"` обовʼязковий** у `npm/package.json` як **whitelist** того, що публікується (без `"files"` npm пакує майже все — це антипатерн для цього правила).
8
- - **Тести й фікстури не публікуються — через негативні glob-патерни у `"files"`.** Тести можна (і зазвичай зручніше) тримати **поруч з кодом** усередині шляхів, перелічених у `"files"` (наприклад `*.test.mjs` поруч з модулем чи `fixtures/` під `rules/<id>/js/`). Щоб вони не потрапляли у tarball, `"files"` **обовʼязково має містити негативні glob-патерни**, що їх виключають. Покрий усі форми тестового: фреймворк-тести (`bun:test`, `node:test`, `vitest`, `@jest/globals`, `mocha`, …), test-style каталоги (`tests/`, `__tests__/`, `fixtures/`, `__fixtures__/`, `spec/`, `test/`), файли за патернами `*.test.*` / `*.spec.*`. Орієнтовний набір: `"!**/*.test.*"`, `"!**/*.spec.*"`, `"!**/test-helpers.*"`, `"!**/fixtures/**"`, `"!**/__tests__/**"`. **Rego (`*_test.rego`):** за конвенцією conftest юніт-тест лежить поруч з полісі у тому самому `package` — `*_test.rego` усередині опублікованого `policy/` дозволені; якщо потрібна максимальна компактність, додай `"!**/*_test.rego"` явно (як у самому `@7n/rules`).
9
- - **Лише runtime-залежності у `npm/package.json`.** `devDependencies` тримай у **кореневому** `package.json` монорепо — тоді `npm install @nitra/<pkg>` не тягне інструментарій, потрібний лише для розробки самого пакета.
10
-
11
- Перевірки JS (`package_structure.mjs`):
12
-
13
- - Наявність `package.json`, `npm/`, `npm/package.json`.
14
- - Walk шляхів з `"files"` з застосуванням негативних patterns і скан залишку на тест-патерни (walking + AST). Якщо після застосування негативних patterns у tarball лишається test-style файл — `check` падає з вказівкою, який саме негативний glob треба додати у `"files"`.
15
-
16
- ## TypeScript declaration (`npm/types`)
17
-
18
- Файл **`npm/package.json`** має містити **`"types"`** (шлях до головного `.d.ts` або `.d.mts` під **`./types/…`**) і запис **`"types"`** у **`files`**, щоб npm публікував декларації.
19
-
20
- Генерація — через **`tsc`** і **`bunx -p typescript`** (окремий пакет **`typescript`** у `devDependencies` не потрібен).
21
-
22
- ### Варіант A: є вихідний **`.js`** під **`npm/src/`**
23
-
24
- Якщо під **`npm/src`** (рекурсивно) є хоча б один файл **`.js`**:
25
-
26
- - **`"types": "./types/index.d.ts"`**;
27
- - у **hk** на **`pre-commit`** з каталогу **`npm/`** викликай:
28
-
29
- ```bash
30
- bunx -p typescript tsc src/**/*.js --declaration --allowJs --emitDeclarationOnly --outDir types --skipLibCheck
31
- ```
32
-
33
- Якщо glob не розгортається — **`bash -O globstar`** або **`include`** у **`tsconfig`** з тими самими **`compilerOptions`**.
34
-
35
- ### Варіант B: немає **`npm/src/**/*.js`** (наприклад лише **`bin/`**, **`scripts/**/*.mjs`**)
36
-
37
- Не створюй штучний **`src/index.js`**. Замість цього:
38
-
39
- 1. Додай **`npm/tsconfig.emit-types.json`** з **`include`** на реальні шляхи (`.js` / `.mjs`), **`compilerOptions`**: **`allowJs`**, **`declaration`**, **`emitDeclarationOnly`**, **`outDir`: `"types"`**, **`skipLibCheck`**: **`true`** (за потреби **`rootDir`**, **`module`**, **`moduleResolution`**).
40
- 2. Після першого **`tsc`** подивись, який **`.d.ts`** / **`.d.mts`** відповідає публічному API, і вкажи його в **`types`** (наприклад **`./types/bin/cli.d.ts`**).
41
- 3. У **hk** на **`pre-commit`** з **`npm/`** викликай **`tsc -p tsconfig.emit-types.json`**. Якщо через імпорти **TypeScript** все одно згенерує зайві **`.d.mts`** у **`types/`**, після **`tsc`** обрізай дерево до потрібного entrypoint (наприклад залиш лише **`types/bin/n-rules.d.ts`** через **`find`**), щоб у пакеті не збирались декларації внутрішніх модулів.
42
-
43
- Файл **`tsconfig.emit-types.json`** тримай у репозиторії для **hk** / локальної генерації; у **`files`** його не додавай, якщо не хочеш публікувати його на **npm**.
44
-
45
- ## Git hooks: hk + pre-commit
46
-
47
- У корені репозиторію — **`hk.pkl`** (або **`.config/hk.pkl`**) з **`["pre-commit"]`** і командою з відповідного варіанту (A або B) вище.
48
-
49
- Після додавання **`hk.pkl`**: **`hk install`**.
50
-
51
- ## npm publish
52
-
53
- **`npm-publish.yml`:** push у **`main`**, **`on.push.paths`** з **`npm/**`**, **`JS-DevTools/npm-publish@v4.1.5`**, **`with.package: npm/package.json`**, **`permissions.id-token: write`** (OIDC на npm).
54
-
55
- Workflow робить **release + publish** одним job (`release-publish`): крок **`Release (bump + CHANGELOG + tag)`** (`bunx n-rules release` — агрегує change-файли, bump `version`, генерує секцію `CHANGELOG.md`, ставить git-тег) виконується **перед** публікацією. Тому потрібні **`permissions.contents: write`** і **`persist-credentials: true`** з **`fetch-depth: 0`** на `checkout` (release пушить commit-back версії та тег), а також локальний composite **`./.github/actions/setup-bun-deps`** і крок `Configure git identity`. Це узгоджено з **`n-changelog`**: `version`/`CHANGELOG.md` змінює лише `n-rules release` у CI на `main`. Програмна перевірка (`npm_module.npm_publish_yml`) звіряє **весь канонічний сніпет** напряму (`target.json:"check":"template"`, generic deep-subset): усі поля й кроки сніпета (`on.push.paths`/`branches`, `concurrency`, `permissions.contents/id-token`, `checkout` з `persist-credentials/fetch-depth`, `setup-bun-deps`, `Configure git identity`, `Release`, publish-крок) **обовʼязкові**; зайві кроки/поля дозволені (subset-of), масиви матчаться за наявністю (порядок кроків не важить). Сніпет — єдине джерело істини: його редагування одразу змінює enforce, без правок rego й без міграторів.
56
-
57
- - Канон: [npm-publish.yml.snippet.yml](./policy/npm_publish_yml/template/npm-publish.yml.snippet.yml)
58
-
59
- ## Канонічні конфіги
60
-
61
- - Кореневий `package.json` (workspaces): [package.json.snippet.json](./policy/root_package_json/template/package.json.snippet.json)
62
- - `npm/package.json` (whitelist `files` обовʼязково має містити `types`): [package.json.snippet.json](./policy/npm_package_json/template/package.json.snippet.json)
63
- - `npm/tsconfig.emit-types.json` (canonical `compilerOptions` для emit-types): [tsconfig.emit-types.json.snippet.json](./policy/emit_types_config/template/tsconfig.emit-types.json.snippet.json)
@@ -1,9 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": "package.json"
7
- }
8
- }
9
- }
@@ -1,26 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-root_package_json.mjs
4
- resource: npm/rules/npm-module/root_package_json/fix-root_package_json.mjs
5
- docgen:
6
- crc: 8181dffe
7
- model: openai-codex/gpt-5.4-mini
8
- tier: cloud-min
9
- score: 100
10
- issues: judge:inaccurate:0.97
11
- judgeModel: openai-codex/gpt-5.4-mini
12
- ---
13
-
14
- ## Огляд
15
-
16
- `patterns` перевіряє кореневий `package.json` на відповідність еталонному шаблону npm-module і використовується для контролю очікуваної структури цього конфіга. Публічна функція: `patterns`. Конфіг, на який спирається код: `package.json`.
17
-
18
- ## Поведінка
19
-
20
- 1. `patterns` визначає набір правил, які звіряють кореневий `package.json` з еталонним шаблоном для npm-module.
21
- 2. Кожне правило в цьому наборі націлене на `package.json` і використовується для вирівнювання кореневого конфіга з очікуваною структурою проєкту.
22
- 3. `patterns` потрібен, щоб централізовано підтримувати однаковий формат root `package.json` у межах цього типу пакета.
23
-
24
- ## Гарантії поведінки
25
-
26
- - Read-only: не виконує операцій запису (ФС/БД).
@@ -1,9 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/npm-module/root_package_json
4
- resource: npm/rules/npm-module/root_package_json/
5
- ---
6
-
7
- | Файл | Тип |
8
- | ----------------------------------------------------- | --------- |
9
- | [fix-root_package_json.mjs](fix-root_package_json.md) | JS Module |
@@ -1,5 +0,0 @@
1
- import { createTemplateFixPattern } from '../../../scripts/lib/fix/template-deep-merge.mjs'
2
-
3
- export const patterns = [
4
- createTemplateFixPattern({ id: 'npm-module-root_package_json-template', targetPath: 'package.json' })
5
- ]