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:
Derek Pearson 2026-03-22 08:36:40 -04:00
parent 30d799e1b9
commit 414a1433ba
2 changed files with 60 additions and 2 deletions

View file

@ -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);

View file

@ -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);
}
});