habit-hooks 0.1.0-beta.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 (192) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +233 -0
  3. package/dist/baseline/commands.d.ts +12 -0
  4. package/dist/baseline/commands.js +131 -0
  5. package/dist/baseline/commands.js.map +1 -0
  6. package/dist/baseline/file-hash.d.ts +2 -0
  7. package/dist/baseline/file-hash.js +25 -0
  8. package/dist/baseline/file-hash.js.map +1 -0
  9. package/dist/baseline/filter.d.ts +8 -0
  10. package/dist/baseline/filter.js +31 -0
  11. package/dist/baseline/filter.js.map +1 -0
  12. package/dist/baseline/store.d.ts +18 -0
  13. package/dist/baseline/store.js +106 -0
  14. package/dist/baseline/store.js.map +1 -0
  15. package/dist/checks/comment-check.d.ts +3 -0
  16. package/dist/checks/comment-check.js +83 -0
  17. package/dist/checks/comment-check.js.map +1 -0
  18. package/dist/checks/eslint-wrap.d.ts +2 -0
  19. package/dist/checks/eslint-wrap.js +104 -0
  20. package/dist/checks/eslint-wrap.js.map +1 -0
  21. package/dist/checks/jscpd-wrap.d.ts +6 -0
  22. package/dist/checks/jscpd-wrap.js +158 -0
  23. package/dist/checks/jscpd-wrap.js.map +1 -0
  24. package/dist/checks/knip-resolve.d.ts +4 -0
  25. package/dist/checks/knip-resolve.js +49 -0
  26. package/dist/checks/knip-resolve.js.map +1 -0
  27. package/dist/checks/knip-schema.d.ts +32 -0
  28. package/dist/checks/knip-schema.js +24 -0
  29. package/dist/checks/knip-schema.js.map +1 -0
  30. package/dist/checks/knip-wrap.d.ts +4 -0
  31. package/dist/checks/knip-wrap.js +127 -0
  32. package/dist/checks/knip-wrap.js.map +1 -0
  33. package/dist/cli/baseline-commands.d.ts +2 -0
  34. package/dist/cli/baseline-commands.js +61 -0
  35. package/dist/cli/baseline-commands.js.map +1 -0
  36. package/dist/cli/emit.d.ts +7 -0
  37. package/dist/cli/emit.js +8 -0
  38. package/dist/cli/emit.js.map +1 -0
  39. package/dist/cli/init/detect.d.ts +8 -0
  40. package/dist/cli/init/detect.js +20 -0
  41. package/dist/cli/init/detect.js.map +1 -0
  42. package/dist/cli/init/git-hook.d.ts +6 -0
  43. package/dist/cli/init/git-hook.js +48 -0
  44. package/dist/cli/init/git-hook.js.map +1 -0
  45. package/dist/cli/init/install-commands.d.ts +6 -0
  46. package/dist/cli/init/install-commands.js +55 -0
  47. package/dist/cli/init/install-commands.js.map +1 -0
  48. package/dist/cli/init/package-scripts.d.ts +6 -0
  49. package/dist/cli/init/package-scripts.js +55 -0
  50. package/dist/cli/init/package-scripts.js.map +1 -0
  51. package/dist/cli/init/prompts.d.ts +9 -0
  52. package/dist/cli/init/prompts.js +33 -0
  53. package/dist/cli/init/prompts.js.map +1 -0
  54. package/dist/cli/init/reporters.d.ts +11 -0
  55. package/dist/cli/init/reporters.js +40 -0
  56. package/dist/cli/init/reporters.js.map +1 -0
  57. package/dist/cli/init/run.d.ts +12 -0
  58. package/dist/cli/init/run.js +159 -0
  59. package/dist/cli/init/run.js.map +1 -0
  60. package/dist/cli/init/scaffold-baseline.d.ts +4 -0
  61. package/dist/cli/init/scaffold-baseline.js +11 -0
  62. package/dist/cli/init/scaffold-baseline.js.map +1 -0
  63. package/dist/cli/init/scaffold-config.d.ts +13 -0
  64. package/dist/cli/init/scaffold-config.js +42 -0
  65. package/dist/cli/init/scaffold-config.js.map +1 -0
  66. package/dist/cli/init/scaffold-eslint-config.d.ts +2 -0
  67. package/dist/cli/init/scaffold-eslint-config.js +12 -0
  68. package/dist/cli/init/scaffold-eslint-config.js.map +1 -0
  69. package/dist/cli/init/scaffold-jscpd-config.d.ts +2 -0
  70. package/dist/cli/init/scaffold-jscpd-config.js +12 -0
  71. package/dist/cli/init/scaffold-jscpd-config.js.map +1 -0
  72. package/dist/cli/init/scaffold-knip-config.d.ts +2 -0
  73. package/dist/cli/init/scaffold-knip-config.js +12 -0
  74. package/dist/cli/init/scaffold-knip-config.js.map +1 -0
  75. package/dist/cli/init/skill.d.ts +7 -0
  76. package/dist/cli/init/skill.js +36 -0
  77. package/dist/cli/init/skill.js.map +1 -0
  78. package/dist/cli/init/snippet.d.ts +1 -0
  79. package/dist/cli/init/snippet.js +18 -0
  80. package/dist/cli/init/snippet.js.map +1 -0
  81. package/dist/cli/init/templates/eslint-config.d.ts +2 -0
  82. package/dist/cli/init/templates/eslint-config.js +35 -0
  83. package/dist/cli/init/templates/eslint-config.js.map +1 -0
  84. package/dist/cli/init/templates/jscpd-config.d.ts +2 -0
  85. package/dist/cli/init/templates/jscpd-config.js +9 -0
  86. package/dist/cli/init/templates/jscpd-config.js.map +1 -0
  87. package/dist/cli/init/templates/knip-config.d.ts +2 -0
  88. package/dist/cli/init/templates/knip-config.js +8 -0
  89. package/dist/cli/init/templates/knip-config.js.map +1 -0
  90. package/dist/cli/init-command.d.ts +2 -0
  91. package/dist/cli/init-command.js +33 -0
  92. package/dist/cli/init-command.js.map +1 -0
  93. package/dist/cli.d.ts +2 -0
  94. package/dist/cli.js +101 -0
  95. package/dist/cli.js.map +1 -0
  96. package/dist/config/defaults.d.ts +4 -0
  97. package/dist/config/defaults.js +172 -0
  98. package/dist/config/defaults.js.map +1 -0
  99. package/dist/config/jiti-loader.d.ts +1 -0
  100. package/dist/config/jiti-loader.js +13 -0
  101. package/dist/config/jiti-loader.js.map +1 -0
  102. package/dist/config/load.d.ts +8 -0
  103. package/dist/config/load.js +53 -0
  104. package/dist/config/load.js.map +1 -0
  105. package/dist/config/merge.d.ts +3 -0
  106. package/dist/config/merge.js +90 -0
  107. package/dist/config/merge.js.map +1 -0
  108. package/dist/config/schema.d.ts +32 -0
  109. package/dist/config/schema.js +4 -0
  110. package/dist/config/schema.js.map +1 -0
  111. package/dist/config/validate.d.ts +2 -0
  112. package/dist/config/validate.js +128 -0
  113. package/dist/config/validate.js.map +1 -0
  114. package/dist/detect/package-json.d.ts +1 -0
  115. package/dist/detect/package-json.js +15 -0
  116. package/dist/detect/package-json.js.map +1 -0
  117. package/dist/detect/tool.d.ts +10 -0
  118. package/dist/detect/tool.js +73 -0
  119. package/dist/detect/tool.js.map +1 -0
  120. package/dist/eslint-runner.d.ts +7 -0
  121. package/dist/eslint-runner.js +34 -0
  122. package/dist/eslint-runner.js.map +1 -0
  123. package/dist/git/exec.d.ts +8 -0
  124. package/dist/git/exec.js +48 -0
  125. package/dist/git/exec.js.map +1 -0
  126. package/dist/git/resolve-scope.d.ts +15 -0
  127. package/dist/git/resolve-scope.js +89 -0
  128. package/dist/git/resolve-scope.js.map +1 -0
  129. package/dist/git/scope.d.ts +7 -0
  130. package/dist/git/scope.js +58 -0
  131. package/dist/git/scope.js.map +1 -0
  132. package/dist/index.d.ts +1 -0
  133. package/dist/index.js +2 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/prompts/loader.d.ts +6 -0
  136. package/dist/prompts/loader.js +27 -0
  137. package/dist/prompts/loader.js.map +1 -0
  138. package/dist/prompts/packaged-dir.d.ts +1 -0
  139. package/dist/prompts/packaged-dir.js +10 -0
  140. package/dist/prompts/packaged-dir.js.map +1 -0
  141. package/dist/prompts/registry.d.ts +3 -0
  142. package/dist/prompts/registry.js +68 -0
  143. package/dist/prompts/registry.js.map +1 -0
  144. package/dist/reporter.d.ts +7 -0
  145. package/dist/reporter.js +114 -0
  146. package/dist/reporter.js.map +1 -0
  147. package/dist/rules/registry.d.ts +3 -0
  148. package/dist/rules/registry.js +37 -0
  149. package/dist/rules/registry.js.map +1 -0
  150. package/dist/runner.d.ts +15 -0
  151. package/dist/runner.js +151 -0
  152. package/dist/runner.js.map +1 -0
  153. package/dist/types.d.ts +41 -0
  154. package/dist/types.js +2 -0
  155. package/dist/types.js.map +1 -0
  156. package/dist/wrap/notices.d.ts +13 -0
  157. package/dist/wrap/notices.js +29 -0
  158. package/dist/wrap/notices.js.map +1 -0
  159. package/dist/wrap/resolve.d.ts +5 -0
  160. package/dist/wrap/resolve.js +9 -0
  161. package/dist/wrap/resolve.js.map +1 -0
  162. package/dist/wrap/run.d.ts +15 -0
  163. package/dist/wrap/run.js +26 -0
  164. package/dist/wrap/run.js.map +1 -0
  165. package/dist/wrap/shell.d.ts +14 -0
  166. package/dist/wrap/shell.js +44 -0
  167. package/dist/wrap/shell.js.map +1 -0
  168. package/package.json +46 -0
  169. package/src/prompts/comment-non-essential.md +7 -0
  170. package/src/prompts/eslint-boundaries-dependencies.md +9 -0
  171. package/src/prompts/eslint-complexity.md +9 -0
  172. package/src/prompts/eslint-eqeqeq.md +3 -0
  173. package/src/prompts/eslint-fatal.md +7 -0
  174. package/src/prompts/eslint-max-lines-per-function.md +9 -0
  175. package/src/prompts/eslint-max-lines.md +9 -0
  176. package/src/prompts/eslint-max-params.md +9 -0
  177. package/src/prompts/eslint-no-duplicate-imports.md +1 -0
  178. package/src/prompts/eslint-no-unused-vars.md +3 -0
  179. package/src/prompts/eslint-no-var.md +1 -0
  180. package/src/prompts/eslint-no-warning-comments.md +3 -0
  181. package/src/prompts/eslint-prefer-const.md +3 -0
  182. package/src/prompts/eslint-typescript-eslint-no-explicit-any.md +3 -0
  183. package/src/prompts/eslint-typescript-eslint-no-inferrable-types.md +3 -0
  184. package/src/prompts/eslint-typescript-eslint-no-non-null-assertion.md +3 -0
  185. package/src/prompts/jscpd-duplication.md +9 -0
  186. package/src/prompts/knip-classMembers.md +7 -0
  187. package/src/prompts/knip-dependencies.md +9 -0
  188. package/src/prompts/knip-exports.md +9 -0
  189. package/src/prompts/knip-files.md +9 -0
  190. package/src/prompts/knip-types.md +9 -0
  191. package/src/prompts/uncoached.md +3 -0
  192. package/src/skills/habit-hooks-review/SKILL.md +108 -0
@@ -0,0 +1,83 @@
1
+ import { Project, SyntaxKind } from 'ts-morph';
2
+ export const DEFAULT_COMMENT_CHECK_THRESHOLDS = {
3
+ maxSingleLineChars: 10,
4
+ maxBlockChars: 15,
5
+ };
6
+ function isExcludedComment(text) {
7
+ return text.includes('eslint-disable');
8
+ }
9
+ function isReportableSingle(text, thresholds) {
10
+ if (!text.startsWith('//'))
11
+ return false;
12
+ if (isExcludedComment(text))
13
+ return false;
14
+ return text.length >= thresholds.maxSingleLineChars;
15
+ }
16
+ function isReportableBlock(text, thresholds) {
17
+ if (!text.startsWith('/*'))
18
+ return false;
19
+ if (isExcludedComment(text))
20
+ return false;
21
+ return text.length >= thresholds.maxBlockChars;
22
+ }
23
+ function truncate(text) {
24
+ const collapsed = text.replace(/\s+/g, ' ').trim();
25
+ return collapsed.length > 50 ? `${collapsed.substring(0, 50)}...` : collapsed;
26
+ }
27
+ function makeViolation(file, comment, kind) {
28
+ return {
29
+ ruleId: 'comment:non-essential',
30
+ file,
31
+ line: comment.getStartLineNumber(),
32
+ message: `${kind}-line comment: "${truncate(comment.getText())}"`,
33
+ };
34
+ }
35
+ function classifyBlock(text) {
36
+ return text.startsWith('/**') ? 'JSDoc' : 'block';
37
+ }
38
+ function collectSingles(source, file, thresholds) {
39
+ return source
40
+ .getDescendantsOfKind(SyntaxKind.SingleLineCommentTrivia)
41
+ .filter((c) => isReportableSingle(c.getText().trim(), thresholds))
42
+ .map((c) => makeViolation(file, c, 'single'));
43
+ }
44
+ function collectBlocks(source, file, thresholds) {
45
+ return source
46
+ .getDescendantsOfKind(SyntaxKind.MultiLineCommentTrivia)
47
+ .filter((c) => isReportableBlock(c.getText().trim(), thresholds))
48
+ .map((c) => makeViolation(file, c, classifyBlock(c.getText().trim())));
49
+ }
50
+ function collectJsDoc(source, file, thresholds) {
51
+ return source
52
+ .getDescendantsOfKind(SyntaxKind.JSDoc)
53
+ .filter((c) => isReportableBlock(c.getText().trim(), thresholds))
54
+ .map((c) => makeViolation(file, c, 'JSDoc'));
55
+ }
56
+ function findCommentsInFile(source, thresholds) {
57
+ const file = source.getFilePath();
58
+ return [
59
+ ...collectSingles(source, file, thresholds),
60
+ ...collectBlocks(source, file, thresholds),
61
+ ...collectJsDoc(source, file, thresholds),
62
+ ];
63
+ }
64
+ function buildProject(files) {
65
+ const project = new Project({ skipAddingFilesFromTsConfig: true });
66
+ project.addSourceFilesAtPaths(files);
67
+ return project;
68
+ }
69
+ function resolveThresholds(rules) {
70
+ const rule = rules.find((r) => r.id === 'comment:non-essential');
71
+ return rule?.commentCheck ?? DEFAULT_COMMENT_CHECK_THRESHOLDS;
72
+ }
73
+ export const commentCheck = {
74
+ id: 'comment',
75
+ async run(files, rules) {
76
+ if (files.length === 0)
77
+ return [];
78
+ const thresholds = resolveThresholds(rules);
79
+ const project = buildProject(files);
80
+ return project.getSourceFiles().flatMap((s) => findCommentsInFile(s, thresholds));
81
+ },
82
+ };
83
+ //# sourceMappingURL=comment-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-check.js","sourceRoot":"","sources":["../../src/checks/comment-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAA8B,MAAM,UAAU,CAAC;AAG3E,MAAM,CAAC,MAAM,gCAAgC,GAA2B;IACtE,kBAAkB,EAAE,EAAE;IACtB,aAAa,EAAE,EAAE;CAClB,CAAC;AAIF,SAAS,iBAAiB,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY,EAAE,UAAkC;IAC1E,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,iBAAiB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC,kBAAkB,CAAC;AACtD,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY,EAAE,UAAkC;IACzE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,iBAAiB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC,aAAa,CAAC;AACjD,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACnD,OAAO,SAAS,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAChF,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,OAAa,EAAE,IAAiB;IACnE,OAAO;QACL,MAAM,EAAE,uBAAuB;QAC/B,IAAI;QACJ,IAAI,EAAE,OAAO,CAAC,kBAAkB,EAAE;QAClC,OAAO,EAAE,GAAG,IAAI,mBAAmB,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG;KAClE,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;AACpD,CAAC;AAED,SAAS,cAAc,CAAC,MAAkB,EAAE,IAAY,EAAE,UAAkC;IAC1F,OAAO,MAAM;SACV,oBAAoB,CAAC,UAAU,CAAC,uBAAuB,CAAC;SACxD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC;SACjE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,aAAa,CAAC,MAAkB,EAAE,IAAY,EAAE,UAAkC;IACzF,OAAO,MAAM;SACV,oBAAoB,CAAC,UAAU,CAAC,sBAAsB,CAAC;SACvD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC;SAChE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,YAAY,CAAC,MAAkB,EAAE,IAAY,EAAE,UAAkC;IACxF,OAAO,MAAM;SACV,oBAAoB,CAAC,UAAU,CAAC,KAAK,CAAC;SACtC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC;SAChE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAkB,EAAE,UAAkC;IAChF,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAClC,OAAO;QACL,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC;QAC3C,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC;QAC1C,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAAe;IACnC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,2BAA2B,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,OAAO,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACrC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,uBAAuB,CAAC,CAAC;IACjE,OAAO,IAAI,EAAE,YAAY,IAAI,gCAAgC,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAU;IACjC,EAAE,EAAE,SAAS;IACb,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK;QACpB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACpC,OAAO,OAAO,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IACpF,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Check } from '../types.js';
2
+ export declare const eslintWrap: Check;
@@ -0,0 +1,104 @@
1
+ import { createRequire } from 'node:module';
2
+ import { dirname, join } from 'node:path';
3
+ import { runTool } from '../wrap/shell.js';
4
+ import { detectTool } from '../detect/tool.js';
5
+ import { lookupPrompt } from '../prompts/registry.js';
6
+ import { emptyOutcome, firstLine, isSpawnFailure, noticesFor, spawnFailureWarning, } from '../wrap/notices.js';
7
+ import { spawnTarget } from '../wrap/resolve.js';
8
+ import { parseJsonStdout } from '../wrap/run.js';
9
+ const require = createRequire(import.meta.url);
10
+ function bundledEslintBin() {
11
+ const main = require.resolve('eslint');
12
+ return join(dirname(main), '..', 'bin', 'eslint.js');
13
+ }
14
+ function resolveEslintBin(cwd) {
15
+ const detected = detectTool(cwd, 'eslint');
16
+ if (detected !== null)
17
+ return { binPath: detected.binPath, isFallback: false };
18
+ return { binPath: bundledEslintBin(), isFallback: true };
19
+ }
20
+ function configWarning(cwd, detail) {
21
+ const suffix = detail.length > 0 ? `: ${detail}` : '';
22
+ return `habit-hooks: eslint skipped in ${cwd} (config error)${suffix}`;
23
+ }
24
+ function tryParseJson(stdout) {
25
+ return parseJsonStdout(stdout, '[');
26
+ }
27
+ function isConfigError(result, parsed) {
28
+ if (parsed !== null)
29
+ return false;
30
+ if (isSpawnFailure(result))
31
+ return false;
32
+ return result.exitCode !== 0 && result.exitCode !== 1;
33
+ }
34
+ function messageToViolation(filePath, m) {
35
+ const ruleId = `eslint:${m.ruleId}`;
36
+ const prompt = lookupPrompt(ruleId);
37
+ const title = prompt?.title ?? m.ruleId;
38
+ return { ruleId, file: filePath, line: m.line, column: m.column, message: `${title}: ${m.message}` };
39
+ }
40
+ function fatalToViolation(filePath, m) {
41
+ return {
42
+ ruleId: 'eslint:fatal',
43
+ file: filePath,
44
+ line: m.line,
45
+ column: m.column,
46
+ message: `Fatal parse/config error: ${m.message}`,
47
+ };
48
+ }
49
+ function isFatalMessage(m) {
50
+ if (m.ruleId !== null)
51
+ return false;
52
+ if (m.fatal === true)
53
+ return true;
54
+ return m.severity === 2;
55
+ }
56
+ function toViolation(filePath, m) {
57
+ if (isFatalMessage(m))
58
+ return fatalToViolation(filePath, m);
59
+ if (m.ruleId === null)
60
+ return null;
61
+ return messageToViolation(filePath, { ...m, ruleId: m.ruleId });
62
+ }
63
+ function fileResultToViolations(result) {
64
+ return result.messages
65
+ .map((m) => toViolation(result.filePath, m))
66
+ .filter((v) => v !== null);
67
+ }
68
+ function parseEslintJson(stdout) {
69
+ const parsed = tryParseJson(stdout);
70
+ if (parsed === null)
71
+ return [];
72
+ return parsed.flatMap(fileResultToViolations);
73
+ }
74
+ function buildArgs(files) {
75
+ return ['--format', 'json', ...files];
76
+ }
77
+ async function executeEslint(resolution, cwd, files) {
78
+ const target = spawnTarget(resolution.binPath, buildArgs(files));
79
+ return runTool({ bin: target.bin, args: target.args, cwd });
80
+ }
81
+ function failureNotices(cwd, result) {
82
+ const detail = firstLine(result.stderr.length > 0 ? result.stderr : result.stdout);
83
+ return [configWarning(cwd, detail)];
84
+ }
85
+ async function runEslint(resolution, cwd, files) {
86
+ const notices = noticesFor('eslint', resolution, cwd);
87
+ const result = await executeEslint(resolution, cwd, files);
88
+ const parsed = tryParseJson(result.stdout);
89
+ if (isSpawnFailure(result))
90
+ return emptyOutcome([...notices, spawnFailureWarning('eslint', cwd, result.warnings)]);
91
+ if (isConfigError(result, parsed))
92
+ return emptyOutcome([...notices, ...failureNotices(cwd, result)]);
93
+ return { violations: parseEslintJson(result.stdout), stderr: notices };
94
+ }
95
+ export const eslintWrap = {
96
+ id: 'eslint',
97
+ async run(files, _rules, cwd) {
98
+ const runCwd = cwd ?? process.cwd();
99
+ if (files.length === 0)
100
+ return { violations: [], stderr: [] };
101
+ return runEslint(resolveEslintBin(runCwd), runCwd, files);
102
+ },
103
+ };
104
+ //# sourceMappingURL=eslint-wrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eslint-wrap.js","sourceRoot":"","sources":["../../src/checks/eslint-wrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type BinResolution } from '../wrap/notices.js';
2
+ import type { Check, CheckOutcome } from '../types.js';
3
+ export declare function tryBundledJscpdBin(resolver?: () => string): string | null;
4
+ export declare function resolveJscpdBin(cwd: string, fallbackResolver?: () => string): BinResolution | null;
5
+ export declare function runJscpdWrap(files: string[], cwd: string, resolution: BinResolution | null): Promise<CheckOutcome>;
6
+ export declare const jscpdWrap: Check;
@@ -0,0 +1,158 @@
1
+ import { createRequire } from 'node:module';
2
+ import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import { detectTool } from '../detect/tool.js';
6
+ import { hasPackageJsonKey } from '../detect/package-json.js';
7
+ import { absolutize, emptyOutcome, firstLine, noticesFor } from '../wrap/notices.js';
8
+ import { isSpawnSkip, spawnWrapped } from '../wrap/run.js';
9
+ const require = createRequire(import.meta.url);
10
+ const RULE_ID = 'jscpd:duplication';
11
+ const REPORT_FILENAME = 'jscpd-report.json';
12
+ const JSCPD_CONFIG_FILES = ['.jscpd.json', 'jscpd.json'];
13
+ function findPackageRoot(start) {
14
+ let dir = start;
15
+ while (dir !== dirname(dir)) {
16
+ if (existsSync(join(dir, 'package.json')))
17
+ return dir;
18
+ dir = dirname(dir);
19
+ }
20
+ throw new Error(`Could not find package.json from ${start}`);
21
+ }
22
+ function bundledJscpdBin() {
23
+ const main = require.resolve('jscpd');
24
+ const pkgRoot = findPackageRoot(dirname(main));
25
+ const pkg = JSON.parse(readFileSync(join(pkgRoot, 'package.json'), 'utf8'));
26
+ const binRel = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.jscpd ?? 'bin/jscpd';
27
+ return join(pkgRoot, binRel);
28
+ }
29
+ export function tryBundledJscpdBin(resolver = bundledJscpdBin) {
30
+ try {
31
+ return resolver();
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ export function resolveJscpdBin(cwd, fallbackResolver = bundledJscpdBin) {
38
+ const detected = detectTool(cwd, 'jscpd');
39
+ if (detected !== null)
40
+ return { binPath: detected.binPath, isFallback: false };
41
+ const fallback = tryBundledJscpdBin(fallbackResolver);
42
+ if (fallback === null)
43
+ return null;
44
+ return { binPath: fallback, isFallback: true };
45
+ }
46
+ function reportMissingWarning(cwd, code, stderr) {
47
+ const detail = firstLine(stderr);
48
+ const suffix = detail.length > 0 ? `: ${detail}` : '';
49
+ return `habit-hooks: jscpd skipped in ${cwd} (exit ${code}, no report)${suffix}`;
50
+ }
51
+ function parseFailureWarning(cwd) {
52
+ return `habit-hooks: jscpd skipped in ${cwd} (unparseable report)`;
53
+ }
54
+ function unresolvedBinWarning(cwd) {
55
+ return `habit-hooks: jscpd skipped in ${cwd} (could not locate bundled bin)`;
56
+ }
57
+ function locationDescription(loc, cwd) {
58
+ return `${absolutize(cwd, loc.name)}:${loc.startLoc.line}-${loc.endLoc.line}`;
59
+ }
60
+ function buildViolation(self, partner, cwd) {
61
+ return {
62
+ ruleId: RULE_ID,
63
+ file: absolutize(cwd, self.name),
64
+ line: self.startLoc.line,
65
+ column: self.startLoc.column,
66
+ message: `duplicates ${locationDescription(partner, cwd)}`,
67
+ };
68
+ }
69
+ function isInScope(loc, scope, cwd) {
70
+ return scope.has(absolutize(cwd, loc.name));
71
+ }
72
+ function cloneToViolations(clone, scope, cwd) {
73
+ const violations = [];
74
+ if (isInScope(clone.firstFile, scope, cwd)) {
75
+ violations.push(buildViolation(clone.firstFile, clone.secondFile, cwd));
76
+ }
77
+ if (isInScope(clone.secondFile, scope, cwd)) {
78
+ violations.push(buildViolation(clone.secondFile, clone.firstFile, cwd));
79
+ }
80
+ return violations;
81
+ }
82
+ function reportToViolations(report, scope, cwd) {
83
+ return (report.duplicates ?? []).flatMap((c) => cloneToViolations(c, scope, cwd));
84
+ }
85
+ function tryReadReport(reportDir) {
86
+ const path = join(reportDir, REPORT_FILENAME);
87
+ if (!existsSync(path))
88
+ return null;
89
+ try {
90
+ return JSON.parse(readFileSync(path, 'utf8'));
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ function buildArgs(reportDir) {
97
+ return ['-r', 'json', '-o', reportDir, '--silent', '--noTips', '-n', '.'];
98
+ }
99
+ function makeReportDir() {
100
+ return mkdtempSync(join(tmpdir(), 'hh-jscpd-'));
101
+ }
102
+ function removeReportDir(reportDir) {
103
+ rmSync(reportDir, { recursive: true, force: true });
104
+ }
105
+ function missingReportOutcome(inputs, result) {
106
+ if (result.exitCode !== 0) {
107
+ return emptyOutcome([...inputs.notices, reportMissingWarning(inputs.cwd, result.exitCode, result.stderr)]);
108
+ }
109
+ return emptyOutcome([...inputs.notices, parseFailureWarning(inputs.cwd)]);
110
+ }
111
+ async function runOnce(inputs, reportDir) {
112
+ const { resolution, cwd } = inputs;
113
+ const result = await spawnWrapped({ tool: 'jscpd', resolution, cwd, args: buildArgs(reportDir) });
114
+ if (isSpawnSkip(result))
115
+ return emptyOutcome([...inputs.notices, result.skipWarning]);
116
+ const report = tryReadReport(reportDir);
117
+ if (report === null)
118
+ return missingReportOutcome(inputs, result);
119
+ return { violations: reportToViolations(report, inputs.scope, inputs.cwd), stderr: inputs.notices };
120
+ }
121
+ async function runJscpd(inputs) {
122
+ const reportDir = makeReportDir();
123
+ try {
124
+ return await runOnce(inputs, reportDir);
125
+ }
126
+ finally {
127
+ removeReportDir(reportDir);
128
+ }
129
+ }
130
+ function hasJscpdConfig(cwd) {
131
+ if (JSCPD_CONFIG_FILES.some((name) => existsSync(join(cwd, name))))
132
+ return true;
133
+ return hasPackageJsonKey(cwd, 'jscpd');
134
+ }
135
+ function noConfigOutcome(cwd, notices) {
136
+ return emptyOutcome([...notices, `habit-hooks: jscpd skipped in ${cwd} (no jscpd config)`]);
137
+ }
138
+ function noBinOutcome(cwd) {
139
+ return emptyOutcome([unresolvedBinWarning(cwd)]);
140
+ }
141
+ export async function runJscpdWrap(files, cwd, resolution) {
142
+ if (files.length === 0)
143
+ return { violations: [], stderr: [] };
144
+ if (resolution === null)
145
+ return noBinOutcome(cwd);
146
+ const notices = noticesFor('jscpd', resolution, cwd);
147
+ if (!hasJscpdConfig(cwd))
148
+ return noConfigOutcome(cwd, notices);
149
+ return runJscpd({ resolution, cwd, scope: new Set(files), notices });
150
+ }
151
+ export const jscpdWrap = {
152
+ id: 'jscpd',
153
+ async run(files, _rules, cwd) {
154
+ const runCwd = cwd ?? process.cwd();
155
+ return runJscpdWrap(files, runCwd, resolveJscpdBin(runCwd));
156
+ },
157
+ };
158
+ //# sourceMappingURL=jscpd-wrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jscpd-wrap.js","sourceRoot":"","sources":["../../src/checks/jscpd-wrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type BinResolution } from '../wrap/notices.js';
2
+ export declare function resolveKnipBin(cwd: string): BinResolution;
3
+ export declare function consumerKnipMajor(cwd: string): number | null;
4
+ export declare function buildKnipArgs(resolution: BinResolution, cwd: string): string[];
@@ -0,0 +1,49 @@
1
+ import { createRequire } from 'node:module';
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { detectTool } from '../detect/tool.js';
5
+ const require = createRequire(import.meta.url);
6
+ const KNIP_BASE_ARGS = ['--reporter', 'json', '--no-exit-code'];
7
+ function bundledKnipDir() {
8
+ const main = require.resolve('knip');
9
+ return join(dirname(main), '..');
10
+ }
11
+ function bundledKnipBin() {
12
+ return join(bundledKnipDir(), 'bin', 'knip.js');
13
+ }
14
+ export function resolveKnipBin(cwd) {
15
+ const detected = detectTool(cwd, 'knip');
16
+ if (detected !== null)
17
+ return { binPath: detected.binPath, isFallback: false };
18
+ return { binPath: bundledKnipBin(), isFallback: true };
19
+ }
20
+ function readMajorFromPackageJson(path) {
21
+ try {
22
+ const pkg = JSON.parse(readFileSync(path, 'utf8'));
23
+ if (typeof pkg.version !== 'string')
24
+ return null;
25
+ const major = Number.parseInt(pkg.version.split('.')[0] ?? '', 10);
26
+ return Number.isFinite(major) ? major : null;
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ export function consumerKnipMajor(cwd) {
33
+ return readMajorFromPackageJson(join(cwd, 'node_modules', 'knip', 'package.json'));
34
+ }
35
+ function bundledKnipMajor() {
36
+ return readMajorFromPackageJson(join(bundledKnipDir(), 'package.json'));
37
+ }
38
+ function effectiveKnipMajor(resolution, cwd) {
39
+ if (resolution.isFallback)
40
+ return bundledKnipMajor();
41
+ return consumerKnipMajor(cwd);
42
+ }
43
+ export function buildKnipArgs(resolution, cwd) {
44
+ const major = effectiveKnipMajor(resolution, cwd);
45
+ if (major !== null && major >= 6)
46
+ return [...KNIP_BASE_ARGS];
47
+ return [...KNIP_BASE_ARGS, '--include', 'classMembers'];
48
+ }
49
+ //# sourceMappingURL=knip-resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knip-resolve.js","sourceRoot":"","sources":["../../src/checks/knip-resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,cAAc,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEhE,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,IAAI,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACzC,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC/E,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AACzD,CAAC;AAED,SAAS,wBAAwB,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAA0B,CAAC;QAC5E,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACjD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACnE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,wBAAwB,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,gBAAgB;IACvB,OAAO,wBAAwB,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,kBAAkB,CAAC,UAAyB,EAAE,GAAW;IAChE,IAAI,UAAU,CAAC,UAAU;QAAE,OAAO,gBAAgB,EAAE,CAAC;IACrD,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAyB,EAAE,GAAW;IAClE,MAAM,KAAK,GAAG,kBAAkB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAClD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,CAAC,GAAG,cAAc,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,cAAc,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,32 @@
1
+ export interface KnipLocation {
2
+ name: string;
3
+ line?: number;
4
+ col?: number;
5
+ }
6
+ export type KnipMemberMap = Record<string, KnipLocation[]>;
7
+ export interface KnipIssue {
8
+ file: string;
9
+ files?: KnipLocation[];
10
+ dependencies?: KnipLocation[];
11
+ devDependencies?: KnipLocation[];
12
+ optionalPeerDependencies?: KnipLocation[];
13
+ unlisted?: KnipLocation[];
14
+ binaries?: KnipLocation[];
15
+ unresolved?: KnipLocation[];
16
+ exports?: KnipLocation[];
17
+ nsExports?: KnipLocation[];
18
+ types?: KnipLocation[];
19
+ nsTypes?: KnipLocation[];
20
+ duplicates?: KnipLocation[][];
21
+ enumMembers?: KnipMemberMap;
22
+ classMembers?: KnipMemberMap;
23
+ namespaceMembers?: KnipMemberMap;
24
+ catalog?: KnipLocation[];
25
+ }
26
+ export interface KnipReport {
27
+ files?: string[];
28
+ issues?: KnipIssue[];
29
+ }
30
+ export declare const LOCATION_KEYS: (keyof KnipIssue)[];
31
+ export declare const MEMBER_KEYS: (keyof KnipIssue)[];
32
+ export declare const KNOWN_KEYS: Set<string>;
@@ -0,0 +1,24 @@
1
+ export const LOCATION_KEYS = [
2
+ 'files',
3
+ 'dependencies',
4
+ 'devDependencies',
5
+ 'optionalPeerDependencies',
6
+ 'unlisted',
7
+ 'binaries',
8
+ 'unresolved',
9
+ 'exports',
10
+ 'nsExports',
11
+ 'types',
12
+ 'nsTypes',
13
+ 'catalog',
14
+ ];
15
+ export const MEMBER_KEYS = ['enumMembers', 'classMembers', 'namespaceMembers'];
16
+ const STRUCTURAL_KEYS = new Set(['file']);
17
+ const SPECIAL_KEYS = new Set(['duplicates']);
18
+ export const KNOWN_KEYS = new Set([
19
+ ...STRUCTURAL_KEYS,
20
+ ...SPECIAL_KEYS,
21
+ ...LOCATION_KEYS,
22
+ ...MEMBER_KEYS,
23
+ ]);
24
+ //# sourceMappingURL=knip-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knip-schema.js","sourceRoot":"","sources":["../../src/checks/knip-schema.ts"],"names":[],"mappings":"AAiCA,MAAM,CAAC,MAAM,aAAa,GAAwB;IAChD,OAAO;IACP,cAAc;IACd,iBAAiB;IACjB,0BAA0B;IAC1B,UAAU;IACV,UAAU;IACV,YAAY;IACZ,SAAS;IACT,WAAW;IACX,OAAO;IACP,SAAS;IACT,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAwB,CAAC,aAAa,EAAE,cAAc,EAAE,kBAAkB,CAAC,CAAC;AAEpG,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAClD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,CAAC,YAAY,CAAC,CAAC,CAAC;AAErD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAS;IACxC,GAAG,eAAe;IAClB,GAAG,YAAY;IACf,GAAI,aAA0B;IAC9B,GAAI,WAAwB;CAC7B,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { buildKnipArgs, consumerKnipMajor, resolveKnipBin } from './knip-resolve.js';
2
+ import type { Check } from '../types.js';
3
+ export { buildKnipArgs, consumerKnipMajor, resolveKnipBin };
4
+ export declare const knipWrap: Check;
@@ -0,0 +1,127 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { hasPackageJsonKey } from '../detect/package-json.js';
4
+ import { absolutize, emptyOutcome, firstLine, noticesFor } from '../wrap/notices.js';
5
+ import { isSpawnSkip, parseJsonStdout, spawnWrapped } from '../wrap/run.js';
6
+ import { buildKnipArgs, consumerKnipMajor, resolveKnipBin } from './knip-resolve.js';
7
+ import { KNOWN_KEYS, LOCATION_KEYS, MEMBER_KEYS, } from './knip-schema.js';
8
+ export { buildKnipArgs, consumerKnipMajor, resolveKnipBin };
9
+ const KNIP_CONFIG_FILES = ['knip.json', 'knip.jsonc', 'knip.ts', 'knip.js'];
10
+ function exitFailureWarning(cwd, code, stderr) {
11
+ const detail = firstLine(stderr);
12
+ const suffix = detail.length > 0 ? `: ${detail}` : '';
13
+ return `habit-hooks: knip skipped in ${cwd} (exit ${code})${suffix}`;
14
+ }
15
+ function buildViolation(args) {
16
+ const { ruleId, file, message, loc } = args;
17
+ return { ruleId, file, line: loc.line ?? 1, column: loc.col, message };
18
+ }
19
+ function locationToViolation(ctx, loc) {
20
+ const ruleId = `knip:${ctx.issueType}`;
21
+ return buildViolation({ ruleId, file: absolutize(ctx.cwd, ctx.issueFile), message: loc.name, loc });
22
+ }
23
+ function memberToViolation(ctx, owner, loc) {
24
+ const ruleId = `knip:${ctx.issueType}`;
25
+ return buildViolation({ ruleId, file: absolutize(ctx.cwd, ctx.issueFile), message: `${owner}.${loc.name}`, loc });
26
+ }
27
+ function flattenMemberMap(ctx, members) {
28
+ return Object.entries(members).flatMap(([owner, list]) => list.map((loc) => memberToViolation(ctx, owner, loc)));
29
+ }
30
+ function buildIssueContext(cwd, issue, key) {
31
+ return { cwd, issueType: key, issueFile: issue.file };
32
+ }
33
+ function locationsForKey(issue, key, cwd) {
34
+ const value = issue[key];
35
+ if (!Array.isArray(value) || value.length === 0)
36
+ return [];
37
+ const ctx = buildIssueContext(cwd, issue, key);
38
+ return value.map((loc) => locationToViolation(ctx, loc));
39
+ }
40
+ function membersForKey(issue, key, cwd) {
41
+ const value = issue[key];
42
+ if (value === undefined || value === null || Array.isArray(value))
43
+ return [];
44
+ return flattenMemberMap(buildIssueContext(cwd, issue, key), value);
45
+ }
46
+ function warnDuplicatesIfPresent(issue, cwd) {
47
+ if (issue.duplicates === undefined || issue.duplicates.length === 0)
48
+ return;
49
+ const file = absolutize(cwd, issue.file);
50
+ process.stderr.write(`habit-hooks: knip duplicates issue ignored in ${file} (not yet supported)\n`);
51
+ }
52
+ function isPopulated(value) {
53
+ if (value === undefined || value === null)
54
+ return false;
55
+ if (Array.isArray(value))
56
+ return value.length > 0;
57
+ if (typeof value === 'object')
58
+ return Object.keys(value).length > 0;
59
+ return true;
60
+ }
61
+ function unknownKeyToViolation(cwd, issue, key) {
62
+ const ruleId = `knip:${key}`;
63
+ const file = absolutize(cwd, issue.file);
64
+ const message = 'unrecognised knip issue type';
65
+ return { ruleId, file, line: 1, message };
66
+ }
67
+ function unknownKeysForIssue(issue, cwd) {
68
+ const record = issue;
69
+ return Object.keys(record)
70
+ .filter((k) => !KNOWN_KEYS.has(k) && isPopulated(record[k]))
71
+ .map((k) => unknownKeyToViolation(cwd, issue, k));
72
+ }
73
+ function issueToViolations(issue, cwd) {
74
+ warnDuplicatesIfPresent(issue, cwd);
75
+ const fromLocations = LOCATION_KEYS.flatMap((k) => locationsForKey(issue, k, cwd));
76
+ const fromMembers = MEMBER_KEYS.flatMap((k) => membersForKey(issue, k, cwd));
77
+ const fromUnknown = unknownKeysForIssue(issue, cwd);
78
+ return [...fromLocations, ...fromMembers, ...fromUnknown];
79
+ }
80
+ function fileEntryToViolation(cwd, file) {
81
+ const ruleId = 'knip:files';
82
+ return { ruleId, file: absolutize(cwd, file), line: 1, message: file };
83
+ }
84
+ function reportToViolations(report, cwd) {
85
+ const filesViolations = (report.files ?? []).map((f) => fileEntryToViolation(cwd, f));
86
+ const issuesViolations = (report.issues ?? []).flatMap((i) => issueToViolations(i, cwd));
87
+ return [...filesViolations, ...issuesViolations];
88
+ }
89
+ function hasPackageJson(cwd) {
90
+ return existsSync(join(cwd, 'package.json'));
91
+ }
92
+ function hasKnipConfig(cwd) {
93
+ if (KNIP_CONFIG_FILES.some((name) => existsSync(join(cwd, name))))
94
+ return true;
95
+ return hasPackageJsonKey(cwd, 'knip');
96
+ }
97
+ async function runKnip(resolution, cwd, notices) {
98
+ const result = await spawnWrapped({ tool: 'knip', resolution, cwd, args: buildKnipArgs(resolution, cwd) });
99
+ if (isSpawnSkip(result))
100
+ return emptyOutcome([...notices, result.skipWarning]);
101
+ const report = parseJsonStdout(result.stdout, '{');
102
+ if (report === null)
103
+ return emptyOutcome([...notices, exitFailureWarning(cwd, result.exitCode, result.stderr)]);
104
+ return { violations: reportToViolations(report, cwd), stderr: notices };
105
+ }
106
+ function noPackageJsonOutcome(cwd, notices) {
107
+ return emptyOutcome([...notices, `habit-hooks: knip skipped in ${cwd} (no package.json)`]);
108
+ }
109
+ function noConfigOutcome(cwd, notices) {
110
+ return emptyOutcome([...notices, `habit-hooks: knip skipped in ${cwd} (no knip config)`]);
111
+ }
112
+ export const knipWrap = {
113
+ id: 'knip',
114
+ async run(files, _rules, cwd) {
115
+ const runCwd = cwd ?? process.cwd();
116
+ if (files.length === 0)
117
+ return { violations: [], stderr: [] };
118
+ const resolution = resolveKnipBin(runCwd);
119
+ const notices = noticesFor('knip', resolution, runCwd);
120
+ if (!hasPackageJson(runCwd))
121
+ return noPackageJsonOutcome(runCwd, notices);
122
+ if (!hasKnipConfig(runCwd))
123
+ return noConfigOutcome(runCwd, notices);
124
+ return runKnip(resolution, runCwd, notices);
125
+ },
126
+ };
127
+ //# sourceMappingURL=knip-wrap.js.map