fix(project-detect): match packageKeys on boundaries, not substrings (#2181)

Framework detection matched a dependency against a framework's packageKeys
with unbounded substring containment (dep.includes(key)), so any dependency
whose name merely contained a key was misclassified: `preact` and even
`reactive` were both detected as `react`.

Match only when the dependency equals the key, or the key is a prefix
immediately followed by a delimiter (/ . _ -). This still matches every real
case (react-dom, @remix-run/node, spring-boot-starter, org.springframework.boot,
github.com/labstack/echo/v4, phoenix_live_view) while excluding preact/reactive
(and incidentally nextra). Adds regression tests.

Co-authored-by: bymle <229636660+bymle@users.noreply.github.com>
This commit is contained in:
bymle
2026-06-07 13:25:34 +08:00
committed by GitHub
parent 09e2bc58d3
commit 9c35aef60f
2 changed files with 47 additions and 1 deletions

View File

@@ -390,7 +390,20 @@ function detectProjectType(projectDir) {
depList = elixirDeps;
break;
}
hasDep = rule.packageKeys.some(key => depList.some(dep => dep.toLowerCase().includes(key.toLowerCase())));
// Boundary-aware match: a dependency matches a packageKey only when it
// equals the key, or the key is a prefix immediately followed by a
// delimiter (/ . _ -). Plain substring matching wrongly classified
// `preact` / `reactive` as `react`. This still matches the real cases:
// react-dom, @remix-run/node, spring-boot-starter, org.springframework.boot,
// github.com/labstack/echo/v4, phoenix_live_view.
hasDep = rule.packageKeys.some(key => {
const k = key.toLowerCase();
return depList.some(dep => {
const d = dep.toLowerCase();
if (!d.startsWith(k)) return false;
return d.length === k.length || /[/._-]/.test(d[k.length]);
});
});
}
if (hasMarker || hasDep) {