impact-analysis 2.0.1 → 2.0.2

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/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # Impact Analysis
2
2
 
3
- Analyze the real impact of code changes in JavaScript, TypeScript, React, Vue, and Angular projects.
3
+ [![npm version](https://badge.fury.io/js/impact-analysis.svg)](https://www.npmjs.com/package/impact-analysis)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.4-blue.svg)](https://www.typescriptlang.org/)
6
+
7
+ > Analyze the real impact of code changes in JavaScript, TypeScript, React, Vue, and Angular projects.
8
+
9
+ **Keywords:** `impact-analysis` · `dependency-graph` · `code-analysis` · `static-analysis` · `react` · `vue` · `angular` · `typescript` · `ast` · `ast-parser` · `code-impact` · `dependency-tracker` · `git-diff` · `change-analysis`
10
+
11
+ ---
4
12
 
5
13
  ## Features
6
14
 
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.explainImpact = explainImpact;
4
+ const generative_ai_1 = require("@google/generative-ai");
5
+ async function explainImpact(results) {
6
+ if (!process.env.GEMINI_API_KEY) {
7
+ console.log('No GEMINI_API_KEY found. Skipping AI explanation.');
8
+ return null;
9
+ }
10
+ try {
11
+ const genAI = new generative_ai_1.GoogleGenerativeAI(process.env.GEMINI_API_KEY);
12
+ const model = genAI.getGenerativeModel({ model: 'gemini-3-flash-preview' });
13
+ const prompt = `Analyze these code changes and explain their impact:
14
+
15
+ ${JSON.stringify(results.map(r => ({
16
+ file: r.changedFile,
17
+ impactedFiles: r.impactedFiles.length,
18
+ risk: r.risk
19
+ })), null, 2)}
20
+
21
+ Provide a brief summary of the overall risk and key concerns.`;
22
+ const result = await model.generateContent(prompt);
23
+ const response = await result.response;
24
+ return response.text();
25
+ }
26
+ catch (error) {
27
+ throw new Error(`AI explanation failed: ${error instanceof Error ? error.message : String(error)}`);
28
+ }
29
+ }
@@ -0,0 +1,39 @@
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.loadCache = loadCache;
7
+ exports.saveCache = saveCache;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const CACHE_FILE = ".impact-analysis-cache.json";
10
+ function loadCache() {
11
+ try {
12
+ if (!fs_1.default.existsSync(CACHE_FILE)) {
13
+ return null;
14
+ }
15
+ const data = JSON.parse(fs_1.default.readFileSync(CACHE_FILE, "utf-8"));
16
+ return {
17
+ imports: new Map(Object.entries(data.imports).map(([key, value]) => [key, new Set(value)])),
18
+ importedBy: new Map(Object.entries(data.importedBy).map(([key, value]) => [key, new Set(value)])),
19
+ componentUsage: new Map(Object.entries(data.componentUsage).map(([key, value]) => [key, new Set(value)]))
20
+ };
21
+ }
22
+ catch (error) {
23
+ console.warn('Cache file corrupted, rebuilding...');
24
+ return null;
25
+ }
26
+ }
27
+ function saveCache(data) {
28
+ try {
29
+ const serializable = {
30
+ imports: Object.fromEntries(Array.from(data.imports.entries()).map(([key, value]) => [key, Array.from(value)])),
31
+ importedBy: Object.fromEntries(Array.from(data.importedBy.entries()).map(([key, value]) => [key, Array.from(value)])),
32
+ componentUsage: Object.fromEntries(Array.from(data.componentUsage.entries()).map(([key, value]) => [key, Array.from(value)]))
33
+ };
34
+ fs_1.default.writeFileSync(CACHE_FILE, JSON.stringify(serializable, null, 2));
35
+ }
36
+ catch (error) {
37
+ console.warn('Failed to save cache:', error instanceof Error ? error.message : String(error));
38
+ }
39
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ require("dotenv/config");
41
+ const path_1 = __importDefault(require("path"));
42
+ const child_process_1 = require("child_process");
43
+ const util_1 = require("util");
44
+ const getChangedFiles_1 = require("./git/getChangedFiles");
45
+ const scanRepo_1 = require("./scanner/scanRepo");
46
+ const buildGraph_1 = require("./graph/buildGraph");
47
+ const analyzer_1 = require("./core/analyzer");
48
+ const html_generator_1 = require("./report/html-generator");
49
+ const explain_1 = require("./ai/explain");
50
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
51
+ async function main() {
52
+ const args = process.argv.slice(2);
53
+ const showHtml = args.includes('--html');
54
+ const useAI = args.includes('--ai');
55
+ const clearCache = args.includes('--clear-cache');
56
+ const baseBranch = args.find(arg => !arg.startsWith('--')) || 'main';
57
+ try {
58
+ if (clearCache) {
59
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
60
+ if (fs.existsSync('.impact-analysis-cache.json')) {
61
+ fs.unlinkSync('.impact-analysis-cache.json');
62
+ console.log('Cache cleared.');
63
+ }
64
+ }
65
+ console.log('Scanning repository...');
66
+ const allFiles = await (0, scanRepo_1.scanRepo)();
67
+ console.log(`Building dependency graph from ${allFiles.length} files...`);
68
+ const graph = await (0, buildGraph_1.buildGraph)(allFiles);
69
+ console.log(`Getting changed files against ${baseBranch}...`);
70
+ const changedFiles = await (0, getChangedFiles_1.getChangedFiles)(baseBranch);
71
+ if (changedFiles.length === 0) {
72
+ console.log('No changed files found.');
73
+ return;
74
+ }
75
+ console.log(`Found ${changedFiles.length} changed files. Analyzing impact...`);
76
+ const dependencyMap = {};
77
+ graph.importedBy.forEach((importers, file) => {
78
+ dependencyMap[file] = Array.from(importers);
79
+ });
80
+ const results = (0, analyzer_1.analyzeImpact)(changedFiles, dependencyMap);
81
+ if (useAI) {
82
+ try {
83
+ console.log('Getting AI explanation...');
84
+ const explanation = await (0, explain_1.explainImpact)(results);
85
+ if (explanation) {
86
+ results.aiExplanation = explanation;
87
+ }
88
+ }
89
+ catch (error) {
90
+ console.warn('AI explanation failed:', error instanceof Error ? error.message : String(error));
91
+ }
92
+ }
93
+ console.log('\n--- Impact Analysis Results ---\n');
94
+ results.forEach((result, index) => {
95
+ console.log(`${index + 1}. ${path_1.default.basename(result.changedFile)}`);
96
+ console.log(` Risk: ${result.risk}`);
97
+ console.log(` Impacted files: ${result.impactedFiles.length}`);
98
+ if (result.impactedFiles.length > 0) {
99
+ result.impactedFiles.slice(0, 5).forEach(file => {
100
+ console.log(` - ${path_1.default.basename(file)}`);
101
+ });
102
+ if (result.impactedFiles.length > 5) {
103
+ console.log(` ... and ${result.impactedFiles.length - 5} more`);
104
+ }
105
+ }
106
+ console.log('');
107
+ });
108
+ // Generate HTML by default
109
+ console.log('Generating HTML report...');
110
+ const htmlPath = (0, html_generator_1.generateHtmlReport)(results);
111
+ console.log(`Report saved: ${htmlPath}`);
112
+ if (showHtml) {
113
+ try {
114
+ // Use platform-specific command to open the file
115
+ const command = process.platform === 'win32'
116
+ ? `start "" "${htmlPath}"`
117
+ : process.platform === 'darwin'
118
+ ? `open "${htmlPath}"`
119
+ : `xdg-open "${htmlPath}"`;
120
+ await execAsync(command);
121
+ console.log(`Report opened in browser`);
122
+ }
123
+ catch (err) {
124
+ console.log(`Could not auto-open browser. Please open: ${htmlPath}`);
125
+ }
126
+ }
127
+ else {
128
+ console.log(`\nTo view the graphical report, run: impact-analysis --html`);
129
+ }
130
+ }
131
+ catch (error) {
132
+ console.error('Error:', error instanceof Error ? error.message : String(error));
133
+ process.exit(1);
134
+ }
135
+ }
136
+ main();
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeImpact = analyzeImpact;
4
+ function analyzeImpact(changedFiles, dependencyMap) {
5
+ return changedFiles.map(changed => {
6
+ const impacted = Object.entries(dependencyMap)
7
+ .filter(([, deps]) => deps.includes(changed))
8
+ .map(([file]) => file);
9
+ let risk = 'LOW';
10
+ if (impacted.length > 5)
11
+ risk = 'HIGH';
12
+ else if (impacted.length > 2)
13
+ risk = 'MEDIUM';
14
+ return {
15
+ changedFile: changed,
16
+ impactedFiles: impacted,
17
+ risk
18
+ };
19
+ });
20
+ }
@@ -0,0 +1,21 @@
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.getChangedFiles = getChangedFiles;
7
+ const simple_git_1 = __importDefault(require("simple-git"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function getChangedFiles(base) {
10
+ try {
11
+ const git = (0, simple_git_1.default)();
12
+ const diff = await git.diff(["--name-only", base]);
13
+ return diff
14
+ .split("\n")
15
+ .filter(f => /\.(js|jsx|ts|tsx|vue)$/.test(f))
16
+ .map(f => path_1.default.resolve(process.cwd(), f));
17
+ }
18
+ catch (error) {
19
+ throw new Error(`Failed to get changed files. Make sure Git is installed and you're in a Git repository. Base branch: ${base}`);
20
+ }
21
+ }
@@ -1,52 +1,56 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { extractImports } from "../parser/parseJS";
4
- import { parseVue } from "../parser/parseVue";
5
- import { DependencyGraph } from "./types";
6
- import { loadCache, saveCache } from "../cache/graphCache";
7
-
8
- function resolveImportPath(importPath: string, fromFile: string, allFiles: string[]): string | null {
9
- if (
10
- (!importPath.startsWith('.') && !importPath.startsWith('/') &&
11
- !importPath.startsWith('~') && !importPath.startsWith('@') &&
12
- !importPath.startsWith('#')) ||
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.buildGraph = buildGraph;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const parseJS_1 = require("../parser/parseJS");
10
+ const parseVue_1 = require("../parser/parseVue");
11
+ const graphCache_1 = require("../cache/graphCache");
12
+ function resolveImportPath(importPath, fromFile, allFiles) {
13
+ if ((!importPath.startsWith('.') && !importPath.startsWith('/') &&
14
+ !importPath.startsWith('~') && !importPath.startsWith('@') &&
15
+ !importPath.startsWith('#')) ||
13
16
  importPath.startsWith('virtual:') ||
14
17
  importPath.startsWith('~icons/') ||
15
18
  importPath.startsWith('unplugin-') ||
16
- /\.(css|scss|sass|less|styl|stylus|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/.test(importPath)
17
- ) {
19
+ /\.(css|scss|sass|less|styl|stylus|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/.test(importPath)) {
18
20
  return null;
19
21
  }
20
-
21
- const baseDir = path.dirname(fromFile);
22
+ const baseDir = path_1.default.dirname(fromFile);
22
23
  const projectRoot = process.cwd();
23
24
  const extensions = ['.vue', '.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs', ''];
24
25
  const indexFiles = ['/index.vue', '/index.js', '/index.ts', '/index.jsx', '/index.tsx', '/index.mjs'];
25
-
26
- let resolvedBase: string | null = null;
27
-
26
+ let resolvedBase = null;
28
27
  // Handle various path alias patterns
29
28
  if (importPath.startsWith('~/') || importPath.startsWith('@/')) {
30
29
  // Common aliases: ~/ or @/ → project root
31
- resolvedBase = path.join(projectRoot, importPath.substring(2));
32
- } else if (importPath.startsWith('#')) {
30
+ resolvedBase = path_1.default.join(projectRoot, importPath.substring(2));
31
+ }
32
+ else if (importPath.startsWith('#')) {
33
33
  // Nuxt 3 aliases: #app, #imports, #components
34
34
  // Map to common locations
35
35
  if (importPath.startsWith('#app') || importPath.startsWith('#imports')) {
36
36
  return null; // Skip Nuxt internals
37
37
  }
38
38
  if (importPath.startsWith('#components/')) {
39
- resolvedBase = path.join(projectRoot, 'components', importPath.substring(12));
40
- } else {
39
+ resolvedBase = path_1.default.join(projectRoot, 'components', importPath.substring(12));
40
+ }
41
+ else {
41
42
  return null;
42
43
  }
43
- } else if (importPath.startsWith('/')) {
44
+ }
45
+ else if (importPath.startsWith('/')) {
44
46
  // Absolute path from root
45
- resolvedBase = path.join(projectRoot, importPath.substring(1));
46
- } else if (importPath.startsWith('.')) {
47
+ resolvedBase = path_1.default.join(projectRoot, importPath.substring(1));
48
+ }
49
+ else if (importPath.startsWith('.')) {
47
50
  // Relative path
48
- resolvedBase = path.join(baseDir, importPath);
49
- } else {
51
+ resolvedBase = path_1.default.join(baseDir, importPath);
52
+ }
53
+ else {
50
54
  // Could be a tsconfig path alias (e.g., @components/...)
51
55
  // Try common patterns
52
56
  const aliasPatterns = [
@@ -56,63 +60,49 @@ function resolveImportPath(importPath: string, fromFile: string, allFiles: strin
56
60
  ['@src/', 'src/'],
57
61
  ['@/', ''],
58
62
  ];
59
-
60
63
  for (const [alias, replacement] of aliasPatterns) {
61
64
  if (importPath.startsWith(alias)) {
62
- resolvedBase = path.join(projectRoot, importPath.replace(alias, replacement));
65
+ resolvedBase = path_1.default.join(projectRoot, importPath.replace(alias, replacement));
63
66
  break;
64
67
  }
65
68
  }
66
-
67
69
  if (!resolvedBase) {
68
70
  return null; // Likely an external package
69
71
  }
70
72
  }
71
-
72
73
  // Normalize path separators
73
- resolvedBase = path.normalize(resolvedBase);
74
-
74
+ resolvedBase = path_1.default.normalize(resolvedBase);
75
75
  const hasExtension = extensions.some(ext => ext && importPath.endsWith(ext));
76
-
77
76
  for (const ext of extensions) {
78
77
  const withExt = hasExtension ? resolvedBase : resolvedBase + ext;
79
-
80
78
  if (allFiles.includes(withExt)) {
81
79
  return withExt;
82
80
  }
83
81
  }
84
-
85
82
  for (const indexFile of indexFiles) {
86
83
  const resolved = resolvedBase + indexFile;
87
84
  if (allFiles.includes(resolved)) {
88
85
  return resolved;
89
86
  }
90
87
  }
91
-
92
88
  return null;
93
89
  }
94
-
95
- export async function buildGraph(files: string[]): Promise<DependencyGraph> {
96
- const cached = loadCache();
90
+ async function buildGraph(files) {
91
+ const cached = (0, graphCache_1.loadCache)();
97
92
  if (cached) {
98
93
  console.log('Using cached dependency graph...');
99
94
  return cached;
100
95
  }
101
-
102
- const graph: DependencyGraph = {
96
+ const graph = {
103
97
  imports: new Map(),
104
98
  importedBy: new Map(),
105
99
  componentUsage: new Map()
106
100
  };
107
-
108
101
  let filesProcessed = 0;
109
-
110
102
  for (const file of files) {
111
- const code = fs.readFileSync(file, "utf-8");
112
-
103
+ const code = fs_1.default.readFileSync(file, "utf-8");
113
104
  if (file.endsWith(".vue")) {
114
- const { imports, components } = parseVue(code);
115
-
105
+ const { imports, components } = (0, parseVue_1.parseVue)(code);
116
106
  imports.forEach(imp => {
117
107
  const resolved = resolveImportPath(imp, file, files);
118
108
  if (resolved) {
@@ -120,16 +110,12 @@ export async function buildGraph(files: string[]): Promise<DependencyGraph> {
120
110
  graph.importedBy.set(resolved, (graph.importedBy.get(resolved) || new Set()).add(file));
121
111
  }
122
112
  });
123
-
124
113
  components.forEach(component => {
125
- graph.componentUsage.set(
126
- component,
127
- (graph.componentUsage.get(component) || new Set()).add(file)
128
- );
114
+ graph.componentUsage.set(component, (graph.componentUsage.get(component) || new Set()).add(file));
129
115
  });
130
- } else {
131
- const imports = extractImports(code);
132
-
116
+ }
117
+ else {
118
+ const imports = (0, parseJS_1.extractImports)(code);
133
119
  imports.forEach(imp => {
134
120
  const resolved = resolveImportPath(imp, file, files);
135
121
  if (resolved) {
@@ -138,14 +124,9 @@ export async function buildGraph(files: string[]): Promise<DependencyGraph> {
138
124
  }
139
125
  });
140
126
  }
141
-
142
127
  filesProcessed++;
143
128
  }
144
-
145
129
  console.log(`Dependency graph built: ${graph.importedBy.size} files with dependencies`);
146
-
147
- saveCache(graph);
148
-
130
+ (0, graphCache_1.saveCache)(graph);
149
131
  return graph;
150
132
  }
151
-
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,70 @@
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.extractImports = extractImports;
7
+ const parser_1 = require("@babel/parser");
8
+ const traverse_1 = __importDefault(require("@babel/traverse"));
9
+ function extractImports(code) {
10
+ const imports = [];
11
+ try {
12
+ const ast = (0, parser_1.parse)(code, {
13
+ sourceType: "unambiguous",
14
+ plugins: [
15
+ "typescript",
16
+ "jsx",
17
+ "decorators-legacy",
18
+ "classProperties",
19
+ "dynamicImport",
20
+ "importMeta",
21
+ "topLevelAwait",
22
+ "classStaticBlock",
23
+ "optionalChaining",
24
+ "nullishCoalescingOperator"
25
+ ],
26
+ errorRecovery: true
27
+ });
28
+ (0, traverse_1.default)(ast, {
29
+ // Static imports: import x from 'module'
30
+ ImportDeclaration(path) {
31
+ imports.push(path.node.source.value);
32
+ },
33
+ // Dynamic imports: import('module')
34
+ Import(path) {
35
+ const parent = path.parent;
36
+ if (parent.type === 'CallExpression' && parent.arguments[0]) {
37
+ const arg = parent.arguments[0];
38
+ if (arg.type === 'StringLiteral') {
39
+ imports.push(arg.value);
40
+ }
41
+ }
42
+ },
43
+ // Export from: export { x } from 'module'
44
+ ExportNamedDeclaration(path) {
45
+ if (path.node.source) {
46
+ imports.push(path.node.source.value);
47
+ }
48
+ },
49
+ // Export all: export * from 'module'
50
+ ExportAllDeclaration(path) {
51
+ imports.push(path.node.source.value);
52
+ },
53
+ // CommonJS require: const x = require('module')
54
+ CallExpression(path) {
55
+ if (path.node.callee.type === 'Identifier' &&
56
+ path.node.callee.name === 'require' &&
57
+ path.node.arguments[0] &&
58
+ path.node.arguments[0].type === 'StringLiteral') {
59
+ imports.push(path.node.arguments[0].value);
60
+ }
61
+ }
62
+ });
63
+ }
64
+ catch (error) {
65
+ // Silently skip files with parse errors
66
+ // Most errors are from node_modules or generated files
67
+ }
68
+ // Remove duplicates
69
+ return [...new Set(imports)];
70
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseVue = parseVue;
4
+ const compiler_sfc_1 = require("@vue/compiler-sfc");
5
+ const parseJS_1 = require("./parseJS");
6
+ const parseVueTemplate_1 = require("./parseVueTemplate");
7
+ function parseVue(code) {
8
+ try {
9
+ const { descriptor } = (0, compiler_sfc_1.parse)(code);
10
+ const scriptContent = descriptor.script?.content || '';
11
+ const scriptSetupContent = descriptor.scriptSetup?.content || '';
12
+ const allScript = scriptContent + '\n' + scriptSetupContent;
13
+ return {
14
+ imports: allScript.trim() ? (0, parseJS_1.extractImports)(allScript) : [],
15
+ components: (0, parseVueTemplate_1.extractTemplateComponents)(code)
16
+ };
17
+ }
18
+ catch (error) {
19
+ return {
20
+ imports: [],
21
+ components: []
22
+ };
23
+ }
24
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractTemplateComponents = extractTemplateComponents;
4
+ const compiler_sfc_1 = require("@vue/compiler-sfc");
5
+ function extractTemplateComponents(code) {
6
+ const { descriptor } = (0, compiler_sfc_1.parse)(code);
7
+ const template = descriptor.template?.content || "";
8
+ const componentRegex = /<([A-Z][\w-]*)/g;
9
+ const components = new Set();
10
+ let match;
11
+ while ((match = componentRegex.exec(template))) {
12
+ components.add(match[1]);
13
+ }
14
+ return Array.from(components);
15
+ }
@@ -1,6 +1,11 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
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.generateHtmlReport = generateHtmlReport;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
4
9
  const HTML_TEMPLATE = `<!DOCTYPE html>
5
10
  <html>
6
11
  <head>
@@ -324,20 +329,13 @@ const HTML_TEMPLATE = `<!DOCTYPE html>
324
329
  </script>
325
330
  </body>
326
331
  </html>`;
327
-
328
- export function generateHtmlReport(data: any) {
329
- const outDir = path.resolve('.impact-analysis');
330
- const jsonPath = path.join(outDir, 'impact.json');
331
- const htmlPath = path.join(outDir, 'report.html');
332
-
333
- fs.mkdirSync(outDir, { recursive: true });
334
- fs.writeFileSync(jsonPath, JSON.stringify(data, null, 2));
335
-
336
- const finalHtml = HTML_TEMPLATE.replace(
337
- '__IMPACT_DATA__',
338
- JSON.stringify(data)
339
- );
340
-
341
- fs.writeFileSync(htmlPath, finalHtml);
342
- return htmlPath;
332
+ function generateHtmlReport(data) {
333
+ const outDir = path_1.default.resolve('.impact-analysis');
334
+ const jsonPath = path_1.default.join(outDir, 'impact.json');
335
+ const htmlPath = path_1.default.join(outDir, 'report.html');
336
+ fs_1.default.mkdirSync(outDir, { recursive: true });
337
+ fs_1.default.writeFileSync(jsonPath, JSON.stringify(data, null, 2));
338
+ const finalHtml = HTML_TEMPLATE.replace('__IMPACT_DATA__', JSON.stringify(data));
339
+ fs_1.default.writeFileSync(htmlPath, finalHtml);
340
+ return htmlPath;
343
341
  }
@@ -0,0 +1,30 @@
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.scanRepo = scanRepo;
7
+ const fast_glob_1 = __importDefault(require("fast-glob"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function scanRepo() {
10
+ try {
11
+ const files = await (0, fast_glob_1.default)("**/*.{js,jsx,ts,tsx,vue}", {
12
+ ignore: [
13
+ "node_modules/**",
14
+ "dist/**",
15
+ "build/**",
16
+ "*.min.js",
17
+ "**/*.min.js",
18
+ ".next/**",
19
+ "coverage/**",
20
+ ".cache/**",
21
+ "out/**",
22
+ ".nuxt/**"
23
+ ]
24
+ });
25
+ return files.map(file => path_1.default.resolve(process.cwd(), file));
26
+ }
27
+ catch (error) {
28
+ throw new Error(`Failed to scan repository: ${error instanceof Error ? error.message : String(error)}`);
29
+ }
30
+ }