ghost-import-hunter 1.0.2 ā 2.0.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/dist/analyzer.js +142 -0
- package/dist/index.js +5 -5
- package/package.json +1 -1
package/dist/analyzer.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
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.analyzeProject = analyzeProject;
|
|
37
|
+
const ts = __importStar(require("typescript"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const glob_1 = require("glob");
|
|
40
|
+
async function analyzeProject(directory) {
|
|
41
|
+
const report = { hallucinations: [], unused: [] };
|
|
42
|
+
// 1. Find all files in the project
|
|
43
|
+
const files = await (0, glob_1.glob)('**/*.{ts,tsx,js,jsx}', {
|
|
44
|
+
cwd: directory,
|
|
45
|
+
ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**'],
|
|
46
|
+
absolute: true
|
|
47
|
+
});
|
|
48
|
+
if (files.length === 0) {
|
|
49
|
+
return report;
|
|
50
|
+
}
|
|
51
|
+
// 2. Create TypeScript Program
|
|
52
|
+
const program = ts.createProgram(files, {
|
|
53
|
+
target: ts.ScriptTarget.ESNext,
|
|
54
|
+
module: ts.ModuleKind.CommonJS,
|
|
55
|
+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
56
|
+
allowJs: true,
|
|
57
|
+
jsx: ts.JsxEmit.React,
|
|
58
|
+
esModuleInterop: true,
|
|
59
|
+
skipLibCheck: true
|
|
60
|
+
});
|
|
61
|
+
const checker = program.getTypeChecker();
|
|
62
|
+
// 3. Analyze each file
|
|
63
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
64
|
+
// Skip external library files (node_modules)
|
|
65
|
+
if (sourceFile.fileName.includes('node_modules'))
|
|
66
|
+
continue;
|
|
67
|
+
// Also ensure we are only analyzing files we actually found (extra safety)
|
|
68
|
+
// Normalize paths for comparison
|
|
69
|
+
const normalizedFilePath = path.resolve(sourceFile.fileName);
|
|
70
|
+
const isProjectFile = files.some(f => path.resolve(f) === normalizedFilePath);
|
|
71
|
+
if (!isProjectFile)
|
|
72
|
+
continue;
|
|
73
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
74
|
+
if (ts.isImportDeclaration(node)) {
|
|
75
|
+
visitImportDeclaration(node, sourceFile, checker, report);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return report;
|
|
80
|
+
}
|
|
81
|
+
function visitImportDeclaration(node, sourceFile, checker, report) {
|
|
82
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
83
|
+
if (!ts.isStringLiteral(moduleSpecifier))
|
|
84
|
+
return;
|
|
85
|
+
const moduleName = moduleSpecifier.text;
|
|
86
|
+
// Resolve Module Symbol
|
|
87
|
+
const symbol = checker.getSymbolAtLocation(moduleSpecifier);
|
|
88
|
+
if (!symbol) {
|
|
89
|
+
// Module not found at all
|
|
90
|
+
// Get line number
|
|
91
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
92
|
+
report.hallucinations.push({
|
|
93
|
+
file: sourceFile.fileName,
|
|
94
|
+
line: line + 1,
|
|
95
|
+
module: moduleName,
|
|
96
|
+
member: '*' // Whole module missing
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Check Named Imports
|
|
101
|
+
if (node.importClause && node.importClause.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
102
|
+
node.importClause.namedBindings.elements.forEach(element => {
|
|
103
|
+
const importName = element.propertyName?.text || element.name.text;
|
|
104
|
+
const importSymbol = checker.getSymbolAtLocation(element.name);
|
|
105
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(element.getStart());
|
|
106
|
+
if (importSymbol) {
|
|
107
|
+
const aliasedSymbol = checker.getAliasedSymbol(importSymbol);
|
|
108
|
+
if (aliasedSymbol) {
|
|
109
|
+
// Check for "unknown" symbol or missing declarations which indicates a hallucination
|
|
110
|
+
if (aliasedSymbol.escapedName === 'unknown' || !aliasedSymbol.declarations || aliasedSymbol.declarations.length === 0) {
|
|
111
|
+
report.hallucinations.push({
|
|
112
|
+
file: sourceFile.fileName,
|
|
113
|
+
line: line + 1,
|
|
114
|
+
module: moduleName,
|
|
115
|
+
member: importName
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// If no aliased symbol, it might be a direct interface/type or something we couldn't resolve deeply
|
|
121
|
+
// For now, if we have a symbol, we assume it exists to avoid false positives unless we are sure.
|
|
122
|
+
// But in our prototype `ghost` gave a symbol that resolved to `unknown`.
|
|
123
|
+
// If `getAliasedSymbol` throws or returns undefined, it might be a local symbol.
|
|
124
|
+
// Double check with module exports if possible
|
|
125
|
+
// const moduleExports = checker.getExportsOfModule(symbol);
|
|
126
|
+
// if (!moduleExports.some(e => e.name === importName)) {
|
|
127
|
+
// // Potential hallucination or just a type?
|
|
128
|
+
// }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// No symbol found at location - definitely a hallucination
|
|
133
|
+
report.hallucinations.push({
|
|
134
|
+
file: sourceFile.fileName,
|
|
135
|
+
line: line + 1,
|
|
136
|
+
module: moduleName,
|
|
137
|
+
member: importName
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -6,8 +6,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
-
const
|
|
10
|
-
const validator_1 = require("./validator");
|
|
9
|
+
const analyzer_1 = require("./analyzer");
|
|
11
10
|
const program = new commander_1.Command();
|
|
12
11
|
program
|
|
13
12
|
.name('ghost-hunter')
|
|
@@ -17,14 +16,15 @@ program
|
|
|
17
16
|
.action(async (directory) => {
|
|
18
17
|
console.log(chalk_1.default.blue(`š» Ghost Hunter scanning: ${directory}...`));
|
|
19
18
|
try {
|
|
20
|
-
|
|
21
|
-
const report = await (0,
|
|
19
|
+
// New v2 Engine using TS Compiler API
|
|
20
|
+
const report = await (0, analyzer_1.analyzeProject)(directory);
|
|
22
21
|
if (report.hallucinations.length > 0) {
|
|
23
22
|
console.log(chalk_1.default.red('\nšØ Hallucinations Detected (AI Lied!):'));
|
|
24
23
|
report.hallucinations.forEach(h => {
|
|
25
|
-
console.log(` - ${chalk_1.default.bold(h.module)}: Used member ${chalk_1.default.bold(h.member)} does not exist
|
|
24
|
+
console.log(` - ${chalk_1.default.bold(h.module)}: Used member ${chalk_1.default.bold(h.member)} does not exist.`);
|
|
26
25
|
console.log(` File: ${h.file}:${h.line}`);
|
|
27
26
|
});
|
|
27
|
+
process.exit(1); // Fail the build
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
30
|
console.log(chalk_1.default.green('\nā
No Hallucinations detected.'));
|
package/package.json
CHANGED