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.
@@ -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 scanner_1 = require("./scanner");
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
- const imports = await (0, scanner_1.scanProject)(directory);
21
- const report = await (0, validator_1.validateImports)(directory, imports);
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 in installed version.`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost-import-hunter",
3
- "version": "1.0.2",
3
+ "version": "2.0.0",
4
4
  "description": "Deterministic tool to detect AI hallucinations and code bloat by verifying import safety against installed node_modules",
5
5
  "repository": {
6
6
  "type": "git",