greenbump 0.7.0 → 0.9.0
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 +73 -15
- package/README.zh-CN.md +37 -4
- package/dist/action/github.test.js +37 -0
- package/dist/action/github.test.js.map +1 -0
- package/dist/action/main.js +19 -4
- package/dist/action/main.js.map +1 -1
- package/dist/agent/calllog.js +23 -0
- package/dist/agent/calllog.js.map +1 -0
- package/dist/agent/calllog.test.js +77 -0
- package/dist/agent/calllog.test.js.map +1 -0
- package/dist/agent/fixer.js +246 -29
- package/dist/agent/fixer.js.map +1 -1
- package/dist/agent/fixer.test.js +299 -0
- package/dist/agent/fixer.test.js.map +1 -1
- package/dist/agent/guard.js +37 -0
- package/dist/agent/guard.js.map +1 -0
- package/dist/agent/guard.test.js +48 -0
- package/dist/agent/guard.test.js.map +1 -0
- package/dist/agent/provider-anthropic.js +2 -2
- package/dist/agent/provider-anthropic.js.map +1 -1
- package/dist/agent/provider-openai.js +2 -2
- package/dist/agent/provider-openai.js.map +1 -1
- package/dist/benchmarks/run.js +0 -0
- package/dist/benchmarks/run.js.map +1 -0
- package/dist/cli/interactive.js +46 -11
- package/dist/cli/interactive.js.map +1 -1
- package/dist/cli.js +37 -7
- package/dist/cli.js.map +1 -1
- package/dist/engine/analyzers/changelog-parser.js +51 -2
- package/dist/engine/analyzers/changelog-parser.js.map +1 -1
- package/dist/engine/batch.js +54 -10
- package/dist/engine/batch.js.map +1 -1
- package/dist/engine/cache/manager.js +19 -1
- package/dist/engine/cache/manager.js.map +1 -1
- package/dist/engine/cache/manager.test.js +17 -0
- package/dist/engine/cache/manager.test.js.map +1 -1
- package/dist/engine/change-detector.js +52 -23
- package/dist/engine/change-detector.js.map +1 -1
- package/dist/engine/change-detector.test.js +72 -0
- package/dist/engine/change-detector.test.js.map +1 -0
- package/dist/engine/changelog.js +22 -2
- package/dist/engine/changelog.js.map +1 -1
- package/dist/engine/changelog.test.js +31 -0
- package/dist/engine/changelog.test.js.map +1 -0
- package/dist/engine/checks.js +10 -4
- package/dist/engine/checks.js.map +1 -1
- package/dist/engine/context/optimizer.js +14 -44
- package/dist/engine/context/optimizer.js.map +1 -1
- package/dist/engine/context/optimizer.test.js +23 -1
- package/dist/engine/context/optimizer.test.js.map +1 -1
- package/dist/engine/ecosystems/cargo.js +2 -1
- package/dist/engine/ecosystems/cargo.js.map +1 -1
- package/dist/engine/ecosystems/conan.js +2 -1
- package/dist/engine/ecosystems/conan.js.map +1 -1
- package/dist/engine/ecosystems/elm.js +2 -1
- package/dist/engine/ecosystems/elm.js.map +1 -1
- package/dist/engine/ecosystems/gradle.js +2 -1
- package/dist/engine/ecosystems/gradle.js.map +1 -1
- package/dist/engine/ecosystems/http.js +18 -0
- package/dist/engine/ecosystems/http.js.map +1 -0
- package/dist/engine/ecosystems/maven.js +2 -1
- package/dist/engine/ecosystems/maven.js.map +1 -1
- package/dist/engine/ecosystems/swiftpm.js +2 -1
- package/dist/engine/ecosystems/swiftpm.js.map +1 -1
- package/dist/engine/exec.js +37 -7
- package/dist/engine/exec.js.map +1 -1
- package/dist/engine/exec.test.js +36 -0
- package/dist/engine/exec.test.js.map +1 -0
- package/dist/engine/fixer/patterns.js +55 -18
- package/dist/engine/fixer/patterns.js.map +1 -1
- package/dist/engine/fixer/patterns.test.js +95 -1
- package/dist/engine/fixer/patterns.test.js.map +1 -1
- package/dist/engine/git.js +38 -16
- package/dist/engine/git.js.map +1 -1
- package/dist/engine/git.test.js +51 -1
- package/dist/engine/git.test.js.map +1 -1
- package/dist/engine/perf/metrics.js +44 -57
- package/dist/engine/perf/metrics.js.map +1 -1
- package/dist/engine/run.js +141 -15
- package/dist/engine/run.js.map +1 -1
- package/dist/engine/run.test.js +65 -1
- package/dist/engine/run.test.js.map +1 -1
- package/dist/engine/sandbox/docker-manager.js +52 -26
- package/dist/engine/sandbox/docker-manager.js.map +1 -1
- package/dist/engine/sandbox/template-generator.js +0 -4
- package/dist/engine/sandbox/template-generator.js.map +1 -1
- package/dist/engine/sandbox/template-generator.test.js +16 -0
- package/dist/engine/sandbox/template-generator.test.js.map +1 -0
- package/dist/engine/scan/benchmark-crosscheck.test.js +116 -0
- package/dist/engine/scan/benchmark-crosscheck.test.js.map +1 -0
- package/dist/engine/scan/detect.js +96 -0
- package/dist/engine/scan/detect.js.map +1 -0
- package/dist/engine/scan/impact.js +128 -0
- package/dist/engine/scan/impact.js.map +1 -0
- package/dist/engine/scan/impact.test.js +217 -0
- package/dist/engine/scan/impact.test.js.map +1 -0
- package/dist/engine/scan/indexer.js +129 -0
- package/dist/engine/scan/indexer.js.map +1 -0
- package/dist/engine/scan/indexer.test.js +149 -0
- package/dist/engine/scan/indexer.test.js.map +1 -0
- package/dist/engine/scan/probes.js +92 -0
- package/dist/engine/scan/probes.js.map +1 -0
- package/dist/engine/scan/probes.test.js +100 -0
- package/dist/engine/scan/probes.test.js.map +1 -0
- package/dist/engine/verify.js +41 -58
- package/dist/engine/verify.js.map +1 -1
- package/dist/engine/verify.test.js +49 -0
- package/dist/engine/verify.test.js.map +1 -0
- package/dist/format.js +96 -0
- package/dist/format.js.map +1 -1
- package/dist/format.test.js +53 -0
- package/dist/format.test.js.map +1 -0
- package/dist/pr.test.js +48 -0
- package/dist/pr.test.js.map +1 -0
- package/package.json +4 -4
- package/dist/engine/dep-graph.js +0 -101
- package/dist/engine/dep-graph.js.map +0 -1
- package/dist/engine/staged-fix.js +0 -108
- package/dist/engine/staged-fix.js.map +0 -1
- package/dist/engine/stages.js +0 -125
- package/dist/engine/stages.js.map +0 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
import { isSensitivePath } from "../../agent/guard.js";
|
|
4
|
+
import { REQUIRES_IMPORT_PREFIX, escapeRegExpLiteral } from "./probes.js";
|
|
5
|
+
/**
|
|
6
|
+
* Single-pass file walker for impact analysis.
|
|
7
|
+
*
|
|
8
|
+
* Scans the project tree once, checking every line against every probe.
|
|
9
|
+
* Optimized for "find which files/lines match any of these patterns" —
|
|
10
|
+
* the inverse of `searchCode` (which is "find lines matching this one pattern").
|
|
11
|
+
*/
|
|
12
|
+
const IGNORE_DIRS = new Set(["node_modules", ".git", "dist", "build", ".next", "coverage", ".turbo"]);
|
|
13
|
+
const SEARCHABLE_EXT = new Set([
|
|
14
|
+
".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts",
|
|
15
|
+
".json", ".vue", ".svelte",
|
|
16
|
+
]);
|
|
17
|
+
const MAX_FILE_SIZE = 1_000_000;
|
|
18
|
+
/**
|
|
19
|
+
* Scan the project once, finding all matches for all probes.
|
|
20
|
+
*
|
|
21
|
+
* Returns hits grouped by probe key, plus a set of probe keys that hit the
|
|
22
|
+
* per-probe cap (indicating their match count is a lower bound).
|
|
23
|
+
*/
|
|
24
|
+
export async function scanOnce(cwd, probes, opts) {
|
|
25
|
+
const hits = [];
|
|
26
|
+
const truncatedProbes = new Set();
|
|
27
|
+
const probeHitsCount = new Map();
|
|
28
|
+
// Compile each probe's regex once, not once per line per probe. A pattern
|
|
29
|
+
// that fails to compile falls back to a literal match (same policy as the
|
|
30
|
+
// fix loop's search_code tool).
|
|
31
|
+
const compiled = [];
|
|
32
|
+
for (const probe of probes) {
|
|
33
|
+
probeHitsCount.set(probe.key, 0);
|
|
34
|
+
let re;
|
|
35
|
+
try {
|
|
36
|
+
re = new RegExp(probe.pattern, "g");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
re = new RegExp(escapeRegExpLiteral(probe.pattern), "g");
|
|
40
|
+
}
|
|
41
|
+
compiled.push({ probe, re });
|
|
42
|
+
}
|
|
43
|
+
async function walk(dir) {
|
|
44
|
+
let entries;
|
|
45
|
+
try {
|
|
46
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const e of entries) {
|
|
52
|
+
if (IGNORE_DIRS.has(e.name))
|
|
53
|
+
continue;
|
|
54
|
+
const abs = join(dir, e.name);
|
|
55
|
+
if (e.isDirectory()) {
|
|
56
|
+
await walk(abs);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const dot = e.name.lastIndexOf(".");
|
|
60
|
+
if (dot < 0 || !SEARCHABLE_EXT.has(e.name.slice(dot)))
|
|
61
|
+
continue;
|
|
62
|
+
if (isSensitivePath(e.name))
|
|
63
|
+
continue;
|
|
64
|
+
let content;
|
|
65
|
+
try {
|
|
66
|
+
const s = await stat(abs);
|
|
67
|
+
if (s.size > MAX_FILE_SIZE)
|
|
68
|
+
continue;
|
|
69
|
+
content = await readFile(abs, "utf8");
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const rel = relative(cwd, abs);
|
|
75
|
+
// Which packages this file imports — resolves the bare-identifier gate
|
|
76
|
+
// (`__requiresImport:<pkg>`) for probes in this same file, so the file
|
|
77
|
+
// is read exactly once.
|
|
78
|
+
const importsHere = new Set();
|
|
79
|
+
for (const m of content.matchAll(/(?:from|require\()\s*['"]([^'"]+)['"]/g)) {
|
|
80
|
+
importsHere.add(m[1]);
|
|
81
|
+
}
|
|
82
|
+
const lines = content.split("\n");
|
|
83
|
+
for (const { probe, re } of compiled) {
|
|
84
|
+
if ((probeHitsCount.get(probe.key) ?? 0) >= opts.perProbeCap) {
|
|
85
|
+
truncatedProbes.add(probe.key);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
// File-level gates: substring filter, or import scope for bare identifiers.
|
|
89
|
+
if (probe.fileFilter) {
|
|
90
|
+
if (probe.fileFilter.startsWith(REQUIRES_IMPORT_PREFIX)) {
|
|
91
|
+
const pkg = probe.fileFilter.slice(REQUIRES_IMPORT_PREFIX.length);
|
|
92
|
+
if (!importsHere.has(pkg))
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
else if (!rel.includes(probe.fileFilter)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (let i = 0; i < lines.length; i++) {
|
|
100
|
+
const line = lines[i];
|
|
101
|
+
re.lastIndex = 0;
|
|
102
|
+
let match;
|
|
103
|
+
while ((match = re.exec(line)) !== null) {
|
|
104
|
+
hits.push({
|
|
105
|
+
probeKey: probe.key,
|
|
106
|
+
file: rel,
|
|
107
|
+
line: i + 1,
|
|
108
|
+
excerpt: line.trim().slice(0, 200),
|
|
109
|
+
});
|
|
110
|
+
const n = (probeHitsCount.get(probe.key) ?? 0) + 1;
|
|
111
|
+
probeHitsCount.set(probe.key, n);
|
|
112
|
+
if (n >= opts.perProbeCap) {
|
|
113
|
+
truncatedProbes.add(probe.key);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
// Zero-width match guard — otherwise exec() would never advance.
|
|
117
|
+
if (match.index === re.lastIndex)
|
|
118
|
+
re.lastIndex++;
|
|
119
|
+
}
|
|
120
|
+
if ((probeHitsCount.get(probe.key) ?? 0) >= opts.perProbeCap)
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
await walk(cwd);
|
|
127
|
+
return { hits, truncatedProbes };
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=indexer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../../src/engine/scan/indexer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, writeFile, rm, mkdir } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join, dirname } from "node:path";
|
|
6
|
+
import { scanOnce } from "./indexer.js";
|
|
7
|
+
import { REQUIRES_IMPORT_PREFIX } from "./probes.js";
|
|
8
|
+
async function withProject(files, fn) {
|
|
9
|
+
const dir = await mkdtemp(join(tmpdir(), "greenbump-scan-"));
|
|
10
|
+
for (const [path, content] of Object.entries(files)) {
|
|
11
|
+
await mkdir(dirname(join(dir, path)), { recursive: true });
|
|
12
|
+
await writeFile(join(dir, path), content, "utf8");
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
await fn(dir);
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
await rm(dir, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function probe(pattern, key = "k0", fileFilter) {
|
|
22
|
+
return { key, pattern, refs: [{ depName: "pkg", changeIndex: 0 }], ...(fileFilter ? { fileFilter } : {}) };
|
|
23
|
+
}
|
|
24
|
+
test("scanOnce: reports file and 1-based line for each match", async () => {
|
|
25
|
+
await withProject({ "src/a.js": "line one\noldApi()\nline three\n" }, async (dir) => {
|
|
26
|
+
const { hits } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 200 });
|
|
27
|
+
assert.equal(hits.length, 1);
|
|
28
|
+
assert.equal(hits[0].file, join("src", "a.js"));
|
|
29
|
+
assert.equal(hits[0].line, 2);
|
|
30
|
+
assert.equal(hits[0].excerpt, "oldApi()");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
test("scanOnce: finds multiple matches across files", async () => {
|
|
34
|
+
await withProject({
|
|
35
|
+
"src/a.js": "oldApi()\n",
|
|
36
|
+
"src/nested/b.ts": "const x = oldApi;\n",
|
|
37
|
+
}, async (dir) => {
|
|
38
|
+
const { hits } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 200 });
|
|
39
|
+
const files = hits.map((h) => h.file).sort();
|
|
40
|
+
assert.deepEqual(files, [join("src", "a.js"), join("src", "nested", "b.ts")]);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
test("scanOnce: counts every occurrence on one line", async () => {
|
|
44
|
+
await withProject({ "a.js": "oldApi(); oldApi();\n" }, async (dir) => {
|
|
45
|
+
const { hits } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 200 });
|
|
46
|
+
assert.equal(hits.length, 2);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
test("scanOnce: caps matches per probe and flags the probe as truncated", async () => {
|
|
50
|
+
const manyLines = Array.from({ length: 50 }, () => "oldApi();").join("\n");
|
|
51
|
+
await withProject({ "a.js": manyLines }, async (dir) => {
|
|
52
|
+
const { hits, truncatedProbes } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 10 });
|
|
53
|
+
assert.equal(hits.length, 10);
|
|
54
|
+
assert.ok(truncatedProbes.has("k0"));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
test("scanOnce: a probe under its cap is not marked truncated", async () => {
|
|
58
|
+
await withProject({ "a.js": "oldApi();\n" }, async (dir) => {
|
|
59
|
+
const { truncatedProbes } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 10 });
|
|
60
|
+
assert.equal(truncatedProbes.size, 0);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
test("scanOnce: matches are attributed to the right probe when several are active", async () => {
|
|
64
|
+
await withProject({ "a.js": "alpha();\nbeta();\n" }, async (dir) => {
|
|
65
|
+
const { hits } = await scanOnce(dir, [probe("alpha", "k-alpha"), probe("beta", "k-beta")], { perProbeCap: 200 });
|
|
66
|
+
assert.equal(hits.length, 2);
|
|
67
|
+
const alpha = hits.find((h) => h.excerpt.startsWith("alpha"));
|
|
68
|
+
const beta = hits.find((h) => h.excerpt.startsWith("beta"));
|
|
69
|
+
assert.equal(alpha?.probeKey, "k-alpha");
|
|
70
|
+
assert.equal(beta?.probeKey, "k-beta");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
test("scanOnce: skips node_modules and other ignored directories", async () => {
|
|
74
|
+
await withProject({
|
|
75
|
+
"src/a.js": "oldApi();\n",
|
|
76
|
+
"node_modules/dep/index.js": "oldApi();\n",
|
|
77
|
+
"dist/bundle.js": "oldApi();\n",
|
|
78
|
+
}, async (dir) => {
|
|
79
|
+
const { hits } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 200 });
|
|
80
|
+
assert.equal(hits.length, 1);
|
|
81
|
+
assert.equal(hits[0].file, join("src", "a.js"));
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
test("scanOnce: never reports matches from secrets files", async () => {
|
|
85
|
+
await withProject({
|
|
86
|
+
"src/a.js": "oldApi();\n",
|
|
87
|
+
".env": "SECRET=oldApi\n",
|
|
88
|
+
"secrets.json": '{"k":"oldApi"}\n',
|
|
89
|
+
"credentials.json": '{"k":"oldApi"}\n',
|
|
90
|
+
}, async (dir) => {
|
|
91
|
+
const { hits } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 200 });
|
|
92
|
+
assert.equal(hits.length, 1);
|
|
93
|
+
assert.equal(hits[0].file, join("src", "a.js"));
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
test("scanOnce: only searches known source extensions", async () => {
|
|
97
|
+
await withProject({ "a.js": "oldApi();\n", "notes.txt": "oldApi();\n", "image.bin": "oldApi();\n" }, async (dir) => {
|
|
98
|
+
const { hits } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 200 });
|
|
99
|
+
assert.deepEqual(hits.map((h) => h.file), ["a.js"]);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
test("scanOnce: fileFilter restricts matches to matching paths", async () => {
|
|
103
|
+
await withProject({ "src/a.js": "oldApi();\n", "test/b.js": "oldApi();\n" }, async (dir) => {
|
|
104
|
+
const { hits } = await scanOnce(dir, [probe("oldApi", "k0", "test")], { perProbeCap: 200 });
|
|
105
|
+
assert.deepEqual(hits.map((h) => h.file), [join("test", "b.js")]);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
test("scanOnce: import gate limits bare-identifier probes to importing files", async () => {
|
|
109
|
+
await withProject({
|
|
110
|
+
"imports.js": "const { describe } = require('vitest');\ndescribe('x', () => {});\n",
|
|
111
|
+
"unrelated.js": "describe('this is not vitest');\n",
|
|
112
|
+
}, async (dir) => {
|
|
113
|
+
const { hits } = await scanOnce(dir, [probe("describe", "k0", `${REQUIRES_IMPORT_PREFIX}vitest`)], { perProbeCap: 200 });
|
|
114
|
+
// Both occurrences live in imports.js; unrelated.js must contribute none.
|
|
115
|
+
assert.deepEqual(hits.map((h) => h.file), ["imports.js", "imports.js"]);
|
|
116
|
+
assert.deepEqual(hits.map((h) => h.line), [1, 2]);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
test("scanOnce: import gate also matches ESM import statements", async () => {
|
|
120
|
+
await withProject({
|
|
121
|
+
"esm.mjs": 'import { describe } from "vitest";\ndescribe("x");\n',
|
|
122
|
+
"other.mjs": 'describe("nope");\n',
|
|
123
|
+
}, async (dir) => {
|
|
124
|
+
const { hits } = await scanOnce(dir, [probe("describe", "k0", `${REQUIRES_IMPORT_PREFIX}vitest`)], { perProbeCap: 200 });
|
|
125
|
+
assert.ok(hits.every((h) => h.file === "esm.mjs"));
|
|
126
|
+
assert.equal(hits.length, 2);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
test("scanOnce: an invalid regex falls back to a literal match instead of throwing", async () => {
|
|
130
|
+
await withProject({ "a.js": "a[b\n" }, async (dir) => {
|
|
131
|
+
// Unbalanced bracket is not a valid regex — must not throw.
|
|
132
|
+
const { hits } = await scanOnce(dir, [probe("a[b")], { perProbeCap: 200 });
|
|
133
|
+
assert.equal(hits.length, 1);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
test("scanOnce: no probes means no hits (and no crash)", async () => {
|
|
137
|
+
await withProject({ "a.js": "anything\n" }, async (dir) => {
|
|
138
|
+
const { hits, truncatedProbes } = await scanOnce(dir, [], { perProbeCap: 200 });
|
|
139
|
+
assert.deepEqual(hits, []);
|
|
140
|
+
assert.equal(truncatedProbes.size, 0);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
test("scanOnce: empty directory yields no hits", async () => {
|
|
144
|
+
await withProject({}, async (dir) => {
|
|
145
|
+
const { hits } = await scanOnce(dir, [probe("oldApi")], { perProbeCap: 200 });
|
|
146
|
+
assert.deepEqual(hits, []);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=indexer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.test.js","sourceRoot":"","sources":["../../../src/engine/scan/indexer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { builtinCodemods, codemodMatchesRange, parseMajor } from "../fixer/patterns.js";
|
|
2
|
+
/**
|
|
3
|
+
* Prefix marking a probe whose matches are only valid inside files that
|
|
4
|
+
* import a given package. Carried in `Probe.fileFilter` so the indexer can
|
|
5
|
+
* resolve it per file without a second pass.
|
|
6
|
+
*/
|
|
7
|
+
export const REQUIRES_IMPORT_PREFIX = "__requiresImport:";
|
|
8
|
+
/**
|
|
9
|
+
* Generate search probes from a breaking change.
|
|
10
|
+
*
|
|
11
|
+
* Strategy:
|
|
12
|
+
* 1. Primary probe = oldApi.split("(")[0].trim(), if length >= 4
|
|
13
|
+
* 2. If primary is a bare identifier (no `.`, `(`, quotes), mark for import-scoped search
|
|
14
|
+
* 3. If primary too short, extract longest `.identifier` member and mark approx
|
|
15
|
+
*/
|
|
16
|
+
export function probesFromChange(change, depName, changeIndex) {
|
|
17
|
+
const probes = [];
|
|
18
|
+
const oldApi = change.oldApi;
|
|
19
|
+
// Primary probe: everything before the first `(`
|
|
20
|
+
const primary = oldApi.split("(")[0].trim();
|
|
21
|
+
if (primary.length >= 4) {
|
|
22
|
+
// Check if it's a bare identifier (no dots, parens, quotes, spaces)
|
|
23
|
+
const isBareIdentifier = /^[a-zA-Z_$][\w$]*$/.test(primary);
|
|
24
|
+
probes.push({
|
|
25
|
+
key: `${depName}:${changeIndex}:primary`,
|
|
26
|
+
pattern: escapeRegExpLiteral(primary),
|
|
27
|
+
approx: false,
|
|
28
|
+
refs: [{ depName, changeIndex }],
|
|
29
|
+
// Bare identifiers like `describe` are too noisy — only search in files
|
|
30
|
+
// that import the package (resolved per file by the indexer).
|
|
31
|
+
...(isBareIdentifier ? { fileFilter: `${REQUIRES_IMPORT_PREFIX}${depName}` } : {}),
|
|
32
|
+
});
|
|
33
|
+
return probes;
|
|
34
|
+
}
|
|
35
|
+
// Fallback: extract the longest `.identifier` member
|
|
36
|
+
const members = oldApi.match(/\.[a-zA-Z_$][\w$]*/g) || [];
|
|
37
|
+
if (members.length > 0) {
|
|
38
|
+
const longest = members.reduce((a, b) => (a.length > b.length ? a : b));
|
|
39
|
+
probes.push({
|
|
40
|
+
key: `${depName}:${changeIndex}:member`,
|
|
41
|
+
pattern: escapeRegExpLiteral(longest),
|
|
42
|
+
approx: true,
|
|
43
|
+
refs: [{ depName, changeIndex }],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return probes;
|
|
47
|
+
}
|
|
48
|
+
/** Generate probes from a codemod (uses its transform.find pattern). */
|
|
49
|
+
export function probesFromCodemod(codemod, depName, changeIndex) {
|
|
50
|
+
if (!codemod.transform)
|
|
51
|
+
return [];
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
key: `${depName}:${changeIndex}:codemod`,
|
|
55
|
+
pattern: codemod.transform.find,
|
|
56
|
+
fileFilter: codemod.transform.fileFilter,
|
|
57
|
+
approx: false,
|
|
58
|
+
refs: [{ depName, changeIndex }],
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Packages whose upgrades can plausibly break the generic Node-runtime
|
|
64
|
+
* codemods (`package: "*"` in the registry, e.g. `util.isArray` removal).
|
|
65
|
+
* Those codemods are error-gated in the fix loop — they only fire when the
|
|
66
|
+
* failure output matches — but `--scan` has no error output, so without this
|
|
67
|
+
* gate they would be attributed to *every* outdated dependency (a zod bump
|
|
68
|
+
* reported as breaking `assert.equal`, which is simply wrong).
|
|
69
|
+
*/
|
|
70
|
+
const NODE_RUNTIME_PACKAGES = new Set(["node", "nodejs", "@types/node"]);
|
|
71
|
+
/** Find codemods that apply to a given package upgrade path. */
|
|
72
|
+
export function codemodsForUpgrade(packageName, fromVersion, toVersion) {
|
|
73
|
+
const fromMajor = parseMajor(fromVersion);
|
|
74
|
+
const toMajor = parseMajor(toVersion);
|
|
75
|
+
return builtinCodemods().filter((c) => {
|
|
76
|
+
if (c.package === "*") {
|
|
77
|
+
// Generic runtime codemods only apply to Node-runtime upgrades.
|
|
78
|
+
if (!NODE_RUNTIME_PACKAGES.has(packageName))
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
else if (c.package !== packageName) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
// Shared with the fix loop so the two can't drift.
|
|
85
|
+
return codemodMatchesRange(c.versionRange, fromMajor, toMajor);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** Escape a string for use as a literal in a RegExp. */
|
|
89
|
+
export function escapeRegExpLiteral(s) {
|
|
90
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=probes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probes.js","sourceRoot":"","sources":["../../../src/engine/scan/probes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { probesFromChange, probesFromCodemod, codemodsForUpgrade, escapeRegExpLiteral, REQUIRES_IMPORT_PREFIX, } from "./probes.js";
|
|
4
|
+
function change(partial) {
|
|
5
|
+
return {
|
|
6
|
+
description: "some change",
|
|
7
|
+
oldApi: "ReactDOM.render",
|
|
8
|
+
newApi: "createRoot",
|
|
9
|
+
severity: "high",
|
|
10
|
+
...partial,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
test("probesFromChange: dotted API call produces a literal pattern, not a regex", () => {
|
|
14
|
+
const probes = probesFromChange(change({ oldApi: "ReactDOM.render(el, root)" }), "react-dom", 0);
|
|
15
|
+
assert.equal(probes.length, 1);
|
|
16
|
+
assert.equal(probes[0].pattern, escapeRegExpLiteral("ReactDOM.render"));
|
|
17
|
+
assert.equal(probes[0].approx, false);
|
|
18
|
+
assert.deepEqual(probes[0].refs, [{ depName: "react-dom", changeIndex: 0 }]);
|
|
19
|
+
});
|
|
20
|
+
test("probesFromChange: dots and parens in the API are escaped so '.' matches literally", () => {
|
|
21
|
+
const probes = probesFromChange(change({ oldApi: "err.error.errors" }), "zod", 0);
|
|
22
|
+
assert.equal(probes[0].pattern, "err\\.error\\.errors");
|
|
23
|
+
// A literal match must not treat '.' as any-char
|
|
24
|
+
assert.ok(new RegExp(probes[0].pattern).test("err.error.errors"));
|
|
25
|
+
assert.ok(!new RegExp(probes[0].pattern).test("errXerrorXerrors"));
|
|
26
|
+
});
|
|
27
|
+
test("probesFromChange: bare identifier is gated to files importing the package", () => {
|
|
28
|
+
const probes = probesFromChange(change({ oldApi: "describe" }), "vitest", 2);
|
|
29
|
+
assert.equal(probes.length, 1);
|
|
30
|
+
assert.equal(probes[0].fileFilter, `${REQUIRES_IMPORT_PREFIX}vitest`);
|
|
31
|
+
assert.equal(probes[0].approx, false);
|
|
32
|
+
});
|
|
33
|
+
test("probesFromChange: short head before the paren falls back to longest member, marked approx", () => {
|
|
34
|
+
// Primary = "fn" (2 chars, below the length floor) → fall back to the
|
|
35
|
+
// longest `.member` path found anywhere in the full oldApi string.
|
|
36
|
+
const probes = probesFromChange(change({ oldApi: "fn(obj.someLongMemberName)" }), "pkg", 0);
|
|
37
|
+
assert.equal(probes.length, 1);
|
|
38
|
+
assert.equal(probes[0].approx, true);
|
|
39
|
+
assert.equal(probes[0].pattern, escapeRegExpLiteral(".someLongMemberName"));
|
|
40
|
+
});
|
|
41
|
+
test("probesFromChange: a usable head is preferred over the member fallback", () => {
|
|
42
|
+
const probes = probesFromChange(change({ oldApi: "client.oldMethod()" }), "pkg", 0);
|
|
43
|
+
assert.equal(probes[0].approx, false);
|
|
44
|
+
assert.equal(probes[0].pattern, escapeRegExpLiteral("client.oldMethod"));
|
|
45
|
+
});
|
|
46
|
+
test("probesFromChange: prose with no dots and no usable head yields no probes", () => {
|
|
47
|
+
const probes = probesFromChange(change({ oldApi: "" }), "pkg", 0);
|
|
48
|
+
assert.deepEqual(probes, []);
|
|
49
|
+
});
|
|
50
|
+
test("probesFromCodemod: uses the transform pattern and carries its fileFilter", () => {
|
|
51
|
+
const probes = probesFromCodemod({
|
|
52
|
+
package: "lodash",
|
|
53
|
+
errorMatch: "_",
|
|
54
|
+
description: "_.pluck → _.map",
|
|
55
|
+
transform: { find: "_\\.pluck\\(", replace: "_.map(", fileFilter: "src" },
|
|
56
|
+
}, "lodash", 0);
|
|
57
|
+
assert.equal(probes.length, 1);
|
|
58
|
+
assert.equal(probes[0].pattern, "_\\.pluck\\(");
|
|
59
|
+
assert.equal(probes[0].fileFilter, "src");
|
|
60
|
+
});
|
|
61
|
+
test("probesFromCodemod: guidance-only codemod (no transform) yields no probes", () => {
|
|
62
|
+
const probes = probesFromCodemod({ package: "eslint", errorMatch: "eslintrc", description: "flat config migration" }, "eslint", 0);
|
|
63
|
+
assert.deepEqual(probes, []);
|
|
64
|
+
});
|
|
65
|
+
test("codemodsForUpgrade: matches by package and major range (zod 3→4)", () => {
|
|
66
|
+
const found = codemodsForUpgrade("zod", "3.23.8", "4.0.5");
|
|
67
|
+
assert.ok(found.length > 0);
|
|
68
|
+
assert.ok(found.every((c) => c.package === "zod"));
|
|
69
|
+
});
|
|
70
|
+
test("codemodsForUpgrade: does not match a minor bump inside the same major", () => {
|
|
71
|
+
const found = codemodsForUpgrade("zod", "3.22.0", "3.23.8");
|
|
72
|
+
assert.equal(found.length, 0);
|
|
73
|
+
});
|
|
74
|
+
test("codemodsForUpgrade: a later major than the range is excluded (react-dom 18→20)", () => {
|
|
75
|
+
const found = codemodsForUpgrade("react-dom", "18.3.1", "20.0.0");
|
|
76
|
+
assert.equal(found.length, 0);
|
|
77
|
+
});
|
|
78
|
+
test("codemodsForUpgrade: multi-major range still matches (mongoose 6→8)", () => {
|
|
79
|
+
const found = codemodsForUpgrade("mongoose", "6.5.0", "8.0.0");
|
|
80
|
+
assert.ok(found.some((c) => c.description.includes("deleteOne")));
|
|
81
|
+
});
|
|
82
|
+
test("codemodsForUpgrade: wildcard codemods are NOT attributed to an unrelated package", () => {
|
|
83
|
+
// The generic `assert.equal` / `util.is*` codemods are `package: "*"` and
|
|
84
|
+
// error-gated. Before this gate they were reported as breaking changes of
|
|
85
|
+
// every outdated dependency (a zod bump "breaking" assert.equal).
|
|
86
|
+
const zodCodemods = codemodsForUpgrade("zod", "3.23.8", "4.0.5");
|
|
87
|
+
assert.ok(!zodCodemods.some((c) => c.package === "*"), "wildcard codemod leaked into zod");
|
|
88
|
+
const expressCodemods = codemodsForUpgrade("express", "4.19.2", "5.0.1");
|
|
89
|
+
assert.ok(!expressCodemods.some((c) => c.package === "*"), "wildcard codemod leaked into express");
|
|
90
|
+
});
|
|
91
|
+
test("codemodsForUpgrade: wildcard codemods DO apply to a Node runtime upgrade", () => {
|
|
92
|
+
const found = codemodsForUpgrade("@types/node", "20.0.0", "22.0.0");
|
|
93
|
+
assert.ok(found.some((c) => c.package === "*"));
|
|
94
|
+
});
|
|
95
|
+
test("escapeRegExpLiteral: neutralizes regex metacharacters", () => {
|
|
96
|
+
const escaped = escapeRegExpLiteral("a.b(c)[d]*");
|
|
97
|
+
assert.ok(new RegExp(escaped).test("a.b(c)[d]*"));
|
|
98
|
+
assert.ok(!new RegExp(escaped).test("axbxcxd"));
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=probes.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probes.test.js","sourceRoot":"","sources":["../../../src/engine/scan/probes.test.ts"],"names":[],"mappings":"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"}
|