distyll 0.1.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 (243) hide show
  1. package/CONTRIBUTING.md +159 -0
  2. package/POSTMORTEM.json +60 -0
  3. package/README.md +218 -0
  4. package/SETUP.md +79 -0
  5. package/action.yml +37 -0
  6. package/dist/cache.d.ts +26 -0
  7. package/dist/cache.d.ts.map +1 -0
  8. package/dist/cache.js +115 -0
  9. package/dist/cache.js.map +1 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +153 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands/ci.d.ts +7 -0
  15. package/dist/commands/ci.d.ts.map +1 -0
  16. package/dist/commands/ci.js +101 -0
  17. package/dist/commands/ci.js.map +1 -0
  18. package/dist/commands/diff.d.ts +10 -0
  19. package/dist/commands/diff.d.ts.map +1 -0
  20. package/dist/commands/diff.js +95 -0
  21. package/dist/commands/diff.js.map +1 -0
  22. package/dist/commands/fingerprint.d.ts +2 -0
  23. package/dist/commands/fingerprint.d.ts.map +1 -0
  24. package/dist/commands/fingerprint.js +77 -0
  25. package/dist/commands/fingerprint.js.map +1 -0
  26. package/dist/commands/hook.d.ts +3 -0
  27. package/dist/commands/hook.d.ts.map +1 -0
  28. package/dist/commands/hook.js +110 -0
  29. package/dist/commands/hook.js.map +1 -0
  30. package/dist/commands/init.d.ts +2 -0
  31. package/dist/commands/init.d.ts.map +1 -0
  32. package/dist/commands/init.js +75 -0
  33. package/dist/commands/init.js.map +1 -0
  34. package/dist/config.d.ts +7 -0
  35. package/dist/config.d.ts.map +1 -0
  36. package/dist/config.js +100 -0
  37. package/dist/config.js.map +1 -0
  38. package/dist/errors.d.ts +30 -0
  39. package/dist/errors.d.ts.map +1 -0
  40. package/dist/errors.js +133 -0
  41. package/dist/errors.js.map +1 -0
  42. package/dist/fingerprint/analyzer.d.ts +3 -0
  43. package/dist/fingerprint/analyzer.d.ts.map +1 -0
  44. package/dist/fingerprint/analyzer.js +230 -0
  45. package/dist/fingerprint/analyzer.js.map +1 -0
  46. package/dist/fingerprint/comparator.d.ts +4 -0
  47. package/dist/fingerprint/comparator.d.ts.map +1 -0
  48. package/dist/fingerprint/comparator.js +78 -0
  49. package/dist/fingerprint/comparator.js.map +1 -0
  50. package/dist/fingerprint/profile.d.ts +5 -0
  51. package/dist/fingerprint/profile.d.ts.map +1 -0
  52. package/dist/fingerprint/profile.js +68 -0
  53. package/dist/fingerprint/profile.js.map +1 -0
  54. package/dist/fixes/index.d.ts +12 -0
  55. package/dist/fixes/index.d.ts.map +1 -0
  56. package/dist/fixes/index.js +42 -0
  57. package/dist/fixes/index.js.map +1 -0
  58. package/dist/fixes/single-use-wrapper.d.ts +8 -0
  59. package/dist/fixes/single-use-wrapper.d.ts.map +1 -0
  60. package/dist/fixes/single-use-wrapper.js +54 -0
  61. package/dist/fixes/single-use-wrapper.js.map +1 -0
  62. package/dist/fixes/unnecessary-try-catch.d.ts +8 -0
  63. package/dist/fixes/unnecessary-try-catch.d.ts.map +1 -0
  64. package/dist/fixes/unnecessary-try-catch.js +37 -0
  65. package/dist/fixes/unnecessary-try-catch.js.map +1 -0
  66. package/dist/fixes/unused-imports.d.ts +7 -0
  67. package/dist/fixes/unused-imports.d.ts.map +1 -0
  68. package/dist/fixes/unused-imports.js +41 -0
  69. package/dist/fixes/unused-imports.js.map +1 -0
  70. package/dist/fixes/verbose-comments.d.ts +7 -0
  71. package/dist/fixes/verbose-comments.d.ts.map +1 -0
  72. package/dist/fixes/verbose-comments.js +29 -0
  73. package/dist/fixes/verbose-comments.js.map +1 -0
  74. package/dist/formatter.d.ts +4 -0
  75. package/dist/formatter.d.ts.map +1 -0
  76. package/dist/formatter.js +72 -0
  77. package/dist/formatter.js.map +1 -0
  78. package/dist/git.d.ts +22 -0
  79. package/dist/git.d.ts.map +1 -0
  80. package/dist/git.js +130 -0
  81. package/dist/git.js.map +1 -0
  82. package/dist/index.d.ts +16 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +40 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/languages/index.d.ts +8 -0
  87. package/dist/languages/index.d.ts.map +1 -0
  88. package/dist/languages/index.js +50 -0
  89. package/dist/languages/index.js.map +1 -0
  90. package/dist/languages/javascript.d.ts +6 -0
  91. package/dist/languages/javascript.d.ts.map +1 -0
  92. package/dist/languages/javascript.js +39 -0
  93. package/dist/languages/javascript.js.map +1 -0
  94. package/dist/languages/python.d.ts +6 -0
  95. package/dist/languages/python.d.ts.map +1 -0
  96. package/dist/languages/python.js +50 -0
  97. package/dist/languages/python.js.map +1 -0
  98. package/dist/parser.d.ts +8 -0
  99. package/dist/parser.d.ts.map +1 -0
  100. package/dist/parser.js +55 -0
  101. package/dist/parser.js.map +1 -0
  102. package/dist/reporters/github.d.ts +4 -0
  103. package/dist/reporters/github.d.ts.map +1 -0
  104. package/dist/reporters/github.js +70 -0
  105. package/dist/reporters/github.js.map +1 -0
  106. package/dist/reporters/terminal.d.ts +4 -0
  107. package/dist/reporters/terminal.d.ts.map +1 -0
  108. package/dist/reporters/terminal.js +59 -0
  109. package/dist/reporters/terminal.js.map +1 -0
  110. package/dist/rules/dead-code-paths.d.ts +3 -0
  111. package/dist/rules/dead-code-paths.d.ts.map +1 -0
  112. package/dist/rules/dead-code-paths.js +57 -0
  113. package/dist/rules/dead-code-paths.js.map +1 -0
  114. package/dist/rules/excessive-comments.d.ts +3 -0
  115. package/dist/rules/excessive-comments.d.ts.map +1 -0
  116. package/dist/rules/excessive-comments.js +86 -0
  117. package/dist/rules/excessive-comments.js.map +1 -0
  118. package/dist/rules/hallucinated-imports.d.ts +3 -0
  119. package/dist/rules/hallucinated-imports.d.ts.map +1 -0
  120. package/dist/rules/hallucinated-imports.js +228 -0
  121. package/dist/rules/hallucinated-imports.js.map +1 -0
  122. package/dist/rules/index.d.ts +4 -0
  123. package/dist/rules/index.d.ts.map +1 -0
  124. package/dist/rules/index.js +34 -0
  125. package/dist/rules/index.js.map +1 -0
  126. package/dist/rules/magic-values.d.ts +3 -0
  127. package/dist/rules/magic-values.d.ts.map +1 -0
  128. package/dist/rules/magic-values.js +168 -0
  129. package/dist/rules/magic-values.js.map +1 -0
  130. package/dist/rules/near-duplicate-functions.d.ts +3 -0
  131. package/dist/rules/near-duplicate-functions.d.ts.map +1 -0
  132. package/dist/rules/near-duplicate-functions.js +78 -0
  133. package/dist/rules/near-duplicate-functions.js.map +1 -0
  134. package/dist/rules/over-defensive-nulls.d.ts +3 -0
  135. package/dist/rules/over-defensive-nulls.d.ts.map +1 -0
  136. package/dist/rules/over-defensive-nulls.js +129 -0
  137. package/dist/rules/over-defensive-nulls.js.map +1 -0
  138. package/dist/rules/redundant-else-return.d.ts +3 -0
  139. package/dist/rules/redundant-else-return.d.ts.map +1 -0
  140. package/dist/rules/redundant-else-return.js +57 -0
  141. package/dist/rules/redundant-else-return.js.map +1 -0
  142. package/dist/rules/single-option-object.d.ts +3 -0
  143. package/dist/rules/single-option-object.d.ts.map +1 -0
  144. package/dist/rules/single-option-object.js +88 -0
  145. package/dist/rules/single-option-object.js.map +1 -0
  146. package/dist/rules/single-use-wrapper.d.ts +3 -0
  147. package/dist/rules/single-use-wrapper.d.ts.map +1 -0
  148. package/dist/rules/single-use-wrapper.js +172 -0
  149. package/dist/rules/single-use-wrapper.js.map +1 -0
  150. package/dist/rules/unnecessary-try-catch.d.ts +3 -0
  151. package/dist/rules/unnecessary-try-catch.d.ts.map +1 -0
  152. package/dist/rules/unnecessary-try-catch.js +116 -0
  153. package/dist/rules/unnecessary-try-catch.js.map +1 -0
  154. package/dist/rules/unused-imports.d.ts +3 -0
  155. package/dist/rules/unused-imports.d.ts.map +1 -0
  156. package/dist/rules/unused-imports.js +103 -0
  157. package/dist/rules/unused-imports.js.map +1 -0
  158. package/dist/rules/verbose-comments.d.ts +3 -0
  159. package/dist/rules/verbose-comments.d.ts.map +1 -0
  160. package/dist/rules/verbose-comments.js +100 -0
  161. package/dist/rules/verbose-comments.js.map +1 -0
  162. package/dist/scanner.d.ts +11 -0
  163. package/dist/scanner.d.ts.map +1 -0
  164. package/dist/scanner.js +196 -0
  165. package/dist/scanner.js.map +1 -0
  166. package/dist/scorer.d.ts +3 -0
  167. package/dist/scorer.d.ts.map +1 -0
  168. package/dist/scorer.js +23 -0
  169. package/dist/scorer.js.map +1 -0
  170. package/dist/types.d.ts +62 -0
  171. package/dist/types.d.ts.map +1 -0
  172. package/dist/types.js +3 -0
  173. package/dist/types.js.map +1 -0
  174. package/hn_post.md +13 -0
  175. package/marketing/COMPETITIVE_ANALYSIS.md +62 -0
  176. package/marketing/EMAIL_ANNOUNCEMENT.md +91 -0
  177. package/marketing/LANDING_PAGE_COPY.md +123 -0
  178. package/marketing/LAUNCH_POST.md +68 -0
  179. package/marketing/PRODUCT_HUNT.md +39 -0
  180. package/marketing/TWITTER_THREAD.md +70 -0
  181. package/package.json +44 -0
  182. package/producthunt.md +52 -0
  183. package/reddit_post.md +39 -0
  184. package/site/favicon.svg +10 -0
  185. package/site/index.html +281 -0
  186. package/site/script.js +82 -0
  187. package/site/style.css +516 -0
  188. package/src/cache.ts +114 -0
  189. package/src/cli.ts +169 -0
  190. package/src/commands/ci.ts +111 -0
  191. package/src/commands/diff.ts +108 -0
  192. package/src/commands/fingerprint.ts +47 -0
  193. package/src/commands/hook.ts +85 -0
  194. package/src/commands/init.ts +42 -0
  195. package/src/config.ts +75 -0
  196. package/src/errors.ts +105 -0
  197. package/src/fingerprint/analyzer.ts +214 -0
  198. package/src/fingerprint/comparator.ts +93 -0
  199. package/src/fingerprint/profile.ts +32 -0
  200. package/src/fixes/index.ts +58 -0
  201. package/src/fixes/single-use-wrapper.ts +60 -0
  202. package/src/fixes/unnecessary-try-catch.ts +43 -0
  203. package/src/fixes/unused-imports.ts +53 -0
  204. package/src/fixes/verbose-comments.ts +35 -0
  205. package/src/formatter.ts +79 -0
  206. package/src/git.ts +115 -0
  207. package/src/index.ts +15 -0
  208. package/src/languages/index.ts +50 -0
  209. package/src/languages/javascript.ts +36 -0
  210. package/src/languages/python.ts +47 -0
  211. package/src/parser.ts +52 -0
  212. package/src/reporters/github.ts +75 -0
  213. package/src/reporters/terminal.ts +67 -0
  214. package/src/rules/dead-code-paths.ts +62 -0
  215. package/src/rules/excessive-comments.ts +94 -0
  216. package/src/rules/hallucinated-imports.ts +195 -0
  217. package/src/rules/index.ts +32 -0
  218. package/src/rules/magic-values.ts +167 -0
  219. package/src/rules/near-duplicate-functions.ts +89 -0
  220. package/src/rules/over-defensive-nulls.ts +137 -0
  221. package/src/rules/redundant-else-return.ts +61 -0
  222. package/src/rules/single-option-object.ts +97 -0
  223. package/src/rules/single-use-wrapper.ts +184 -0
  224. package/src/rules/unnecessary-try-catch.ts +121 -0
  225. package/src/rules/unused-imports.ts +115 -0
  226. package/src/rules/verbose-comments.ts +105 -0
  227. package/src/scanner.ts +184 -0
  228. package/src/scorer.ts +26 -0
  229. package/src/types.ts +70 -0
  230. package/tests/commands/diff.test.ts +107 -0
  231. package/tests/config.test.ts +69 -0
  232. package/tests/e2e.test.ts +163 -0
  233. package/tests/edge-cases.test.ts +167 -0
  234. package/tests/fingerprint/analyzer.test.ts +131 -0
  235. package/tests/fixes/unnecessary-try-catch.test.ts +62 -0
  236. package/tests/git.test.ts +79 -0
  237. package/tests/rules/hallucinated-imports.test.ts +59 -0
  238. package/tests/rules/near-duplicate-functions.test.ts +90 -0
  239. package/tests/rules/unnecessary-try-catch.test.ts +81 -0
  240. package/tests/scanner.test.ts +88 -0
  241. package/tsconfig.json +20 -0
  242. package/twitter_thread.md +46 -0
  243. package/vitest.config.ts +7 -0
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.singleOptionObject = void 0;
4
+ const parser_1 = require("../parser");
5
+ function getDestructuredParams(node) {
6
+ const params = node.childForFieldName('parameters');
7
+ if (!params)
8
+ return [];
9
+ return params.namedChildren.filter((p) => {
10
+ // Destructured object parameter: function({ foo }) {}
11
+ if (p.type === 'object_pattern')
12
+ return true;
13
+ // TypeScript: function({ foo }: Options) {}
14
+ if (p.type === 'required_parameter' || p.type === 'optional_parameter') {
15
+ const pattern = p.childForFieldName('pattern');
16
+ if (pattern?.type === 'object_pattern')
17
+ return true;
18
+ }
19
+ return false;
20
+ });
21
+ }
22
+ function getObjectPatternPropertyCount(node) {
23
+ let objectPattern = node;
24
+ if (node.type === 'required_parameter' || node.type === 'optional_parameter') {
25
+ const pattern = node.childForFieldName('pattern');
26
+ if (pattern?.type === 'object_pattern')
27
+ objectPattern = pattern;
28
+ else
29
+ return 0;
30
+ }
31
+ if (objectPattern.type !== 'object_pattern')
32
+ return 0;
33
+ // Count actual properties (not rest elements)
34
+ return objectPattern.namedChildren.filter((c) => c.type === 'shorthand_property_identifier_pattern' ||
35
+ c.type === 'pair_pattern' ||
36
+ c.type === 'object_assignment_pattern' ||
37
+ c.type === 'assignment_pattern').length;
38
+ }
39
+ function getFunctionName(node) {
40
+ if (node.type === 'function_declaration' || node.type === 'method_definition') {
41
+ return node.childForFieldName('name')?.text ?? null;
42
+ }
43
+ if (node.parent?.type === 'variable_declarator') {
44
+ return node.parent.childForFieldName('name')?.text ?? null;
45
+ }
46
+ return null;
47
+ }
48
+ exports.singleOptionObject = {
49
+ name: 'single-option-object',
50
+ description: 'Flags function parameters that destructure an object with only one property',
51
+ severity: 'info',
52
+ check(tree, source, filePath) {
53
+ const findings = [];
54
+ const functions = (0, parser_1.findNodesMulti)(tree.rootNode, [
55
+ 'function_declaration',
56
+ 'arrow_function',
57
+ 'function',
58
+ 'method_definition',
59
+ ]);
60
+ for (const fn of functions) {
61
+ const destructuredParams = getDestructuredParams(fn);
62
+ for (const param of destructuredParams) {
63
+ const propCount = getObjectPatternPropertyCount(param);
64
+ if (propCount === 1) {
65
+ // Skip if function has only this one parameter and it's a callback
66
+ const parent = fn.parent;
67
+ if (parent?.type === 'arguments' || parent?.type === 'call_expression') {
68
+ continue;
69
+ }
70
+ const name = getFunctionName(fn);
71
+ const label = name ? `Function '${name}'` : 'Function';
72
+ findings.push({
73
+ file: filePath,
74
+ line: param.startPosition.row + 1,
75
+ column: param.startPosition.column + 1,
76
+ endLine: param.endPosition.row + 1,
77
+ endColumn: param.endPosition.column + 1,
78
+ rule: 'single-option-object',
79
+ severity: 'info',
80
+ message: `${label} destructures an options object with only 1 property — use a plain parameter instead`,
81
+ });
82
+ }
83
+ }
84
+ }
85
+ return findings;
86
+ },
87
+ };
88
+ //# sourceMappingURL=single-option-object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single-option-object.js","sourceRoot":"","sources":["../../src/rules/single-option-object.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from '../types';
2
+ export declare const singleUseWrapper: Rule;
3
+ //# sourceMappingURL=single-use-wrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single-use-wrapper.d.ts","sourceRoot":"","sources":["../../src/rules/single-use-wrapper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,UAAU,CAAC;AA+H9C,eAAO,MAAM,gBAAgB,EAAE,IAsD9B,CAAC"}
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.singleUseWrapper = void 0;
4
+ const parser_1 = require("../parser");
5
+ function getFunctionBody(node) {
6
+ return node.childForFieldName('body');
7
+ }
8
+ function getFunctionName(node) {
9
+ if (node.type === 'function_declaration' || node.type === 'method_definition') {
10
+ const nameNode = node.childForFieldName('name');
11
+ return nameNode?.text ?? null;
12
+ }
13
+ if (node.type === 'variable_declarator') {
14
+ const nameNode = node.childForFieldName('name');
15
+ return nameNode?.text ?? null;
16
+ }
17
+ return null;
18
+ }
19
+ function getStatements(body) {
20
+ if (body.type === 'statement_block') {
21
+ return body.namedChildren;
22
+ }
23
+ return [];
24
+ }
25
+ const COMPLEX_ARG_TYPES = new Set([
26
+ 'arrow_function', 'function', 'object', 'array',
27
+ 'template_string', 'binary_expression', 'new_expression',
28
+ 'ternary_expression', 'conditional_expression',
29
+ 'unary_expression', 'update_expression', 'assignment_expression',
30
+ 'augmented_assignment_expression', 'yield_expression',
31
+ 'spread_element', 'regex', 'class',
32
+ ]);
33
+ function isSimpleNode(node) {
34
+ if (node.type === 'identifier' || node.type === 'this' ||
35
+ node.type === 'string' || node.type === 'number' ||
36
+ node.type === 'true' || node.type === 'false' ||
37
+ node.type === 'null' || node.type === 'undefined') {
38
+ return true;
39
+ }
40
+ // Simple member_expression like a.b (not computed a[b])
41
+ if (node.type === 'member_expression') {
42
+ const computed = node.children.some(c => c.type === '[');
43
+ return !computed;
44
+ }
45
+ return false;
46
+ }
47
+ function hasOnlySimpleArguments(callExpr) {
48
+ // Check if callee involves complex expressions (e.g. new Foo().method())
49
+ const callee = callExpr.childForFieldName('function');
50
+ if (callee?.type === 'member_expression') {
51
+ const object = callee.childForFieldName('object');
52
+ if (object && COMPLEX_ARG_TYPES.has(object.type))
53
+ return false;
54
+ // Also check for chained calls with complex args: foo(complex).bar(simple)
55
+ if (object?.type === 'call_expression') {
56
+ if (!hasOnlySimpleArguments(object))
57
+ return false;
58
+ }
59
+ }
60
+ const args = callExpr.childForFieldName('arguments');
61
+ if (!args)
62
+ return true;
63
+ for (const arg of args.namedChildren) {
64
+ if (isSimpleNode(arg))
65
+ continue;
66
+ // Known complex types
67
+ if (COMPLEX_ARG_TYPES.has(arg.type))
68
+ return false;
69
+ // Anything else unexpected — be conservative, treat as complex
70
+ return false;
71
+ }
72
+ return true;
73
+ }
74
+ function extractCallExpression(node) {
75
+ if (node.type === 'call_expression')
76
+ return node;
77
+ // Parenthesized expression wrapping a call
78
+ if (node.type === 'parenthesized_expression' && node.namedChildren.length === 1) {
79
+ return extractCallExpression(node.namedChildren[0]);
80
+ }
81
+ return null;
82
+ }
83
+ function isSimpleDelegatingFunction(body) {
84
+ // For arrow functions with expression body (no braces)
85
+ if (body.type === 'call_expression') {
86
+ return hasOnlySimpleArguments(body);
87
+ }
88
+ const stmts = getStatements(body);
89
+ if (stmts.length !== 1)
90
+ return false;
91
+ const stmt = stmts[0];
92
+ // Single return statement with a function call
93
+ if (stmt.type === 'return_statement') {
94
+ const returnValue = stmt.namedChildren[0];
95
+ if (!returnValue)
96
+ return false;
97
+ const call = extractCallExpression(returnValue);
98
+ if (!call)
99
+ return false;
100
+ return hasOnlySimpleArguments(call);
101
+ }
102
+ // Single expression statement that is a function call (void wrapper)
103
+ if (stmt.type === 'expression_statement') {
104
+ const expr = stmt.namedChildren[0];
105
+ if (!expr)
106
+ return false;
107
+ const call = extractCallExpression(expr);
108
+ if (!call)
109
+ return false;
110
+ return hasOnlySimpleArguments(call);
111
+ }
112
+ return false;
113
+ }
114
+ function getParameterCount(node) {
115
+ const params = node.childForFieldName('parameters');
116
+ if (!params)
117
+ return 0;
118
+ return params.namedChildren.filter(c => c.type === 'identifier' ||
119
+ c.type === 'required_parameter' ||
120
+ c.type === 'optional_parameter' ||
121
+ c.type === 'rest_parameter' ||
122
+ c.type === 'assignment_pattern').length;
123
+ }
124
+ exports.singleUseWrapper = {
125
+ name: 'single-use-wrapper',
126
+ description: 'Flags functions that merely delegate to another function without adding logic',
127
+ severity: 'warning',
128
+ check(tree, source, filePath) {
129
+ const findings = [];
130
+ const functionNodes = (0, parser_1.findNodesMulti)(tree.rootNode, [
131
+ 'function_declaration',
132
+ 'arrow_function',
133
+ 'function',
134
+ ]);
135
+ for (const fn of functionNodes) {
136
+ const body = getFunctionBody(fn);
137
+ if (!body)
138
+ continue;
139
+ if (!isSimpleDelegatingFunction(body))
140
+ continue;
141
+ // Skip if function has more than 3 parameters (likely an intentional adapter)
142
+ if (getParameterCount(fn) > 3)
143
+ continue;
144
+ // Skip anonymous functions passed as callbacks — those are idiomatic
145
+ if (fn.type === 'arrow_function' || fn.type === 'function') {
146
+ const parent = fn.parent;
147
+ if (parent?.type === 'arguments' || parent?.type === 'call_expression' ||
148
+ parent?.type === 'array' || parent?.type === 'pair') {
149
+ continue;
150
+ }
151
+ }
152
+ // Try to get the function name for the message
153
+ let name = getFunctionName(fn);
154
+ if (!name && fn.parent?.type === 'variable_declarator') {
155
+ name = getFunctionName(fn.parent);
156
+ }
157
+ const label = name ? `Function '${name}'` : 'Function';
158
+ findings.push({
159
+ file: filePath,
160
+ line: fn.startPosition.row + 1,
161
+ column: fn.startPosition.column + 1,
162
+ endLine: fn.endPosition.row + 1,
163
+ endColumn: fn.endPosition.column + 1,
164
+ rule: 'single-use-wrapper',
165
+ severity: 'warning',
166
+ message: `${label} only delegates to another function — consider inlining it`,
167
+ });
168
+ }
169
+ return findings;
170
+ },
171
+ };
172
+ //# sourceMappingURL=single-use-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single-use-wrapper.js","sourceRoot":"","sources":["../../src/rules/single-use-wrapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from '../types';
2
+ export declare const unnecessaryTryCatch: Rule;
3
+ //# sourceMappingURL=unnecessary-try-catch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unnecessary-try-catch.d.ts","sourceRoot":"","sources":["../../src/rules/unnecessary-try-catch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,UAAU,CAAC;AA6D9C,eAAO,MAAM,mBAAmB,EAAE,IAyDjC,CAAC"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unnecessaryTryCatch = void 0;
4
+ const parser_1 = require("../parser");
5
+ const THROWING_CALL_INDICATORS = new Set([
6
+ 'fetch', 'require', 'import', 'eval',
7
+ 'JSON.parse', 'JSON.stringify',
8
+ 'parseInt', 'parseFloat',
9
+ 'decodeURI', 'decodeURIComponent',
10
+ 'atob', 'btoa',
11
+ ]);
12
+ function isAwaitExpression(node) {
13
+ let found = false;
14
+ (0, parser_1.walkTree)(node, (n) => {
15
+ if (n.type === 'await_expression')
16
+ found = true;
17
+ });
18
+ return found;
19
+ }
20
+ function containsThrowStatement(node) {
21
+ let found = false;
22
+ (0, parser_1.walkTree)(node, (n) => {
23
+ if (n.type === 'throw_statement')
24
+ found = true;
25
+ });
26
+ return found;
27
+ }
28
+ function containsFunctionCall(node) {
29
+ let found = false;
30
+ (0, parser_1.walkTree)(node, (n) => {
31
+ if (n.type === 'call_expression')
32
+ found = true;
33
+ });
34
+ return found;
35
+ }
36
+ function isPropertyAccess(node) {
37
+ let found = false;
38
+ (0, parser_1.walkTree)(node, (n) => {
39
+ if (n.type === 'member_expression' || n.type === 'subscript_expression')
40
+ found = true;
41
+ });
42
+ return found;
43
+ }
44
+ function catchBlockOnlyRethrows(catchClause) {
45
+ const body = catchClause.childForFieldName('body');
46
+ if (!body)
47
+ return false;
48
+ const statements = body.namedChildren;
49
+ if (statements.length !== 1)
50
+ return false;
51
+ const stmt = statements[0];
52
+ if (stmt.type !== 'throw_statement')
53
+ return false;
54
+ const thrown = stmt.namedChildren[0];
55
+ if (!thrown)
56
+ return false;
57
+ const catchParam = catchClause.childForFieldName('parameter');
58
+ if (!catchParam)
59
+ return false;
60
+ return thrown.text === catchParam.text;
61
+ }
62
+ exports.unnecessaryTryCatch = {
63
+ name: 'unnecessary-try-catch',
64
+ description: 'Flags try-catch blocks that wrap code unlikely to throw',
65
+ severity: 'warning',
66
+ check(tree, source, filePath) {
67
+ const findings = [];
68
+ const tryStatements = (0, parser_1.findNodes)(tree.rootNode, 'try_statement');
69
+ for (const tryNode of tryStatements) {
70
+ const body = tryNode.childForFieldName('body');
71
+ const handler = tryNode.childForFieldName('handler');
72
+ if (!body || !handler)
73
+ continue;
74
+ // If catch block only rethrows, that's always useless
75
+ if (catchBlockOnlyRethrows(handler)) {
76
+ findings.push({
77
+ file: filePath,
78
+ line: tryNode.startPosition.row + 1,
79
+ column: tryNode.startPosition.column + 1,
80
+ endLine: tryNode.endPosition.row + 1,
81
+ endColumn: tryNode.endPosition.column + 1,
82
+ rule: 'unnecessary-try-catch',
83
+ severity: 'warning',
84
+ message: 'try-catch block catches an error only to rethrow it unchanged',
85
+ });
86
+ continue;
87
+ }
88
+ // Skip if the try body contains await (async operations can throw)
89
+ if (isAwaitExpression(body))
90
+ continue;
91
+ // Skip if try body contains throw statements
92
+ if (containsThrowStatement(body))
93
+ continue;
94
+ // Skip if try body contains function calls (they might throw)
95
+ if (containsFunctionCall(body))
96
+ continue;
97
+ // Skip if try body contains property access (might throw on null/undefined)
98
+ if (isPropertyAccess(body))
99
+ continue;
100
+ // If we get here, the try body only contains assignments, returns of
101
+ // literals/variables, or other purely synchronous non-throwing ops
102
+ findings.push({
103
+ file: filePath,
104
+ line: tryNode.startPosition.row + 1,
105
+ column: tryNode.startPosition.column + 1,
106
+ endLine: tryNode.endPosition.row + 1,
107
+ endColumn: tryNode.endPosition.column + 1,
108
+ rule: 'unnecessary-try-catch',
109
+ severity: 'warning',
110
+ message: 'try-catch wraps code that is unlikely to throw — consider removing the try-catch',
111
+ });
112
+ }
113
+ return findings;
114
+ },
115
+ };
116
+ //# sourceMappingURL=unnecessary-try-catch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unnecessary-try-catch.js","sourceRoot":"","sources":["../../src/rules/unnecessary-try-catch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from '../types';
2
+ export declare const unusedImports: Rule;
3
+ //# sourceMappingURL=unused-imports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unused-imports.d.ts","sourceRoot":"","sources":["../../src/rules/unused-imports.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,UAAU,CAAC;AAuE9C,eAAO,MAAM,aAAa,EAAE,IAyC3B,CAAC"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unusedImports = void 0;
4
+ const parser_1 = require("../parser");
5
+ function getImportedNames(importNode) {
6
+ const names = [];
7
+ (0, parser_1.walkTree)(importNode, (node) => {
8
+ // import Foo from '...' (default import)
9
+ if (node.type === 'identifier' && node.parent?.type === 'import_clause') {
10
+ names.push({ name: node.text, node });
11
+ }
12
+ // import { Foo } from '...' or import { Foo as Bar } from '...'
13
+ if (node.type === 'import_specifier') {
14
+ const alias = node.childForFieldName('alias');
15
+ const nameNode = alias ?? node.childForFieldName('name');
16
+ if (nameNode) {
17
+ names.push({ name: nameNode.text, node: nameNode });
18
+ }
19
+ }
20
+ // import * as Foo from '...'
21
+ if (node.type === 'namespace_import') {
22
+ const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
23
+ if (nameNode) {
24
+ names.push({ name: nameNode.text, node: nameNode });
25
+ }
26
+ }
27
+ });
28
+ return names;
29
+ }
30
+ function collectAllIdentifiers(root, excludeImports) {
31
+ const excludeSet = new Set(excludeImports.map((n) => n.id));
32
+ const identifiers = new Set();
33
+ (0, parser_1.walkTree)(root, (node) => {
34
+ if (node.type === 'identifier' || node.type === 'shorthand_property_identifier' ||
35
+ node.type === 'shorthand_property_identifier_pattern') {
36
+ // Skip nodes that are part of import declarations
37
+ let parent = node;
38
+ let isInImport = false;
39
+ while (parent) {
40
+ if (parent.type === 'import_statement' || parent.type === 'import_declaration') {
41
+ isInImport = true;
42
+ break;
43
+ }
44
+ parent = parent.parent;
45
+ }
46
+ if (!isInImport) {
47
+ identifiers.add(node.text);
48
+ }
49
+ }
50
+ // Also check type references for TypeScript
51
+ if (node.type === 'type_identifier') {
52
+ let parent = node;
53
+ let isInImport = false;
54
+ while (parent) {
55
+ if (parent.type === 'import_statement' || parent.type === 'import_declaration') {
56
+ isInImport = true;
57
+ break;
58
+ }
59
+ parent = parent.parent;
60
+ }
61
+ if (!isInImport) {
62
+ identifiers.add(node.text);
63
+ }
64
+ }
65
+ });
66
+ return identifiers;
67
+ }
68
+ exports.unusedImports = {
69
+ name: 'unused-imports',
70
+ description: 'Flags imported identifiers that are never used in the file',
71
+ severity: 'warning',
72
+ check(tree, source, filePath) {
73
+ const findings = [];
74
+ const importStatements = (0, parser_1.findNodes)(tree.rootNode, 'import_statement');
75
+ if (importStatements.length === 0)
76
+ return findings;
77
+ // Collect all identifiers used outside of imports
78
+ const usedIdentifiers = collectAllIdentifiers(tree.rootNode, importStatements);
79
+ for (const importStmt of importStatements) {
80
+ // Skip side-effect imports: import './foo' or import 'module'
81
+ const hasClause = importStmt.children.some((c) => c.type === 'import_clause');
82
+ if (!hasClause)
83
+ continue;
84
+ const importedNames = getImportedNames(importStmt);
85
+ for (const { name, node } of importedNames) {
86
+ if (!usedIdentifiers.has(name)) {
87
+ findings.push({
88
+ file: filePath,
89
+ line: node.startPosition.row + 1,
90
+ column: node.startPosition.column + 1,
91
+ endLine: node.endPosition.row + 1,
92
+ endColumn: node.endPosition.column + 1,
93
+ rule: 'unused-imports',
94
+ severity: 'warning',
95
+ message: `'${name}' is imported but never used`,
96
+ });
97
+ }
98
+ }
99
+ }
100
+ return findings;
101
+ },
102
+ };
103
+ //# sourceMappingURL=unused-imports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unused-imports.js","sourceRoot":"","sources":["../../src/rules/unused-imports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from '../types';
2
+ export declare const verboseComments: Rule;
3
+ //# sourceMappingURL=verbose-comments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verbose-comments.d.ts","sourceRoot":"","sources":["../../src/rules/verbose-comments.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,UAAU,CAAC;AA8D9C,eAAO,MAAM,eAAe,EAAE,IAwC7B,CAAC"}
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verboseComments = void 0;
4
+ const parser_1 = require("../parser");
5
+ function tokenize(text) {
6
+ return text
7
+ .toLowerCase()
8
+ .replace(/[^a-z0-9_$]+/g, ' ')
9
+ .trim()
10
+ .split(/\s+/)
11
+ .filter((t) => t.length > 1);
12
+ }
13
+ function jaccardSimilarity(a, b) {
14
+ if (a.length === 0 || b.length === 0)
15
+ return 0;
16
+ const setA = new Set(a);
17
+ const setB = new Set(b);
18
+ let intersection = 0;
19
+ for (const item of setA) {
20
+ if (setB.has(item))
21
+ intersection++;
22
+ }
23
+ const union = new Set([...setA, ...setB]).size;
24
+ return union === 0 ? 0 : intersection / union;
25
+ }
26
+ function stripCommentSyntax(text) {
27
+ return text
28
+ .replace(/^\/\/\s*/, '')
29
+ .replace(/^\/\*+\s*/, '')
30
+ .replace(/\s*\*+\/$/, '')
31
+ .replace(/^\s*\*\s?/gm, '')
32
+ .trim();
33
+ }
34
+ function getNextSiblingCode(node) {
35
+ let sibling = node.nextNamedSibling;
36
+ // Skip over other comments
37
+ while (sibling && sibling.type === 'comment') {
38
+ sibling = sibling.nextNamedSibling;
39
+ }
40
+ if (!sibling)
41
+ return null;
42
+ // Get just the first line of the sibling code for comparison
43
+ const text = sibling.text;
44
+ const firstLine = text.split('\n')[0];
45
+ return firstLine || null;
46
+ }
47
+ const TRIVIAL_COMMENTS = new Set([
48
+ 'todo', 'fixme', 'hack', 'note', 'bug', 'xxx',
49
+ 'eslint-disable', 'eslint-enable', 'ts-ignore', 'ts-expect-error',
50
+ 'prettier-ignore', 'istanbul ignore', 'c8 ignore',
51
+ '@ts-ignore', '@ts-expect-error', '@ts-nocheck',
52
+ ]);
53
+ function isTrivialOrDirective(commentText) {
54
+ const lower = commentText.toLowerCase().trim();
55
+ for (const directive of TRIVIAL_COMMENTS) {
56
+ if (lower.startsWith(directive))
57
+ return true;
58
+ }
59
+ // Skip very short comments (likely meaningful labels)
60
+ if (lower.length < 10)
61
+ return true;
62
+ return false;
63
+ }
64
+ exports.verboseComments = {
65
+ name: 'verbose-comments',
66
+ description: 'Flags comments that restate the code they describe',
67
+ severity: 'info',
68
+ check(tree, source, filePath) {
69
+ const findings = [];
70
+ const comments = (0, parser_1.findNodes)(tree.rootNode, 'comment');
71
+ for (const comment of comments) {
72
+ const raw = stripCommentSyntax(comment.text);
73
+ if (isTrivialOrDirective(raw))
74
+ continue;
75
+ const nextCode = getNextSiblingCode(comment);
76
+ if (!nextCode)
77
+ continue;
78
+ const commentTokens = tokenize(raw);
79
+ const codeTokens = tokenize(nextCode);
80
+ if (commentTokens.length < 3)
81
+ continue;
82
+ const similarity = jaccardSimilarity(commentTokens, codeTokens);
83
+ // Only flag if >80% similar — very conservative threshold
84
+ if (similarity >= 0.8) {
85
+ findings.push({
86
+ file: filePath,
87
+ line: comment.startPosition.row + 1,
88
+ column: comment.startPosition.column + 1,
89
+ endLine: comment.endPosition.row + 1,
90
+ endColumn: comment.endPosition.column + 1,
91
+ rule: 'verbose-comments',
92
+ severity: 'info',
93
+ message: `Comment restates the code (${Math.round(similarity * 100)}% similar) — consider removing it`,
94
+ });
95
+ }
96
+ }
97
+ return findings;
98
+ },
99
+ };
100
+ //# sourceMappingURL=verbose-comments.js.map