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.
- package/CONTRIBUTING.md +159 -0
- package/POSTMORTEM.json +60 -0
- package/README.md +218 -0
- package/SETUP.md +79 -0
- package/action.yml +37 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +115 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +153 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ci.d.ts +7 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +101 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/diff.d.ts +10 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +95 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/fingerprint.d.ts +2 -0
- package/dist/commands/fingerprint.d.ts.map +1 -0
- package/dist/commands/fingerprint.js +77 -0
- package/dist/commands/fingerprint.js.map +1 -0
- package/dist/commands/hook.d.ts +3 -0
- package/dist/commands/hook.d.ts.map +1 -0
- package/dist/commands/hook.js +110 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +75 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +100 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +133 -0
- package/dist/errors.js.map +1 -0
- package/dist/fingerprint/analyzer.d.ts +3 -0
- package/dist/fingerprint/analyzer.d.ts.map +1 -0
- package/dist/fingerprint/analyzer.js +230 -0
- package/dist/fingerprint/analyzer.js.map +1 -0
- package/dist/fingerprint/comparator.d.ts +4 -0
- package/dist/fingerprint/comparator.d.ts.map +1 -0
- package/dist/fingerprint/comparator.js +78 -0
- package/dist/fingerprint/comparator.js.map +1 -0
- package/dist/fingerprint/profile.d.ts +5 -0
- package/dist/fingerprint/profile.d.ts.map +1 -0
- package/dist/fingerprint/profile.js +68 -0
- package/dist/fingerprint/profile.js.map +1 -0
- package/dist/fixes/index.d.ts +12 -0
- package/dist/fixes/index.d.ts.map +1 -0
- package/dist/fixes/index.js +42 -0
- package/dist/fixes/index.js.map +1 -0
- package/dist/fixes/single-use-wrapper.d.ts +8 -0
- package/dist/fixes/single-use-wrapper.d.ts.map +1 -0
- package/dist/fixes/single-use-wrapper.js +54 -0
- package/dist/fixes/single-use-wrapper.js.map +1 -0
- package/dist/fixes/unnecessary-try-catch.d.ts +8 -0
- package/dist/fixes/unnecessary-try-catch.d.ts.map +1 -0
- package/dist/fixes/unnecessary-try-catch.js +37 -0
- package/dist/fixes/unnecessary-try-catch.js.map +1 -0
- package/dist/fixes/unused-imports.d.ts +7 -0
- package/dist/fixes/unused-imports.d.ts.map +1 -0
- package/dist/fixes/unused-imports.js +41 -0
- package/dist/fixes/unused-imports.js.map +1 -0
- package/dist/fixes/verbose-comments.d.ts +7 -0
- package/dist/fixes/verbose-comments.d.ts.map +1 -0
- package/dist/fixes/verbose-comments.js +29 -0
- package/dist/fixes/verbose-comments.js.map +1 -0
- package/dist/formatter.d.ts +4 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +72 -0
- package/dist/formatter.js.map +1 -0
- package/dist/git.d.ts +22 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +130 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/index.d.ts +8 -0
- package/dist/languages/index.d.ts.map +1 -0
- package/dist/languages/index.js +50 -0
- package/dist/languages/index.js.map +1 -0
- package/dist/languages/javascript.d.ts +6 -0
- package/dist/languages/javascript.d.ts.map +1 -0
- package/dist/languages/javascript.js +39 -0
- package/dist/languages/javascript.js.map +1 -0
- package/dist/languages/python.d.ts +6 -0
- package/dist/languages/python.d.ts.map +1 -0
- package/dist/languages/python.js +50 -0
- package/dist/languages/python.js.map +1 -0
- package/dist/parser.d.ts +8 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +55 -0
- package/dist/parser.js.map +1 -0
- package/dist/reporters/github.d.ts +4 -0
- package/dist/reporters/github.d.ts.map +1 -0
- package/dist/reporters/github.js +70 -0
- package/dist/reporters/github.js.map +1 -0
- package/dist/reporters/terminal.d.ts +4 -0
- package/dist/reporters/terminal.d.ts.map +1 -0
- package/dist/reporters/terminal.js +59 -0
- package/dist/reporters/terminal.js.map +1 -0
- package/dist/rules/dead-code-paths.d.ts +3 -0
- package/dist/rules/dead-code-paths.d.ts.map +1 -0
- package/dist/rules/dead-code-paths.js +57 -0
- package/dist/rules/dead-code-paths.js.map +1 -0
- package/dist/rules/excessive-comments.d.ts +3 -0
- package/dist/rules/excessive-comments.d.ts.map +1 -0
- package/dist/rules/excessive-comments.js +86 -0
- package/dist/rules/excessive-comments.js.map +1 -0
- package/dist/rules/hallucinated-imports.d.ts +3 -0
- package/dist/rules/hallucinated-imports.d.ts.map +1 -0
- package/dist/rules/hallucinated-imports.js +228 -0
- package/dist/rules/hallucinated-imports.js.map +1 -0
- package/dist/rules/index.d.ts +4 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +34 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/magic-values.d.ts +3 -0
- package/dist/rules/magic-values.d.ts.map +1 -0
- package/dist/rules/magic-values.js +168 -0
- package/dist/rules/magic-values.js.map +1 -0
- package/dist/rules/near-duplicate-functions.d.ts +3 -0
- package/dist/rules/near-duplicate-functions.d.ts.map +1 -0
- package/dist/rules/near-duplicate-functions.js +78 -0
- package/dist/rules/near-duplicate-functions.js.map +1 -0
- package/dist/rules/over-defensive-nulls.d.ts +3 -0
- package/dist/rules/over-defensive-nulls.d.ts.map +1 -0
- package/dist/rules/over-defensive-nulls.js +129 -0
- package/dist/rules/over-defensive-nulls.js.map +1 -0
- package/dist/rules/redundant-else-return.d.ts +3 -0
- package/dist/rules/redundant-else-return.d.ts.map +1 -0
- package/dist/rules/redundant-else-return.js +57 -0
- package/dist/rules/redundant-else-return.js.map +1 -0
- package/dist/rules/single-option-object.d.ts +3 -0
- package/dist/rules/single-option-object.d.ts.map +1 -0
- package/dist/rules/single-option-object.js +88 -0
- package/dist/rules/single-option-object.js.map +1 -0
- package/dist/rules/single-use-wrapper.d.ts +3 -0
- package/dist/rules/single-use-wrapper.d.ts.map +1 -0
- package/dist/rules/single-use-wrapper.js +172 -0
- package/dist/rules/single-use-wrapper.js.map +1 -0
- package/dist/rules/unnecessary-try-catch.d.ts +3 -0
- package/dist/rules/unnecessary-try-catch.d.ts.map +1 -0
- package/dist/rules/unnecessary-try-catch.js +116 -0
- package/dist/rules/unnecessary-try-catch.js.map +1 -0
- package/dist/rules/unused-imports.d.ts +3 -0
- package/dist/rules/unused-imports.d.ts.map +1 -0
- package/dist/rules/unused-imports.js +103 -0
- package/dist/rules/unused-imports.js.map +1 -0
- package/dist/rules/verbose-comments.d.ts +3 -0
- package/dist/rules/verbose-comments.d.ts.map +1 -0
- package/dist/rules/verbose-comments.js +100 -0
- package/dist/rules/verbose-comments.js.map +1 -0
- package/dist/scanner.d.ts +11 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +196 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scorer.d.ts +3 -0
- package/dist/scorer.d.ts.map +1 -0
- package/dist/scorer.js +23 -0
- package/dist/scorer.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/hn_post.md +13 -0
- package/marketing/COMPETITIVE_ANALYSIS.md +62 -0
- package/marketing/EMAIL_ANNOUNCEMENT.md +91 -0
- package/marketing/LANDING_PAGE_COPY.md +123 -0
- package/marketing/LAUNCH_POST.md +68 -0
- package/marketing/PRODUCT_HUNT.md +39 -0
- package/marketing/TWITTER_THREAD.md +70 -0
- package/package.json +44 -0
- package/producthunt.md +52 -0
- package/reddit_post.md +39 -0
- package/site/favicon.svg +10 -0
- package/site/index.html +281 -0
- package/site/script.js +82 -0
- package/site/style.css +516 -0
- package/src/cache.ts +114 -0
- package/src/cli.ts +169 -0
- package/src/commands/ci.ts +111 -0
- package/src/commands/diff.ts +108 -0
- package/src/commands/fingerprint.ts +47 -0
- package/src/commands/hook.ts +85 -0
- package/src/commands/init.ts +42 -0
- package/src/config.ts +75 -0
- package/src/errors.ts +105 -0
- package/src/fingerprint/analyzer.ts +214 -0
- package/src/fingerprint/comparator.ts +93 -0
- package/src/fingerprint/profile.ts +32 -0
- package/src/fixes/index.ts +58 -0
- package/src/fixes/single-use-wrapper.ts +60 -0
- package/src/fixes/unnecessary-try-catch.ts +43 -0
- package/src/fixes/unused-imports.ts +53 -0
- package/src/fixes/verbose-comments.ts +35 -0
- package/src/formatter.ts +79 -0
- package/src/git.ts +115 -0
- package/src/index.ts +15 -0
- package/src/languages/index.ts +50 -0
- package/src/languages/javascript.ts +36 -0
- package/src/languages/python.ts +47 -0
- package/src/parser.ts +52 -0
- package/src/reporters/github.ts +75 -0
- package/src/reporters/terminal.ts +67 -0
- package/src/rules/dead-code-paths.ts +62 -0
- package/src/rules/excessive-comments.ts +94 -0
- package/src/rules/hallucinated-imports.ts +195 -0
- package/src/rules/index.ts +32 -0
- package/src/rules/magic-values.ts +167 -0
- package/src/rules/near-duplicate-functions.ts +89 -0
- package/src/rules/over-defensive-nulls.ts +137 -0
- package/src/rules/redundant-else-return.ts +61 -0
- package/src/rules/single-option-object.ts +97 -0
- package/src/rules/single-use-wrapper.ts +184 -0
- package/src/rules/unnecessary-try-catch.ts +121 -0
- package/src/rules/unused-imports.ts +115 -0
- package/src/rules/verbose-comments.ts +105 -0
- package/src/scanner.ts +184 -0
- package/src/scorer.ts +26 -0
- package/src/types.ts +70 -0
- package/tests/commands/diff.test.ts +107 -0
- package/tests/config.test.ts +69 -0
- package/tests/e2e.test.ts +163 -0
- package/tests/edge-cases.test.ts +167 -0
- package/tests/fingerprint/analyzer.test.ts +131 -0
- package/tests/fixes/unnecessary-try-catch.test.ts +62 -0
- package/tests/git.test.ts +79 -0
- package/tests/rules/hallucinated-imports.test.ts +59 -0
- package/tests/rules/near-duplicate-functions.test.ts +90 -0
- package/tests/rules/unnecessary-try-catch.test.ts +81 -0
- package/tests/scanner.test.ts +88 -0
- package/tsconfig.json +20 -0
- package/twitter_thread.md +46 -0
- package/vitest.config.ts +7 -0
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Parser from 'tree-sitter';
|
|
2
|
+
import type { Language } from './types';
|
|
3
|
+
export declare function parse(source: string, language: Language): Parser.Tree;
|
|
4
|
+
export declare function detectLanguage(filePath: string): Language | null;
|
|
5
|
+
export declare function walkTree(node: Parser.SyntaxNode, callback: (node: Parser.SyntaxNode) => void): void;
|
|
6
|
+
export declare function findNodes(root: Parser.SyntaxNode, type: string): Parser.SyntaxNode[];
|
|
7
|
+
export declare function findNodesMulti(root: Parser.SyntaxNode, types: string[]): Parser.SyntaxNode[];
|
|
8
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAexC,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,IAAI,CAGrE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEhE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,KAAK,IAAI,GAAG,IAAI,CAKnG;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAQpF;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAS5F"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parse = parse;
|
|
7
|
+
exports.detectLanguage = detectLanguage;
|
|
8
|
+
exports.walkTree = walkTree;
|
|
9
|
+
exports.findNodes = findNodes;
|
|
10
|
+
exports.findNodesMulti = findNodesMulti;
|
|
11
|
+
const tree_sitter_1 = __importDefault(require("tree-sitter"));
|
|
12
|
+
const languages_1 = require("./languages");
|
|
13
|
+
const parsers = new Map();
|
|
14
|
+
function getParser(language) {
|
|
15
|
+
let parser = parsers.get(language);
|
|
16
|
+
if (parser)
|
|
17
|
+
return parser;
|
|
18
|
+
parser = new tree_sitter_1.default();
|
|
19
|
+
parser.setLanguage((0, languages_1.getGrammar)(language));
|
|
20
|
+
parsers.set(language, parser);
|
|
21
|
+
return parser;
|
|
22
|
+
}
|
|
23
|
+
function parse(source, language) {
|
|
24
|
+
const parser = getParser(language);
|
|
25
|
+
return parser.parse(source);
|
|
26
|
+
}
|
|
27
|
+
function detectLanguage(filePath) {
|
|
28
|
+
return (0, languages_1.detectLanguageFromExt)(filePath);
|
|
29
|
+
}
|
|
30
|
+
function walkTree(node, callback) {
|
|
31
|
+
callback(node);
|
|
32
|
+
for (const child of node.children) {
|
|
33
|
+
walkTree(child, callback);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function findNodes(root, type) {
|
|
37
|
+
const results = [];
|
|
38
|
+
walkTree(root, (node) => {
|
|
39
|
+
if (node.type === type) {
|
|
40
|
+
results.push(node);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
function findNodesMulti(root, types) {
|
|
46
|
+
const typeSet = new Set(types);
|
|
47
|
+
const results = [];
|
|
48
|
+
walkTree(root, (node) => {
|
|
49
|
+
if (typeSet.has(node.type)) {
|
|
50
|
+
results.push(node);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return results;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":";;;;;AAgBA,sBAGC;AAED,wCAEC;AAED,4BAKC;AAED,8BAQC;AAED,wCASC;AAnDD,8DAAiC;AAEjC,2CAAgE;AAEhE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;AAE5C,SAAS,SAAS,CAAC,QAAkB;IACnC,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,GAAG,IAAI,qBAAM,EAAE,CAAC;IACtB,MAAM,CAAC,WAAW,CAAC,IAAA,sBAAU,EAAC,QAAQ,CAAC,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAgB,KAAK,CAAC,MAAc,EAAE,QAAkB;IACtD,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,SAAgB,cAAc,CAAC,QAAgB;IAC7C,OAAO,IAAA,iCAAqB,EAAC,QAAQ,CAAC,CAAC;AACzC,CAAC;AAED,SAAgB,QAAQ,CAAC,IAAuB,EAAE,QAA2C;IAC3F,QAAQ,CAAC,IAAI,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,SAAgB,SAAS,CAAC,IAAuB,EAAE,IAAY;IAC7D,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QACtB,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,cAAc,CAAC,IAAuB,EAAE,KAAe;IACrE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QACtB,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/reporters/github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAqB,MAAM,UAAU,CAAC;AAQ/D,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAapE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAmDhE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatGitHubAnnotations = formatGitHubAnnotations;
|
|
4
|
+
exports.formatGitHubSummary = formatGitHubSummary;
|
|
5
|
+
const SEVERITY_MAP = {
|
|
6
|
+
info: 'notice',
|
|
7
|
+
warning: 'warning',
|
|
8
|
+
error: 'error',
|
|
9
|
+
};
|
|
10
|
+
function formatGitHubAnnotations(summary) {
|
|
11
|
+
const lines = [];
|
|
12
|
+
for (const result of summary.results) {
|
|
13
|
+
for (const finding of result.findings) {
|
|
14
|
+
const level = SEVERITY_MAP[finding.severity];
|
|
15
|
+
const file = finding.file;
|
|
16
|
+
const line = finding.line;
|
|
17
|
+
lines.push(`::${level} file=${file},line=${line}::${finding.message} (${finding.rule})`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return lines.join('\n');
|
|
21
|
+
}
|
|
22
|
+
function formatGitHubSummary(summary) {
|
|
23
|
+
const lines = [];
|
|
24
|
+
lines.push('## Distyll Slop Report');
|
|
25
|
+
lines.push('');
|
|
26
|
+
const emoji = summary.score <= 20 ? '🟢' : summary.score <= 50 ? '🟡' : '🔴';
|
|
27
|
+
lines.push(`**Slop Score: ${emoji} ${summary.score}/100**`);
|
|
28
|
+
lines.push('');
|
|
29
|
+
const findingCount = summary.totalFindings;
|
|
30
|
+
const fileCount = summary.results.filter((r) => r.findings.length > 0).length;
|
|
31
|
+
lines.push(`Found ${findingCount} issue${findingCount === 1 ? '' : 's'} across ${fileCount} file${fileCount === 1 ? '' : 's'}.`);
|
|
32
|
+
lines.push('');
|
|
33
|
+
// Group findings by severity
|
|
34
|
+
const allFindings = summary.results.flatMap((r) => r.findings);
|
|
35
|
+
const errors = allFindings.filter((f) => f.severity === 'error').length;
|
|
36
|
+
const warnings = allFindings.filter((f) => f.severity === 'warning').length;
|
|
37
|
+
const infos = allFindings.filter((f) => f.severity === 'info').length;
|
|
38
|
+
if (errors > 0 || warnings > 0 || infos > 0) {
|
|
39
|
+
lines.push('| Severity | Count |');
|
|
40
|
+
lines.push('|----------|-------|');
|
|
41
|
+
if (errors > 0)
|
|
42
|
+
lines.push(`| 🔴 Error | ${errors} |`);
|
|
43
|
+
if (warnings > 0)
|
|
44
|
+
lines.push(`| 🟡 Warning | ${warnings} |`);
|
|
45
|
+
if (infos > 0)
|
|
46
|
+
lines.push(`| 🔵 Info | ${infos} |`);
|
|
47
|
+
lines.push('');
|
|
48
|
+
}
|
|
49
|
+
// List top findings (max 20)
|
|
50
|
+
const topFindings = allFindings.slice(0, 20);
|
|
51
|
+
if (topFindings.length > 0) {
|
|
52
|
+
lines.push('<details>');
|
|
53
|
+
lines.push('<summary>Findings</summary>');
|
|
54
|
+
lines.push('');
|
|
55
|
+
lines.push('| File | Line | Rule | Message |');
|
|
56
|
+
lines.push('|------|------|------|---------|');
|
|
57
|
+
for (const f of topFindings) {
|
|
58
|
+
const shortFile = f.file.replace(/.*\//, '');
|
|
59
|
+
lines.push(`| \`${shortFile}\` | ${f.line} | ${f.rule} | ${f.message} |`);
|
|
60
|
+
}
|
|
61
|
+
if (allFindings.length > 20) {
|
|
62
|
+
lines.push('');
|
|
63
|
+
lines.push(`_...and ${allFindings.length - 20} more findings._`);
|
|
64
|
+
}
|
|
65
|
+
lines.push('');
|
|
66
|
+
lines.push('</details>');
|
|
67
|
+
}
|
|
68
|
+
return lines.join('\n');
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/reporters/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/reporters/terminal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,UAAU,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAc7C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CA6BvF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.formatTerminalReport = formatTerminalReport;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const SEVERITY_COLORS = {
|
|
9
|
+
info: chalk_1.default.blue,
|
|
10
|
+
warning: chalk_1.default.yellow,
|
|
11
|
+
error: chalk_1.default.red,
|
|
12
|
+
};
|
|
13
|
+
const SEVERITY_ICONS = {
|
|
14
|
+
info: 'i',
|
|
15
|
+
warning: '!',
|
|
16
|
+
error: 'x',
|
|
17
|
+
};
|
|
18
|
+
function formatTerminalReport(summary, trend) {
|
|
19
|
+
const lines = [];
|
|
20
|
+
for (const result of summary.results) {
|
|
21
|
+
if (result.findings.length === 0)
|
|
22
|
+
continue;
|
|
23
|
+
lines.push('');
|
|
24
|
+
lines.push(chalk_1.default.underline(result.file));
|
|
25
|
+
for (const finding of result.findings) {
|
|
26
|
+
const color = SEVERITY_COLORS[finding.severity];
|
|
27
|
+
const icon = SEVERITY_ICONS[finding.severity];
|
|
28
|
+
const location = chalk_1.default.gray(`${finding.file}:${finding.line}:${finding.column}`);
|
|
29
|
+
const rule = chalk_1.default.gray(`(${finding.rule})`);
|
|
30
|
+
lines.push(` ${color(icon)} ${location} ${finding.message} ${rule}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
lines.push('');
|
|
34
|
+
lines.push(formatScoreLine(summary.score));
|
|
35
|
+
lines.push(chalk_1.default.gray(`${summary.totalFindings} finding${summary.totalFindings === 1 ? '' : 's'} across ${summary.results.length} file${summary.results.length === 1 ? '' : 's'}`));
|
|
36
|
+
if (trend) {
|
|
37
|
+
lines.push(formatTrendLine(trend));
|
|
38
|
+
}
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
41
|
+
function formatScoreLine(score) {
|
|
42
|
+
const label = 'Slop Score';
|
|
43
|
+
if (score <= 20)
|
|
44
|
+
return chalk_1.default.green(`${label}: ${score}/100 - Clean`);
|
|
45
|
+
if (score <= 50)
|
|
46
|
+
return chalk_1.default.yellow(`${label}: ${score}/100 - Moderate`);
|
|
47
|
+
return chalk_1.default.red(`${label}: ${score}/100 - High`);
|
|
48
|
+
}
|
|
49
|
+
function formatTrendLine(trend) {
|
|
50
|
+
if (trend.direction === 'first-scan') {
|
|
51
|
+
return chalk_1.default.gray('First scan — no trend data yet.');
|
|
52
|
+
}
|
|
53
|
+
const arrow = trend.direction === 'improving' ? chalk_1.default.green('↓') :
|
|
54
|
+
trend.direction === 'worsening' ? chalk_1.default.red('↑') :
|
|
55
|
+
chalk_1.default.gray('→');
|
|
56
|
+
const delta = trend.delta !== null ? (trend.delta > 0 ? `+${trend.delta}` : `${trend.delta}`) : '';
|
|
57
|
+
return `${arrow} Trend: ${delta} from previous score of ${trend.previous}`;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/reporters/terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-code-paths.d.ts","sourceRoot":"","sources":["../../src/rules/dead-code-paths.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,UAAU,CAAC;AAa9C,eAAO,MAAM,aAAa,EAAE,IA8C3B,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deadCodePaths = void 0;
|
|
4
|
+
const parser_1 = require("../parser");
|
|
5
|
+
const TERMINATING_STATEMENTS = new Set([
|
|
6
|
+
'return_statement',
|
|
7
|
+
'throw_statement',
|
|
8
|
+
'break_statement',
|
|
9
|
+
'continue_statement',
|
|
10
|
+
]);
|
|
11
|
+
function isBlockLike(node) {
|
|
12
|
+
return node.type === 'statement_block' || node.type === 'block';
|
|
13
|
+
}
|
|
14
|
+
exports.deadCodePaths = {
|
|
15
|
+
name: 'dead-code-paths',
|
|
16
|
+
description: 'Flags unreachable code after unconditional return, throw, break, or continue statements',
|
|
17
|
+
severity: 'warning',
|
|
18
|
+
check(tree, source, filePath) {
|
|
19
|
+
const findings = [];
|
|
20
|
+
const flagged = new Set();
|
|
21
|
+
(0, parser_1.walkTree)(tree.rootNode, (node) => {
|
|
22
|
+
if (!isBlockLike(node))
|
|
23
|
+
return;
|
|
24
|
+
const children = node.namedChildren;
|
|
25
|
+
for (let i = 0; i < children.length - 1; i++) {
|
|
26
|
+
const stmt = children[i];
|
|
27
|
+
// Check if this statement is a terminating statement
|
|
28
|
+
if (!TERMINATING_STATEMENTS.has(stmt.type))
|
|
29
|
+
continue;
|
|
30
|
+
// Everything after a terminating statement in the same block is dead code
|
|
31
|
+
for (let j = i + 1; j < children.length; j++) {
|
|
32
|
+
const dead = children[j];
|
|
33
|
+
// Skip comments — they're not executable code
|
|
34
|
+
if (dead.type === 'comment')
|
|
35
|
+
continue;
|
|
36
|
+
const lineKey = dead.startPosition.row;
|
|
37
|
+
if (flagged.has(lineKey))
|
|
38
|
+
continue;
|
|
39
|
+
flagged.add(lineKey);
|
|
40
|
+
findings.push({
|
|
41
|
+
file: filePath,
|
|
42
|
+
line: dead.startPosition.row + 1,
|
|
43
|
+
column: dead.startPosition.column + 1,
|
|
44
|
+
endLine: dead.endPosition.row + 1,
|
|
45
|
+
endColumn: dead.endPosition.column + 1,
|
|
46
|
+
rule: 'dead-code-paths',
|
|
47
|
+
severity: 'warning',
|
|
48
|
+
message: `Unreachable code after ${stmt.type.replace('_statement', '')} on line ${stmt.startPosition.row + 1}`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
break; // No need to check further in this block
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return findings;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=dead-code-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-code-paths.js","sourceRoot":"","sources":["../../src/rules/dead-code-paths.ts"],"names":[],"mappings":";;;AACA,sCAAqC;AAGrC,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,oBAAoB;CACrB,CAAC,CAAC;AAEH,SAAS,WAAW,CAAC,IAAuB;IAC1C,OAAO,IAAI,CAAC,IAAI,KAAK,iBAAiB,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC;AAClE,CAAC;AAEY,QAAA,aAAa,GAAS;IACjC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EAAE,yFAAyF;IACtG,QAAQ,EAAE,SAAS;IAEnB,KAAK,CAAC,IAAiB,EAAE,MAAc,EAAE,QAAgB;QACvD,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAElC,IAAA,iBAAQ,EAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;YAC/B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;gBAAE,OAAO;YAE/B,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC;YACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAEzB,qDAAqD;gBACrD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAErD,0EAA0E;gBAC1E,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;oBACzB,8CAA8C;oBAC9C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;wBAAE,SAAS;oBAEtC,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;oBACvC,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;wBAAE,SAAS;oBACnC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBAErB,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;wBAChC,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;wBACrC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC;wBACjC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;wBACtC,IAAI,EAAE,iBAAiB;wBACvB,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,0BAA0B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,EAAE;qBAC/G,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,CAAC,yCAAyC;YAClD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excessive-comments.d.ts","sourceRoot":"","sources":["../../src/rules/excessive-comments.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,UAAU,CAAC;AAK9C,eAAO,MAAM,iBAAiB,EAAE,IAoD/B,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.excessiveComments = void 0;
|
|
4
|
+
const COMMENT_DENSITY_THRESHOLD = 0.5; // >50% of lines have trailing comments
|
|
5
|
+
const MIN_LINES_TO_CHECK = 10; // Don't flag very short files
|
|
6
|
+
exports.excessiveComments = {
|
|
7
|
+
name: 'excessive-comments',
|
|
8
|
+
description: 'Flags files or functions with excessive inline comment density (>50% of lines)',
|
|
9
|
+
severity: 'info',
|
|
10
|
+
check(tree, source, filePath) {
|
|
11
|
+
const findings = [];
|
|
12
|
+
const lines = source.split('\n');
|
|
13
|
+
if (lines.length < MIN_LINES_TO_CHECK)
|
|
14
|
+
return findings;
|
|
15
|
+
// Count lines that have trailing inline comments (not standalone comment lines)
|
|
16
|
+
let codeLines = 0;
|
|
17
|
+
let trailingCommentLines = 0;
|
|
18
|
+
for (const line of lines) {
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
if (trimmed.length === 0)
|
|
21
|
+
continue;
|
|
22
|
+
// Skip standalone comment lines — those are fine
|
|
23
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
codeLines++;
|
|
27
|
+
// Check for trailing comments on code lines
|
|
28
|
+
// This is a heuristic — we look for // or # after code content
|
|
29
|
+
// Being careful to not match // inside strings
|
|
30
|
+
if (hasTrailingComment(trimmed)) {
|
|
31
|
+
trailingCommentLines++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (codeLines < MIN_LINES_TO_CHECK)
|
|
35
|
+
return findings;
|
|
36
|
+
const density = trailingCommentLines / codeLines;
|
|
37
|
+
if (density > COMMENT_DENSITY_THRESHOLD) {
|
|
38
|
+
findings.push({
|
|
39
|
+
file: filePath,
|
|
40
|
+
line: 1,
|
|
41
|
+
column: 1,
|
|
42
|
+
endLine: lines.length,
|
|
43
|
+
endColumn: 1,
|
|
44
|
+
rule: 'excessive-comments',
|
|
45
|
+
severity: 'info',
|
|
46
|
+
message: `${Math.round(density * 100)}% of code lines have trailing comments (${trailingCommentLines}/${codeLines}) — high comment density is a common AI code pattern`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return findings;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
function hasTrailingComment(line) {
|
|
53
|
+
// Simple heuristic: look for // or # that's not inside a string
|
|
54
|
+
let inSingleQuote = false;
|
|
55
|
+
let inDoubleQuote = false;
|
|
56
|
+
let inTemplate = false;
|
|
57
|
+
for (let i = 0; i < line.length; i++) {
|
|
58
|
+
const ch = line[i];
|
|
59
|
+
const prev = i > 0 ? line[i - 1] : '';
|
|
60
|
+
if (prev === '\\')
|
|
61
|
+
continue;
|
|
62
|
+
if (ch === "'" && !inDoubleQuote && !inTemplate)
|
|
63
|
+
inSingleQuote = !inSingleQuote;
|
|
64
|
+
if (ch === '"' && !inSingleQuote && !inTemplate)
|
|
65
|
+
inDoubleQuote = !inDoubleQuote;
|
|
66
|
+
if (ch === '`' && !inSingleQuote && !inDoubleQuote)
|
|
67
|
+
inTemplate = !inTemplate;
|
|
68
|
+
if (inSingleQuote || inDoubleQuote || inTemplate)
|
|
69
|
+
continue;
|
|
70
|
+
// JS/TS trailing comment
|
|
71
|
+
if (ch === '/' && i + 1 < line.length && line[i + 1] === '/') {
|
|
72
|
+
// Make sure there's actual code before the comment
|
|
73
|
+
const before = line.slice(0, i).trim();
|
|
74
|
+
if (before.length > 0)
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
// Python trailing comment
|
|
78
|
+
if (ch === '#') {
|
|
79
|
+
const before = line.slice(0, i).trim();
|
|
80
|
+
if (before.length > 0)
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=excessive-comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excessive-comments.js","sourceRoot":"","sources":["../../src/rules/excessive-comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hallucinated-imports.d.ts","sourceRoot":"","sources":["../../src/rules/hallucinated-imports.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAW,IAAI,EAAY,MAAM,UAAU,CAAC;AAqIxD,eAAO,MAAM,mBAAmB,EAAE,IAgDjC,CAAC"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.hallucinatedImports = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const parser_1 = require("../parser");
|
|
40
|
+
const languages_1 = require("../languages");
|
|
41
|
+
function getJSImportSources(tree) {
|
|
42
|
+
const imports = [];
|
|
43
|
+
const importStmts = (0, parser_1.findNodes)(tree.rootNode, 'import_statement');
|
|
44
|
+
for (const stmt of importStmts) {
|
|
45
|
+
(0, parser_1.walkTree)(stmt, (node) => {
|
|
46
|
+
if (node.type === 'string' && node.parent?.type === 'import_statement') {
|
|
47
|
+
const source = node.text.replace(/['"]/g, '');
|
|
48
|
+
imports.push({ source, node: stmt });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Also check require() calls
|
|
53
|
+
const callNodes = (0, parser_1.findNodes)(tree.rootNode, 'call_expression');
|
|
54
|
+
for (const call of callNodes) {
|
|
55
|
+
const fn = call.childForFieldName('function');
|
|
56
|
+
if (fn?.text === 'require' && call.childForFieldName('arguments')) {
|
|
57
|
+
const args = call.childForFieldName('arguments');
|
|
58
|
+
if (args && args.namedChildren.length === 1) {
|
|
59
|
+
const arg = args.namedChildren[0];
|
|
60
|
+
if (arg.type === 'string') {
|
|
61
|
+
const source = arg.text.replace(/['"]/g, '');
|
|
62
|
+
imports.push({ source, node: call });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return imports;
|
|
68
|
+
}
|
|
69
|
+
function getPythonImportModules(tree) {
|
|
70
|
+
const imports = [];
|
|
71
|
+
// import foo / import foo.bar
|
|
72
|
+
const importStmts = (0, parser_1.findNodes)(tree.rootNode, 'import_statement');
|
|
73
|
+
for (const stmt of importStmts) {
|
|
74
|
+
(0, parser_1.walkTree)(stmt, (node) => {
|
|
75
|
+
if (node.type === 'dotted_name' && node.parent?.type === 'import_statement') {
|
|
76
|
+
// Get just the top-level module name
|
|
77
|
+
const fullName = node.text;
|
|
78
|
+
const topLevel = fullName.split('.')[0];
|
|
79
|
+
imports.push({ source: topLevel, node: stmt });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// from foo import bar / from foo.bar import baz
|
|
84
|
+
const fromStmts = (0, parser_1.findNodes)(tree.rootNode, 'import_from_statement');
|
|
85
|
+
for (const stmt of fromStmts) {
|
|
86
|
+
(0, parser_1.walkTree)(stmt, (node) => {
|
|
87
|
+
if (node.type === 'dotted_name' && node.parent?.type === 'import_from_statement') {
|
|
88
|
+
// Only get the module name (first dotted_name child, not imported names)
|
|
89
|
+
const isModuleName = !node.previousNamedSibling || node.previousNamedSibling.type !== 'dotted_name';
|
|
90
|
+
if (isModuleName) {
|
|
91
|
+
const topLevel = node.text.split('.')[0];
|
|
92
|
+
imports.push({ source: topLevel, node: stmt });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// relative imports like `from .foo import bar` are always local — skip
|
|
96
|
+
if (node.type === 'relative_import') {
|
|
97
|
+
// Mark as safe — no push
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return imports;
|
|
102
|
+
}
|
|
103
|
+
function isRelativeImport(source) {
|
|
104
|
+
return source.startsWith('.') || source.startsWith('/');
|
|
105
|
+
}
|
|
106
|
+
function isNodeModuleInstalled(source, filePath) {
|
|
107
|
+
// Check node_modules from the file's directory up
|
|
108
|
+
const pkgName = source.startsWith('@') ? source.split('/').slice(0, 2).join('/') : source.split('/')[0];
|
|
109
|
+
let dir = path.dirname(filePath);
|
|
110
|
+
for (let i = 0; i < 50; i++) {
|
|
111
|
+
const candidate = path.join(dir, 'node_modules', pkgName);
|
|
112
|
+
if (fs.existsSync(candidate))
|
|
113
|
+
return true;
|
|
114
|
+
const parent = path.dirname(dir);
|
|
115
|
+
if (parent === dir)
|
|
116
|
+
break;
|
|
117
|
+
dir = parent;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
function resolveJSImport(source, filePath, language) {
|
|
122
|
+
// Relative imports — check if file exists
|
|
123
|
+
if (isRelativeImport(source)) {
|
|
124
|
+
const dir = path.dirname(filePath);
|
|
125
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', ''];
|
|
126
|
+
for (const ext of extensions) {
|
|
127
|
+
if (fs.existsSync(path.join(dir, source + ext)))
|
|
128
|
+
return true;
|
|
129
|
+
if (fs.existsSync(path.join(dir, source, 'index' + ext)))
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const stdlib = (0, languages_1.getStdlibModules)(language);
|
|
135
|
+
if (stdlib.has(source))
|
|
136
|
+
return true;
|
|
137
|
+
if (isNodeModuleInstalled(source, filePath))
|
|
138
|
+
return true;
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
function resolvePythonImport(source, filePath) {
|
|
142
|
+
const stdlib = (0, languages_1.getStdlibModules)('python');
|
|
143
|
+
if (stdlib.has(source))
|
|
144
|
+
return true;
|
|
145
|
+
// Check if it's a local file/package in the project
|
|
146
|
+
const dir = path.dirname(filePath);
|
|
147
|
+
if (fs.existsSync(path.join(dir, source + '.py')))
|
|
148
|
+
return true;
|
|
149
|
+
if (fs.existsSync(path.join(dir, source)) && fs.existsSync(path.join(dir, source, '__init__.py')))
|
|
150
|
+
return true;
|
|
151
|
+
// Check parent directories for the module (common in Python projects)
|
|
152
|
+
let searchDir = path.dirname(dir);
|
|
153
|
+
for (let i = 0; i < 5; i++) {
|
|
154
|
+
if (fs.existsSync(path.join(searchDir, source + '.py')))
|
|
155
|
+
return true;
|
|
156
|
+
if (fs.existsSync(path.join(searchDir, source, '__init__.py')))
|
|
157
|
+
return true;
|
|
158
|
+
const parent = path.dirname(searchDir);
|
|
159
|
+
if (parent === searchDir)
|
|
160
|
+
break;
|
|
161
|
+
searchDir = parent;
|
|
162
|
+
}
|
|
163
|
+
// Can't definitively say it's hallucinated — might be an installed package
|
|
164
|
+
// Be conservative: only flag if it looks like a plausible hallucination
|
|
165
|
+
return true; // Default to "resolved" to avoid false positives
|
|
166
|
+
}
|
|
167
|
+
exports.hallucinatedImports = {
|
|
168
|
+
name: 'hallucinated-imports',
|
|
169
|
+
description: 'Flags imports of modules that do not exist in the project or known standard libraries',
|
|
170
|
+
severity: 'error',
|
|
171
|
+
check(tree, source, filePath) {
|
|
172
|
+
const findings = [];
|
|
173
|
+
const language = detectLang(filePath);
|
|
174
|
+
if (!language)
|
|
175
|
+
return findings;
|
|
176
|
+
if ((0, languages_1.isJSLike)(language)) {
|
|
177
|
+
const imports = getJSImportSources(tree);
|
|
178
|
+
for (const { source: src, node } of imports) {
|
|
179
|
+
if (!src)
|
|
180
|
+
continue;
|
|
181
|
+
if (!resolveJSImport(src, filePath, language)) {
|
|
182
|
+
findings.push({
|
|
183
|
+
file: filePath,
|
|
184
|
+
line: node.startPosition.row + 1,
|
|
185
|
+
column: node.startPosition.column + 1,
|
|
186
|
+
endLine: node.endPosition.row + 1,
|
|
187
|
+
endColumn: node.endPosition.column + 1,
|
|
188
|
+
rule: 'hallucinated-imports',
|
|
189
|
+
severity: 'error',
|
|
190
|
+
message: `Import '${src}' could not be resolved — module not found in project or node_modules`,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else if ((0, languages_1.isPython)(language)) {
|
|
196
|
+
const imports = getPythonImportModules(tree);
|
|
197
|
+
for (const { source: src, node } of imports) {
|
|
198
|
+
if (!src)
|
|
199
|
+
continue;
|
|
200
|
+
if (!resolvePythonImport(src, filePath)) {
|
|
201
|
+
findings.push({
|
|
202
|
+
file: filePath,
|
|
203
|
+
line: node.startPosition.row + 1,
|
|
204
|
+
column: node.startPosition.column + 1,
|
|
205
|
+
endLine: node.endPosition.row + 1,
|
|
206
|
+
endColumn: node.endPosition.column + 1,
|
|
207
|
+
rule: 'hallucinated-imports',
|
|
208
|
+
severity: 'error',
|
|
209
|
+
message: `Import '${src}' could not be resolved — module not found in project or standard library`,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return findings;
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
function detectLang(filePath) {
|
|
218
|
+
if (filePath.endsWith('.tsx'))
|
|
219
|
+
return 'tsx';
|
|
220
|
+
if (filePath.endsWith('.ts'))
|
|
221
|
+
return 'typescript';
|
|
222
|
+
if (filePath.endsWith('.js') || filePath.endsWith('.jsx') || filePath.endsWith('.mjs') || filePath.endsWith('.cjs'))
|
|
223
|
+
return 'javascript';
|
|
224
|
+
if (filePath.endsWith('.py') || filePath.endsWith('.pyi'))
|
|
225
|
+
return 'python';
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=hallucinated-imports.js.map
|