Skip to content

Commit 4ef3c05

Browse files
feat: Java/Maven support with prompt.xml parsing and new technnology definitions
1 parent 9d45fea commit 4ef3c05

6 files changed

Lines changed: 348 additions & 27 deletions

File tree

package-lock.json

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"commander": "^11.0.0",
4747
"cosmiconfig": "^9.0.0",
4848
"fast-glob": "^3.3.2",
49+
"fast-xml-parser": "^5.3.6",
4950
"fs-extra": "^11.2.0",
5051
"lucide-static": "^0.555.0",
5152
"ora": "^7.0.1"
Lines changed: 34 additions & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

src/scan.ts

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from 'fs';
22
import path from 'path';
3+
import { XMLParser } from 'fast-xml-parser';
34
import { techMap } from './techMap';
45
import simpleIconsHex from './simple-icons-hex.json';
56
import { generateMarkdown, copyAssets } from './output';
@@ -37,7 +38,7 @@ function getPackageJson(projectPath: string) {
3738
const content = fs.readFileSync(pkgPathUnderscore, 'utf-8');
3839
return JSON.parse(content);
3940
} catch (e: any) {
40-
throw new Error(`Failed to read _package.json: ${e.message}`);
41+
console.warn(`Failed to read _package.json: ${e.message}`);
4142
}
4243
}
4344

@@ -47,13 +48,60 @@ function getPackageJson(projectPath: string) {
4748
const content = fs.readFileSync(pkgPath, 'utf-8');
4849
return JSON.parse(content);
4950
} catch (e: any) {
50-
throw new Error(`Failed to read package.json: ${e.message}`);
51+
console.warn(`Failed to read package.json: ${e.message}`);
5152
}
5253
}
5354

5455
return null;
5556
}
5657

58+
// Helper to look for pom.xml or _pom.xml
59+
function getPomXml(projectPath: string) {
60+
const pomPath = path.join(projectPath, 'pom.xml');
61+
const pomPathUnderscore = path.join(projectPath, '_pom.xml');
62+
63+
// Check if we are inside public/stackscan
64+
const isInsideStackScanDir = projectPath.includes(path.join('public', 'stackscan'));
65+
66+
// Priority 1: Check for active pom.xml and rename it
67+
if (fs.existsSync(pomPath) && isInsideStackScanDir) {
68+
try {
69+
console.log(`Renaming ${pomPath} to ${pomPathUnderscore}`);
70+
fs.renameSync(pomPath, pomPathUnderscore);
71+
} catch (e: any) {
72+
console.warn(`Failed to rename pom.xml to _pom.xml: ${e.message}`);
73+
}
74+
}
75+
76+
// Read _pom.xml or pom.xml
77+
let xmlContent: string | null = null;
78+
if (fs.existsSync(pomPathUnderscore)) {
79+
try {
80+
xmlContent = fs.readFileSync(pomPathUnderscore, 'utf-8');
81+
} catch (e: any) {
82+
console.warn(`Failed to read _pom.xml: ${e.message}`);
83+
}
84+
} else if (fs.existsSync(pomPath)) {
85+
try {
86+
xmlContent = fs.readFileSync(pomPath, 'utf-8');
87+
} catch (e: any) {
88+
console.warn(`Failed to read pom.xml: ${e.message}`);
89+
}
90+
}
91+
92+
if (xmlContent) {
93+
try {
94+
const parser = new XMLParser();
95+
return parser.parse(xmlContent);
96+
} catch (e: any) {
97+
console.warn(`Failed to parse XML: ${e.message}`);
98+
}
99+
}
100+
101+
return null;
102+
}
103+
104+
57105
const CATEGORY_PRIORITY = [
58106
"language",
59107
"framework",
@@ -112,12 +160,34 @@ interface SyncOptions {
112160

113161
async function analyzeProject(projectPath: string, options: SyncOptions): Promise<any[]> {
114162
const pkg = getPackageJson(projectPath);
115-
if (!pkg) {
116-
throw new Error(`No package.json or _package.json found at ${projectPath}`);
163+
const pom = getPomXml(projectPath);
164+
165+
if (!pkg && !pom) {
166+
throw new Error(`No package.json or pom.xml found at ${projectPath}`);
117167
}
118168

119169
// 1. Detect Tech
120-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
170+
const allDeps: Record<string, any> = {};
171+
172+
// Process package.json
173+
if (pkg) {
174+
Object.assign(allDeps, pkg.dependencies, pkg.devDependencies);
175+
}
176+
177+
// Process pom.xml
178+
if (pom && pom.project && pom.project.dependencies && pom.project.dependencies.dependency) {
179+
let deps = pom.project.dependencies.dependency;
180+
if (!Array.isArray(deps)) {
181+
deps = [deps];
182+
}
183+
184+
deps.forEach((d: any) => {
185+
// Map groupId:artifactId and just artifactId
186+
if (d.artifactId) allDeps[d.artifactId] = "latest";
187+
if (d.groupId && d.artifactId) allDeps[`${d.groupId}:${d.artifactId}`] = "latest";
188+
});
189+
}
190+
121191
const detectedTechs: any[] = [];
122192

123193
Object.keys(allDeps).forEach(dep => {
@@ -261,8 +331,12 @@ async function scan(targetPath?: string | object, optionsOrUndefined?: SyncOptio
261331

262332
for (const dir of projectDirs) {
263333
const projectPath = path.join(BASE_DIR, dir.name);
334+
335+
// Check for package.json OR pom.xml (or their underscored variants)
336+
const hasPackageJson = fs.existsSync(path.join(projectPath, 'package.json')) || fs.existsSync(path.join(projectPath, '_package.json'));
337+
const hasPomXml = fs.existsSync(path.join(projectPath, 'pom.xml')) || fs.existsSync(path.join(projectPath, '_pom.xml'));
264338

265-
if (fs.existsSync(path.join(projectPath, 'package.json'))) {
339+
if (hasPackageJson || hasPomXml) {
266340
try {
267341
const techsWithUrls = await analyzeProject(projectPath, options);
268342

@@ -288,7 +362,7 @@ async function scan(targetPath?: string | object, optionsOrUndefined?: SyncOptio
288362
console.error(`❌ Error processing ${dir.name}:`, err.message);
289363
}
290364
} else {
291-
console.warn(`⚠️ Skipping "${dir.name}": No package.json found.`);
365+
console.warn(`⚠️ Skipping "${dir.name}": No package.json or pom.xml found.`);
292366
}
293367
}
294368

0 commit comments

Comments
 (0)