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.
Files changed (121) hide show
  1. package/README.md +73 -15
  2. package/README.zh-CN.md +37 -4
  3. package/dist/action/github.test.js +37 -0
  4. package/dist/action/github.test.js.map +1 -0
  5. package/dist/action/main.js +19 -4
  6. package/dist/action/main.js.map +1 -1
  7. package/dist/agent/calllog.js +23 -0
  8. package/dist/agent/calllog.js.map +1 -0
  9. package/dist/agent/calllog.test.js +77 -0
  10. package/dist/agent/calllog.test.js.map +1 -0
  11. package/dist/agent/fixer.js +246 -29
  12. package/dist/agent/fixer.js.map +1 -1
  13. package/dist/agent/fixer.test.js +299 -0
  14. package/dist/agent/fixer.test.js.map +1 -1
  15. package/dist/agent/guard.js +37 -0
  16. package/dist/agent/guard.js.map +1 -0
  17. package/dist/agent/guard.test.js +48 -0
  18. package/dist/agent/guard.test.js.map +1 -0
  19. package/dist/agent/provider-anthropic.js +2 -2
  20. package/dist/agent/provider-anthropic.js.map +1 -1
  21. package/dist/agent/provider-openai.js +2 -2
  22. package/dist/agent/provider-openai.js.map +1 -1
  23. package/dist/benchmarks/run.js +0 -0
  24. package/dist/benchmarks/run.js.map +1 -0
  25. package/dist/cli/interactive.js +46 -11
  26. package/dist/cli/interactive.js.map +1 -1
  27. package/dist/cli.js +37 -7
  28. package/dist/cli.js.map +1 -1
  29. package/dist/engine/analyzers/changelog-parser.js +51 -2
  30. package/dist/engine/analyzers/changelog-parser.js.map +1 -1
  31. package/dist/engine/batch.js +54 -10
  32. package/dist/engine/batch.js.map +1 -1
  33. package/dist/engine/cache/manager.js +19 -1
  34. package/dist/engine/cache/manager.js.map +1 -1
  35. package/dist/engine/cache/manager.test.js +17 -0
  36. package/dist/engine/cache/manager.test.js.map +1 -1
  37. package/dist/engine/change-detector.js +52 -23
  38. package/dist/engine/change-detector.js.map +1 -1
  39. package/dist/engine/change-detector.test.js +72 -0
  40. package/dist/engine/change-detector.test.js.map +1 -0
  41. package/dist/engine/changelog.js +22 -2
  42. package/dist/engine/changelog.js.map +1 -1
  43. package/dist/engine/changelog.test.js +31 -0
  44. package/dist/engine/changelog.test.js.map +1 -0
  45. package/dist/engine/checks.js +10 -4
  46. package/dist/engine/checks.js.map +1 -1
  47. package/dist/engine/context/optimizer.js +14 -44
  48. package/dist/engine/context/optimizer.js.map +1 -1
  49. package/dist/engine/context/optimizer.test.js +23 -1
  50. package/dist/engine/context/optimizer.test.js.map +1 -1
  51. package/dist/engine/ecosystems/cargo.js +2 -1
  52. package/dist/engine/ecosystems/cargo.js.map +1 -1
  53. package/dist/engine/ecosystems/conan.js +2 -1
  54. package/dist/engine/ecosystems/conan.js.map +1 -1
  55. package/dist/engine/ecosystems/elm.js +2 -1
  56. package/dist/engine/ecosystems/elm.js.map +1 -1
  57. package/dist/engine/ecosystems/gradle.js +2 -1
  58. package/dist/engine/ecosystems/gradle.js.map +1 -1
  59. package/dist/engine/ecosystems/http.js +18 -0
  60. package/dist/engine/ecosystems/http.js.map +1 -0
  61. package/dist/engine/ecosystems/maven.js +2 -1
  62. package/dist/engine/ecosystems/maven.js.map +1 -1
  63. package/dist/engine/ecosystems/swiftpm.js +2 -1
  64. package/dist/engine/ecosystems/swiftpm.js.map +1 -1
  65. package/dist/engine/exec.js +37 -7
  66. package/dist/engine/exec.js.map +1 -1
  67. package/dist/engine/exec.test.js +36 -0
  68. package/dist/engine/exec.test.js.map +1 -0
  69. package/dist/engine/fixer/patterns.js +55 -18
  70. package/dist/engine/fixer/patterns.js.map +1 -1
  71. package/dist/engine/fixer/patterns.test.js +95 -1
  72. package/dist/engine/fixer/patterns.test.js.map +1 -1
  73. package/dist/engine/git.js +38 -16
  74. package/dist/engine/git.js.map +1 -1
  75. package/dist/engine/git.test.js +51 -1
  76. package/dist/engine/git.test.js.map +1 -1
  77. package/dist/engine/perf/metrics.js +44 -57
  78. package/dist/engine/perf/metrics.js.map +1 -1
  79. package/dist/engine/run.js +141 -15
  80. package/dist/engine/run.js.map +1 -1
  81. package/dist/engine/run.test.js +65 -1
  82. package/dist/engine/run.test.js.map +1 -1
  83. package/dist/engine/sandbox/docker-manager.js +52 -26
  84. package/dist/engine/sandbox/docker-manager.js.map +1 -1
  85. package/dist/engine/sandbox/template-generator.js +0 -4
  86. package/dist/engine/sandbox/template-generator.js.map +1 -1
  87. package/dist/engine/sandbox/template-generator.test.js +16 -0
  88. package/dist/engine/sandbox/template-generator.test.js.map +1 -0
  89. package/dist/engine/scan/benchmark-crosscheck.test.js +116 -0
  90. package/dist/engine/scan/benchmark-crosscheck.test.js.map +1 -0
  91. package/dist/engine/scan/detect.js +96 -0
  92. package/dist/engine/scan/detect.js.map +1 -0
  93. package/dist/engine/scan/impact.js +128 -0
  94. package/dist/engine/scan/impact.js.map +1 -0
  95. package/dist/engine/scan/impact.test.js +217 -0
  96. package/dist/engine/scan/impact.test.js.map +1 -0
  97. package/dist/engine/scan/indexer.js +129 -0
  98. package/dist/engine/scan/indexer.js.map +1 -0
  99. package/dist/engine/scan/indexer.test.js +149 -0
  100. package/dist/engine/scan/indexer.test.js.map +1 -0
  101. package/dist/engine/scan/probes.js +92 -0
  102. package/dist/engine/scan/probes.js.map +1 -0
  103. package/dist/engine/scan/probes.test.js +100 -0
  104. package/dist/engine/scan/probes.test.js.map +1 -0
  105. package/dist/engine/verify.js +41 -58
  106. package/dist/engine/verify.js.map +1 -1
  107. package/dist/engine/verify.test.js +49 -0
  108. package/dist/engine/verify.test.js.map +1 -0
  109. package/dist/format.js +96 -0
  110. package/dist/format.js.map +1 -1
  111. package/dist/format.test.js +53 -0
  112. package/dist/format.test.js.map +1 -0
  113. package/dist/pr.test.js +48 -0
  114. package/dist/pr.test.js.map +1 -0
  115. package/package.json +4 -4
  116. package/dist/engine/dep-graph.js +0 -101
  117. package/dist/engine/dep-graph.js.map +0 -1
  118. package/dist/engine/staged-fix.js +0 -108
  119. package/dist/engine/staged-fix.js.map +0 -1
  120. package/dist/engine/stages.js +0 -125
  121. 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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"}