fix(skills): address QA round 17
QA17-1: Support multiline arrays inside [project.optional-dependencies] so FastAPI declared in multiline optional dependency groups is detected correctly. QA17-2: Extend Spring Boot version-catalog detection to bundle aliases (libs.bundles.*) by resolving bundles back to library aliases that map to org.springframework.boot artifacts. Add regression tests for: - multiline optional FastAPI dependencies - Spring Boot bundle alias detection
This commit is contained in:
parent
30d799e1b9
commit
414a1433ba
2 changed files with 60 additions and 2 deletions
|
|
@ -825,6 +825,7 @@ function containsSpringBootMarker(
|
|||
|
||||
const springBootAliases = new Set<string>();
|
||||
const springBootLibraries = new Set<string>();
|
||||
const springBootBundles = new Set<string>();
|
||||
for (const relativePath of versionCatalogFiles) {
|
||||
try {
|
||||
const raw = readBounded(join(basePath, relativePath), 64 * 1024);
|
||||
|
|
@ -839,6 +840,18 @@ function containsSpringBootMarker(
|
|||
while ((match = libraryRe.exec(content)) !== null) {
|
||||
springBootLibraries.add(normalizePluginAlias(match[1]));
|
||||
}
|
||||
|
||||
const bundleRe = /^\s*([A-Za-z0-9_.-]+)\s*=\s*\[([\s\S]*?)\]/gm;
|
||||
while ((match = bundleRe.exec(content)) !== null) {
|
||||
const bundleAlias = normalizePluginAlias(`bundles.${match[1]}`);
|
||||
const referencedAliases = match[2]
|
||||
.split(",")
|
||||
.map((part) => normalizePluginAlias(part.replace(/["'\s]/g, "")))
|
||||
.filter(Boolean);
|
||||
if (referencedAliases.some((alias) => springBootLibraries.has(alias))) {
|
||||
springBootBundles.add(bundleAlias);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// unreadable version catalog — continue scanning others
|
||||
}
|
||||
|
|
@ -848,7 +861,7 @@ function containsSpringBootMarker(
|
|||
if (springBootAliases.has(alias)) return true;
|
||||
}
|
||||
for (const alias of usedLibraryAliases) {
|
||||
if (springBootLibraries.has(alias)) return true;
|
||||
if (springBootLibraries.has(alias) || springBootBundles.has(alias)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -888,6 +901,7 @@ function extractPyprojectDependencySections(content: string): string {
|
|||
const collected: string[] = [];
|
||||
let section = "";
|
||||
let collectingProjectDeps = false;
|
||||
let collectingOptionalDeps = false;
|
||||
let bracketDepth = 0;
|
||||
|
||||
for (const line of lines) {
|
||||
|
|
@ -902,6 +916,15 @@ function extractPyprojectDependencySections(content: string): string {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (collectingOptionalDeps) {
|
||||
collected.push(line);
|
||||
bracketDepth += countChar(line, "[") - countChar(line, "]");
|
||||
if (bracketDepth <= 0) {
|
||||
collectingOptionalDeps = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const sectionMatch = trimmed.match(/^\[([^\]]+)\]$/);
|
||||
if (sectionMatch) {
|
||||
section = sectionMatch[1].trim();
|
||||
|
|
@ -923,7 +946,10 @@ function extractPyprojectDependencySections(content: string): string {
|
|||
if (section === "project.optional-dependencies") {
|
||||
const equalsIndex = line.indexOf("=");
|
||||
if (equalsIndex !== -1) {
|
||||
collected.push(line.slice(equalsIndex + 1));
|
||||
const value = line.slice(equalsIndex + 1);
|
||||
collected.push(value);
|
||||
bracketDepth = countChar(value, "[") - countChar(value, "]");
|
||||
collectingOptionalDeps = bracketDepth > 0;
|
||||
}
|
||||
} else {
|
||||
collected.push(line);
|
||||
|
|
|
|||
|
|
@ -823,6 +823,21 @@ test("detectProjectSignals: pyproject optional-dependency group name does not tr
|
|||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: pyproject multiline optional dependency emits dep:fastapi", () => {
|
||||
const dir = makeTempDir("signals-fastapi-pyproject-optional-multiline");
|
||||
try {
|
||||
writeFileSync(
|
||||
join(dir, "pyproject.toml"),
|
||||
'[project]\ndependencies = ["flask>=3.0"]\n\n[project.optional-dependencies]\napi = [\n "fastapi>=0.115",\n "uvicorn>=0.30",\n]\n',
|
||||
"utf-8",
|
||||
);
|
||||
const signals = detectProjectSignals(dir);
|
||||
assert.ok(signals.detectedFiles.includes("dep:fastapi"), "multiline optional dependency arrays should trigger FastAPI detection");
|
||||
} finally {
|
||||
cleanup(dir);
|
||||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: FastAPI direct reference with @ emits dep:fastapi", () => {
|
||||
const dir = makeTempDir("signals-fastapi-direct-reference");
|
||||
try {
|
||||
|
|
@ -1060,3 +1075,20 @@ test("detectProjectSignals: Spring Boot version-catalog library alias emits dep:
|
|||
cleanup(dir);
|
||||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: Spring Boot version-catalog bundle alias emits dep:spring-boot", () => {
|
||||
const dir = makeTempDir("signals-spring-version-catalog-bundle");
|
||||
try {
|
||||
mkdirSync(join(dir, "gradle"), { recursive: true });
|
||||
writeFileSync(join(dir, "build.gradle.kts"), "dependencies { implementation(libs.bundles.backend.web) }", "utf-8");
|
||||
writeFileSync(
|
||||
join(dir, "gradle", "libs.versions.toml"),
|
||||
"[libraries]\nspring-boot-starter-web = { module = 'org.springframework.boot:spring-boot-starter-web', version = '3.2.0' }\n\n[bundles]\nbackend-web = ['spring-boot-starter-web']\n",
|
||||
"utf-8",
|
||||
);
|
||||
const signals = detectProjectSignals(dir);
|
||||
assert.ok(signals.detectedFiles.includes("dep:spring-boot"), "Spring Boot bundle aliases should trigger detection");
|
||||
} finally {
|
||||
cleanup(dir);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue