lockdelta 0.1.0 → 0.1.1
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/action.yml +1 -1
- package/dist/action.js +8575 -458
- package/dist/action.js.map +1 -1
- package/dist/cli.js +327 -321
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +320 -315
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -4,152 +4,148 @@ import { readFileSync } from "fs";
|
|
|
4
4
|
// src/core/report.ts
|
|
5
5
|
import { posix as posix2 } from "path";
|
|
6
6
|
|
|
7
|
-
// src/ecosystems/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
try {
|
|
11
|
-
const data = parse(content);
|
|
12
|
-
const packages = {};
|
|
13
|
-
for (const pkg of [...data.package ?? [], ...data.packages ?? []]) {
|
|
14
|
-
if (typeof pkg.name === "string" && typeof pkg.version === "string") {
|
|
15
|
-
packages[pkg.name] = pkg.version;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return packages;
|
|
19
|
-
} catch {
|
|
20
|
-
return parseTomlPackagesRegex(content);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function parseTomlPackagesRegex(content) {
|
|
24
|
-
const packages = {};
|
|
25
|
-
const blocks = content.split(/\[\[packages?\]\]/);
|
|
26
|
-
for (const block of blocks) {
|
|
27
|
-
const nameMatch = block.match(/\nname\s*=\s*"([^"]+)"/);
|
|
28
|
-
const versionMatch = block.match(/\nversion\s*=\s*"([^"]+)"/);
|
|
29
|
-
if (nameMatch && versionMatch) {
|
|
30
|
-
packages[nameMatch[1]] = versionMatch[1];
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return packages;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/ecosystems/python/pyproject.ts
|
|
37
|
-
import { parse as parse2 } from "smol-toml";
|
|
38
|
-
function normalizePythonName(name) {
|
|
39
|
-
return name.toLowerCase().replace(/[-_.]+/g, "_");
|
|
40
|
-
}
|
|
41
|
-
function extractPkgName(dep) {
|
|
42
|
-
const match = String(dep).match(/^([\w][\w.-]*)/);
|
|
43
|
-
return match ? normalizePythonName(match[1]) : null;
|
|
7
|
+
// src/ecosystems/deno/deno-json.ts
|
|
8
|
+
function normalizeDenoName(name) {
|
|
9
|
+
return name.toLowerCase();
|
|
44
10
|
}
|
|
45
11
|
function parseDirectDeps(content) {
|
|
46
12
|
const prod = /* @__PURE__ */ new Set();
|
|
47
|
-
const dev = /* @__PURE__ */ new Set();
|
|
48
13
|
let data;
|
|
49
14
|
try {
|
|
50
|
-
data =
|
|
15
|
+
data = JSON.parse(content);
|
|
51
16
|
} catch {
|
|
52
|
-
return { prod, dev };
|
|
53
|
-
}
|
|
54
|
-
const project = data["project"];
|
|
55
|
-
const pep517Deps = project?.["dependencies"];
|
|
56
|
-
if (Array.isArray(pep517Deps)) {
|
|
57
|
-
for (const dep of pep517Deps) {
|
|
58
|
-
const name = extractPkgName(dep);
|
|
59
|
-
if (name) prod.add(name);
|
|
60
|
-
}
|
|
17
|
+
return { prod, dev: /* @__PURE__ */ new Set() };
|
|
61
18
|
}
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
for (const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const name = extractPkgName(dep);
|
|
68
|
-
if (name && !prod.has(name)) dev.add(name);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
19
|
+
const imports = data.imports;
|
|
20
|
+
if (imports) {
|
|
21
|
+
for (const specifier of Object.values(imports)) {
|
|
22
|
+
const name = extractPackageName(specifier);
|
|
23
|
+
if (name) prod.add(normalizeDenoName(name));
|
|
71
24
|
}
|
|
72
25
|
}
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (poetryDeps) {
|
|
78
|
-
for (const key of Object.keys(poetryDeps)) {
|
|
79
|
-
if (key.toLowerCase() !== "python") prod.add(normalizePythonName(key));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const devDeps = poetry["dev-dependencies"];
|
|
83
|
-
if (devDeps) {
|
|
84
|
-
for (const key of Object.keys(devDeps)) {
|
|
85
|
-
const normalized = normalizePythonName(key);
|
|
86
|
-
if (!prod.has(normalized)) dev.add(normalized);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
const groups = poetry["group"];
|
|
90
|
-
if (groups) {
|
|
91
|
-
for (const group of Object.values(groups)) {
|
|
92
|
-
const groupDeps = group["dependencies"];
|
|
93
|
-
if (groupDeps) {
|
|
94
|
-
for (const key of Object.keys(groupDeps)) {
|
|
95
|
-
const normalized = normalizePythonName(key);
|
|
96
|
-
if (!prod.has(normalized)) dev.add(normalized);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
26
|
+
const workspace = data.workspace;
|
|
27
|
+
for (const specifier of workspace?.dependencies ?? []) {
|
|
28
|
+
const name = extractPackageName(specifier);
|
|
29
|
+
if (name) prod.add(normalizeDenoName(name));
|
|
101
30
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
31
|
+
return { prod, dev: /* @__PURE__ */ new Set() };
|
|
32
|
+
}
|
|
33
|
+
function extractPackageName(specifier) {
|
|
34
|
+
const withoutProtocol = specifier.replace(/^(?:npm|jsr|node):/, "");
|
|
35
|
+
if (specifier.startsWith("node:")) return null;
|
|
36
|
+
if (withoutProtocol.startsWith("@")) {
|
|
37
|
+
const atIdx2 = withoutProtocol.indexOf("@", 1);
|
|
38
|
+
return atIdx2 > 0 ? withoutProtocol.slice(0, atIdx2) : withoutProtocol;
|
|
109
39
|
}
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
40
|
+
const atIdx = withoutProtocol.indexOf("@");
|
|
41
|
+
return atIdx > 0 ? withoutProtocol.slice(0, atIdx) : withoutProtocol || null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/ecosystems/deno/parsers/deno-lock.ts
|
|
45
|
+
function parseDenoLock(content) {
|
|
46
|
+
const data = JSON.parse(content);
|
|
47
|
+
const result = {};
|
|
48
|
+
for (const [key, registry2] of [
|
|
49
|
+
["npm", data.packages?.npm],
|
|
50
|
+
["jsr", data.packages?.jsr]
|
|
51
|
+
]) {
|
|
52
|
+
if (!registry2) continue;
|
|
53
|
+
for (const specifier of Object.keys(registry2)) {
|
|
54
|
+
const { name, version } = splitSpecifier(specifier);
|
|
55
|
+
const resultKey = key === "jsr" ? `jsr:${name}` : name;
|
|
56
|
+
if (name && version && !result[resultKey]) {
|
|
57
|
+
result[resultKey] = version;
|
|
120
58
|
}
|
|
121
59
|
}
|
|
122
60
|
}
|
|
123
|
-
return
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
function splitSpecifier(specifier) {
|
|
64
|
+
if (specifier.startsWith("@")) {
|
|
65
|
+
const atIdx2 = specifier.indexOf("@", 1);
|
|
66
|
+
if (atIdx2 < 0) return { name: specifier, version: "" };
|
|
67
|
+
return { name: specifier.slice(0, atIdx2), version: specifier.slice(atIdx2 + 1) };
|
|
68
|
+
}
|
|
69
|
+
const atIdx = specifier.indexOf("@");
|
|
70
|
+
if (atIdx < 0) return { name: specifier, version: "" };
|
|
71
|
+
return { name: specifier.slice(0, atIdx), version: specifier.slice(atIdx + 1) };
|
|
124
72
|
}
|
|
125
73
|
|
|
126
|
-
// src/ecosystems/
|
|
127
|
-
var SUPPORTED_LOCKFILES = [
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
{ filename: "pdm.lock", type: "pdm" },
|
|
131
|
-
{ filename: "pylock.toml", type: "pylock" }
|
|
132
|
-
// PEP 751
|
|
133
|
-
];
|
|
134
|
-
var lockfileTypeMap = new Map(SUPPORTED_LOCKFILES.map((l) => [l.filename, l.type]));
|
|
135
|
-
var pythonEcosystem = {
|
|
136
|
-
name: "python",
|
|
74
|
+
// src/ecosystems/deno/index.ts
|
|
75
|
+
var SUPPORTED_LOCKFILES = [{ filename: "deno.lock", type: "deno" }];
|
|
76
|
+
var denoEcosystem = {
|
|
77
|
+
name: "deno",
|
|
137
78
|
supportedLockfiles: SUPPORTED_LOCKFILES,
|
|
138
|
-
manifestName: "
|
|
79
|
+
manifestName: "deno.json",
|
|
139
80
|
getLockfileType(filename) {
|
|
140
|
-
return
|
|
81
|
+
return filename === "deno.lock" ? "deno" : void 0;
|
|
141
82
|
},
|
|
142
83
|
parseLockfile(content, _lockfileType) {
|
|
143
|
-
return
|
|
84
|
+
return parseDenoLock(content);
|
|
144
85
|
},
|
|
145
86
|
parseDirectDeps(manifestContent) {
|
|
146
87
|
return parseDirectDeps(manifestContent);
|
|
147
88
|
},
|
|
148
89
|
normalizeName(name) {
|
|
149
|
-
return
|
|
90
|
+
return normalizeDenoName(name);
|
|
150
91
|
}
|
|
151
92
|
};
|
|
152
93
|
|
|
94
|
+
// src/ecosystems/javascript/package-json.ts
|
|
95
|
+
var PROD_SECTIONS = ["dependencies", "optionalDependencies", "peerDependencies"];
|
|
96
|
+
function normalizeJsName(name) {
|
|
97
|
+
return name.toLowerCase();
|
|
98
|
+
}
|
|
99
|
+
function parseDirectDeps2(content) {
|
|
100
|
+
const prod = /* @__PURE__ */ new Set();
|
|
101
|
+
const dev = /* @__PURE__ */ new Set();
|
|
102
|
+
let data;
|
|
103
|
+
try {
|
|
104
|
+
data = JSON.parse(content);
|
|
105
|
+
} catch {
|
|
106
|
+
return { prod, dev };
|
|
107
|
+
}
|
|
108
|
+
for (const section of PROD_SECTIONS) {
|
|
109
|
+
const deps = data[section];
|
|
110
|
+
if (deps && typeof deps === "object") {
|
|
111
|
+
for (const name of Object.keys(deps)) {
|
|
112
|
+
prod.add(normalizeJsName(name));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const devDeps = data.devDependencies;
|
|
117
|
+
if (devDeps && typeof devDeps === "object") {
|
|
118
|
+
for (const name of Object.keys(devDeps)) {
|
|
119
|
+
const normalized = normalizeJsName(name);
|
|
120
|
+
if (!prod.has(normalized)) dev.add(normalized);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { prod, dev };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/ecosystems/javascript/parsers/bun.ts
|
|
127
|
+
function parseBunLock(content) {
|
|
128
|
+
const data = JSON.parse(content);
|
|
129
|
+
const result = {};
|
|
130
|
+
for (const [name, entry] of Object.entries(data.packages ?? {})) {
|
|
131
|
+
if (!Array.isArray(entry)) continue;
|
|
132
|
+
const nameAtVersion = entry[0];
|
|
133
|
+
if (typeof nameAtVersion !== "string") continue;
|
|
134
|
+
const version = extractVersion(nameAtVersion);
|
|
135
|
+
if (!version || version.startsWith("workspace:")) continue;
|
|
136
|
+
result[name] = version;
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
function extractVersion(nameAtVersion) {
|
|
141
|
+
if (nameAtVersion.startsWith("@")) {
|
|
142
|
+
const atIdx2 = nameAtVersion.indexOf("@", 1);
|
|
143
|
+
return atIdx2 > 0 ? nameAtVersion.slice(atIdx2 + 1) : "";
|
|
144
|
+
}
|
|
145
|
+
const atIdx = nameAtVersion.indexOf("@");
|
|
146
|
+
return atIdx > 0 ? nameAtVersion.slice(atIdx + 1) : "";
|
|
147
|
+
}
|
|
148
|
+
|
|
153
149
|
// src/ecosystems/javascript/parsers/npm.ts
|
|
154
150
|
function parseNpmLock(content) {
|
|
155
151
|
const data = JSON.parse(content);
|
|
@@ -189,69 +185,10 @@ function parseV1Dependencies(deps, result = {}) {
|
|
|
189
185
|
return result;
|
|
190
186
|
}
|
|
191
187
|
|
|
192
|
-
// src/ecosystems/javascript/parsers/yarn.ts
|
|
193
|
-
import { parse as parseYaml } from "yaml";
|
|
194
|
-
function parseYarnLock(content) {
|
|
195
|
-
return isYarnBerry(content) ? parseYarnBerry(content) : parseYarnV1(content);
|
|
196
|
-
}
|
|
197
|
-
function isYarnBerry(content) {
|
|
198
|
-
return content.includes("__metadata:");
|
|
199
|
-
}
|
|
200
|
-
function extractNameFromSpecifier(spec) {
|
|
201
|
-
const trimmed = spec.trim().replace(/^"|"$/g, "");
|
|
202
|
-
if (trimmed.startsWith("@")) {
|
|
203
|
-
const idx = trimmed.indexOf("@", 1);
|
|
204
|
-
return idx > 0 ? trimmed.slice(0, idx) : trimmed;
|
|
205
|
-
}
|
|
206
|
-
const atIdx = trimmed.indexOf("@");
|
|
207
|
-
return atIdx > 0 ? trimmed.slice(0, atIdx) : trimmed;
|
|
208
|
-
}
|
|
209
|
-
function parseYarnV1(content) {
|
|
210
|
-
const packages = {};
|
|
211
|
-
const blocks = content.split(/\n\n+/);
|
|
212
|
-
for (const block of blocks) {
|
|
213
|
-
const trimmed = block.trim();
|
|
214
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
215
|
-
const versionMatch = trimmed.match(/^[ \t]+version "([^"]+)"/m);
|
|
216
|
-
if (!versionMatch) continue;
|
|
217
|
-
const headerLine = trimmed.split("\n")[0].trim().replace(/:$/, "");
|
|
218
|
-
const firstSpecifier = headerLine.split(",")[0].trim().replace(/^"|"$/g, "");
|
|
219
|
-
const name = extractNameFromSpecifier(firstSpecifier);
|
|
220
|
-
if (name && !packages[name]) {
|
|
221
|
-
packages[name] = versionMatch[1];
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return packages;
|
|
225
|
-
}
|
|
226
|
-
function parseYarnBerry(content) {
|
|
227
|
-
const data = parseYaml(content);
|
|
228
|
-
const packages = {};
|
|
229
|
-
for (const [key, value] of Object.entries(data)) {
|
|
230
|
-
if (key === "__metadata") continue;
|
|
231
|
-
if (typeof value !== "object" || !value) continue;
|
|
232
|
-
const entry = value;
|
|
233
|
-
if (entry.linkType === "soft") continue;
|
|
234
|
-
if (!entry.version) continue;
|
|
235
|
-
const cleanKey = key.replace(/^"|"$/g, "");
|
|
236
|
-
const name = extractNameFromBerryKey(cleanKey);
|
|
237
|
-
if (name && !packages[name]) {
|
|
238
|
-
packages[name] = entry.version;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return packages;
|
|
242
|
-
}
|
|
243
|
-
function extractNameFromBerryKey(key) {
|
|
244
|
-
if (key.startsWith("@")) {
|
|
245
|
-
const idx = key.indexOf("@", 1);
|
|
246
|
-
return idx > 0 ? key.slice(0, idx) : key;
|
|
247
|
-
}
|
|
248
|
-
return key.split("@")[0];
|
|
249
|
-
}
|
|
250
|
-
|
|
251
188
|
// src/ecosystems/javascript/parsers/pnpm.ts
|
|
252
|
-
import { parse as
|
|
189
|
+
import { parse as parseYaml } from "yaml";
|
|
253
190
|
function parsePnpmLock(content) {
|
|
254
|
-
const data =
|
|
191
|
+
const data = parseYaml(content);
|
|
255
192
|
if (!data?.packages) return {};
|
|
256
193
|
const lockfileVersion = parseLockfileVersion(data.lockfileVersion);
|
|
257
194
|
if (lockfileVersion >= 9) {
|
|
@@ -261,13 +198,14 @@ function parsePnpmLock(content) {
|
|
|
261
198
|
}
|
|
262
199
|
function parseLockfileVersion(v) {
|
|
263
200
|
if (typeof v === "number") return v;
|
|
264
|
-
if (typeof v === "string") return parseFloat(v);
|
|
201
|
+
if (typeof v === "string") return Number.parseFloat(v);
|
|
265
202
|
return 0;
|
|
266
203
|
}
|
|
267
204
|
function parsePnpmV9(packages) {
|
|
268
205
|
const result = {};
|
|
269
206
|
for (const key of Object.keys(packages)) {
|
|
270
|
-
let name
|
|
207
|
+
let name;
|
|
208
|
+
let version;
|
|
271
209
|
if (key.startsWith("@")) {
|
|
272
210
|
const atIdx = key.indexOf("@", 1);
|
|
273
211
|
if (atIdx < 0) continue;
|
|
@@ -290,7 +228,8 @@ function parsePnpmLegacy(packages) {
|
|
|
290
228
|
const result = {};
|
|
291
229
|
for (const key of Object.keys(packages)) {
|
|
292
230
|
const cleaned = key.startsWith("/") ? key.slice(1) : key;
|
|
293
|
-
let name
|
|
231
|
+
let name;
|
|
232
|
+
let version;
|
|
294
233
|
if (cleaned.startsWith("@")) {
|
|
295
234
|
const secondSlash = cleaned.indexOf("/", cleaned.indexOf("/") + 1);
|
|
296
235
|
const secondAt = cleaned.indexOf("@", 1);
|
|
@@ -327,59 +266,63 @@ function stripVersionSuffix(version) {
|
|
|
327
266
|
return version.split("(")[0].split("_")[0].trim();
|
|
328
267
|
}
|
|
329
268
|
|
|
330
|
-
// src/ecosystems/javascript/parsers/
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
for (const [name, entry] of Object.entries(data.packages ?? {})) {
|
|
335
|
-
if (!Array.isArray(entry)) continue;
|
|
336
|
-
const nameAtVersion = entry[0];
|
|
337
|
-
if (typeof nameAtVersion !== "string") continue;
|
|
338
|
-
const version = extractVersion(nameAtVersion);
|
|
339
|
-
if (!version || version.startsWith("workspace:")) continue;
|
|
340
|
-
result[name] = version;
|
|
341
|
-
}
|
|
342
|
-
return result;
|
|
343
|
-
}
|
|
344
|
-
function extractVersion(nameAtVersion) {
|
|
345
|
-
if (nameAtVersion.startsWith("@")) {
|
|
346
|
-
const atIdx2 = nameAtVersion.indexOf("@", 1);
|
|
347
|
-
return atIdx2 > 0 ? nameAtVersion.slice(atIdx2 + 1) : "";
|
|
348
|
-
}
|
|
349
|
-
const atIdx = nameAtVersion.indexOf("@");
|
|
350
|
-
return atIdx > 0 ? nameAtVersion.slice(atIdx + 1) : "";
|
|
269
|
+
// src/ecosystems/javascript/parsers/yarn.ts
|
|
270
|
+
import { parse as parseYaml2 } from "yaml";
|
|
271
|
+
function parseYarnLock(content) {
|
|
272
|
+
return isYarnBerry(content) ? parseYarnBerry(content) : parseYarnV1(content);
|
|
351
273
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
var PROD_SECTIONS = ["dependencies", "optionalDependencies", "peerDependencies"];
|
|
355
|
-
function normalizeJsName(name) {
|
|
356
|
-
return name.toLowerCase();
|
|
274
|
+
function isYarnBerry(content) {
|
|
275
|
+
return content.includes("__metadata:");
|
|
357
276
|
}
|
|
358
|
-
function
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
277
|
+
function extractNameFromSpecifier(spec) {
|
|
278
|
+
const trimmed = spec.trim().replace(/^"|"$/g, "");
|
|
279
|
+
if (trimmed.startsWith("@")) {
|
|
280
|
+
const idx = trimmed.indexOf("@", 1);
|
|
281
|
+
return idx > 0 ? trimmed.slice(0, idx) : trimmed;
|
|
282
|
+
}
|
|
283
|
+
const atIdx = trimmed.indexOf("@");
|
|
284
|
+
return atIdx > 0 ? trimmed.slice(0, atIdx) : trimmed;
|
|
285
|
+
}
|
|
286
|
+
function parseYarnV1(content) {
|
|
287
|
+
const packages = {};
|
|
288
|
+
const blocks = content.split(/\n\n+/);
|
|
289
|
+
for (const block of blocks) {
|
|
290
|
+
const trimmed = block.trim();
|
|
291
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
292
|
+
const versionMatch = trimmed.match(/^[ \t]+version "([^"]+)"/m);
|
|
293
|
+
if (!versionMatch) continue;
|
|
294
|
+
const headerLine = trimmed.split("\n")[0].trim().replace(/:$/, "");
|
|
295
|
+
const firstSpecifier = headerLine.split(",")[0].trim().replace(/^"|"$/g, "");
|
|
296
|
+
const name = extractNameFromSpecifier(firstSpecifier);
|
|
297
|
+
if (name && !packages[name]) {
|
|
298
|
+
packages[name] = versionMatch[1];
|
|
373
299
|
}
|
|
374
300
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
301
|
+
return packages;
|
|
302
|
+
}
|
|
303
|
+
function parseYarnBerry(content) {
|
|
304
|
+
const data = parseYaml2(content);
|
|
305
|
+
const packages = {};
|
|
306
|
+
for (const [key, value] of Object.entries(data)) {
|
|
307
|
+
if (key === "__metadata") continue;
|
|
308
|
+
if (typeof value !== "object" || !value) continue;
|
|
309
|
+
const entry = value;
|
|
310
|
+
if (entry.linkType === "soft") continue;
|
|
311
|
+
if (!entry.version) continue;
|
|
312
|
+
const cleanKey = key.replace(/^"|"$/g, "");
|
|
313
|
+
const name = extractNameFromBerryKey(cleanKey);
|
|
314
|
+
if (name && !packages[name]) {
|
|
315
|
+
packages[name] = entry.version;
|
|
380
316
|
}
|
|
381
317
|
}
|
|
382
|
-
return
|
|
318
|
+
return packages;
|
|
319
|
+
}
|
|
320
|
+
function extractNameFromBerryKey(key) {
|
|
321
|
+
if (key.startsWith("@")) {
|
|
322
|
+
const idx = key.indexOf("@", 1);
|
|
323
|
+
return idx > 0 ? key.slice(0, idx) : key;
|
|
324
|
+
}
|
|
325
|
+
return key.split("@")[0];
|
|
383
326
|
}
|
|
384
327
|
|
|
385
328
|
// src/ecosystems/javascript/index.ts
|
|
@@ -389,13 +332,13 @@ var SUPPORTED_LOCKFILES2 = [
|
|
|
389
332
|
{ filename: "pnpm-lock.yaml", type: "pnpm" },
|
|
390
333
|
{ filename: "bun.lock", type: "bun" }
|
|
391
334
|
];
|
|
392
|
-
var
|
|
335
|
+
var lockfileTypeMap = new Map(SUPPORTED_LOCKFILES2.map((l) => [l.filename, l.type]));
|
|
393
336
|
var javascriptEcosystem = {
|
|
394
337
|
name: "javascript",
|
|
395
338
|
supportedLockfiles: SUPPORTED_LOCKFILES2,
|
|
396
339
|
manifestName: "package.json",
|
|
397
340
|
getLockfileType(filename) {
|
|
398
|
-
return
|
|
341
|
+
return lockfileTypeMap.get(filename);
|
|
399
342
|
},
|
|
400
343
|
parseLockfile(content, lockfileType) {
|
|
401
344
|
switch (lockfileType) {
|
|
@@ -419,90 +362,149 @@ var javascriptEcosystem = {
|
|
|
419
362
|
}
|
|
420
363
|
};
|
|
421
364
|
|
|
422
|
-
// src/ecosystems/
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
[
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
for (const specifier of Object.keys(registry2)) {
|
|
432
|
-
const { name, version } = splitSpecifier(specifier);
|
|
433
|
-
const resultKey = key === "jsr" ? `jsr:${name}` : name;
|
|
434
|
-
if (name && version && !result[resultKey]) {
|
|
435
|
-
result[resultKey] = version;
|
|
365
|
+
// src/ecosystems/python/parsers/toml.ts
|
|
366
|
+
import { parse } from "smol-toml";
|
|
367
|
+
function parseTomlPackages(content) {
|
|
368
|
+
try {
|
|
369
|
+
const data = parse(content);
|
|
370
|
+
const packages = {};
|
|
371
|
+
for (const pkg of [...data.package ?? [], ...data.packages ?? []]) {
|
|
372
|
+
if (typeof pkg.name === "string" && typeof pkg.version === "string") {
|
|
373
|
+
packages[pkg.name] = pkg.version;
|
|
436
374
|
}
|
|
437
375
|
}
|
|
376
|
+
return packages;
|
|
377
|
+
} catch {
|
|
378
|
+
return parseTomlPackagesRegex(content);
|
|
438
379
|
}
|
|
439
|
-
return result;
|
|
440
380
|
}
|
|
441
|
-
function
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
381
|
+
function parseTomlPackagesRegex(content) {
|
|
382
|
+
const packages = {};
|
|
383
|
+
const blocks = content.split(/\[\[packages?\]\]/);
|
|
384
|
+
for (const block of blocks) {
|
|
385
|
+
const nameMatch = block.match(/\nname\s*=\s*"([^"]+)"/);
|
|
386
|
+
const versionMatch = block.match(/\nversion\s*=\s*"([^"]+)"/);
|
|
387
|
+
if (nameMatch && versionMatch) {
|
|
388
|
+
packages[nameMatch[1]] = versionMatch[1];
|
|
389
|
+
}
|
|
446
390
|
}
|
|
447
|
-
|
|
448
|
-
if (atIdx < 0) return { name: specifier, version: "" };
|
|
449
|
-
return { name: specifier.slice(0, atIdx), version: specifier.slice(atIdx + 1) };
|
|
391
|
+
return packages;
|
|
450
392
|
}
|
|
451
393
|
|
|
452
|
-
// src/ecosystems/
|
|
453
|
-
|
|
454
|
-
|
|
394
|
+
// src/ecosystems/python/pyproject.ts
|
|
395
|
+
import { parse as parse2 } from "smol-toml";
|
|
396
|
+
function normalizePythonName(name) {
|
|
397
|
+
return name.toLowerCase().replace(/[-_.]+/g, "_");
|
|
398
|
+
}
|
|
399
|
+
function extractPkgName(dep) {
|
|
400
|
+
const match = String(dep).match(/^([\w][\w.-]*)/);
|
|
401
|
+
return match ? normalizePythonName(match[1]) : null;
|
|
455
402
|
}
|
|
456
403
|
function parseDirectDeps3(content) {
|
|
457
404
|
const prod = /* @__PURE__ */ new Set();
|
|
405
|
+
const dev = /* @__PURE__ */ new Set();
|
|
458
406
|
let data;
|
|
459
407
|
try {
|
|
460
|
-
data =
|
|
408
|
+
data = parse2(content);
|
|
461
409
|
} catch {
|
|
462
|
-
return { prod, dev
|
|
410
|
+
return { prod, dev };
|
|
463
411
|
}
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
412
|
+
const project = data.project;
|
|
413
|
+
const pep517Deps = project?.dependencies;
|
|
414
|
+
if (Array.isArray(pep517Deps)) {
|
|
415
|
+
for (const dep of pep517Deps) {
|
|
416
|
+
const name = extractPkgName(dep);
|
|
417
|
+
if (name) prod.add(name);
|
|
469
418
|
}
|
|
470
419
|
}
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
const
|
|
474
|
-
|
|
420
|
+
const optDeps = project?.["optional-dependencies"];
|
|
421
|
+
if (optDeps && typeof optDeps === "object") {
|
|
422
|
+
for (const group of Object.values(optDeps)) {
|
|
423
|
+
if (Array.isArray(group)) {
|
|
424
|
+
for (const dep of group) {
|
|
425
|
+
const name = extractPkgName(dep);
|
|
426
|
+
if (name && !prod.has(name)) dev.add(name);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
475
430
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
431
|
+
const tool = data.tool;
|
|
432
|
+
const poetry = tool?.poetry;
|
|
433
|
+
if (poetry) {
|
|
434
|
+
const poetryDeps = poetry.dependencies;
|
|
435
|
+
if (poetryDeps) {
|
|
436
|
+
for (const key of Object.keys(poetryDeps)) {
|
|
437
|
+
if (key.toLowerCase() !== "python") prod.add(normalizePythonName(key));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const devDeps = poetry["dev-dependencies"];
|
|
441
|
+
if (devDeps) {
|
|
442
|
+
for (const key of Object.keys(devDeps)) {
|
|
443
|
+
const normalized = normalizePythonName(key);
|
|
444
|
+
if (!prod.has(normalized)) dev.add(normalized);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const groups = poetry.group;
|
|
448
|
+
if (groups) {
|
|
449
|
+
for (const group of Object.values(groups)) {
|
|
450
|
+
const groupDeps = group.dependencies;
|
|
451
|
+
if (groupDeps) {
|
|
452
|
+
for (const key of Object.keys(groupDeps)) {
|
|
453
|
+
const normalized = normalizePythonName(key);
|
|
454
|
+
if (!prod.has(normalized)) dev.add(normalized);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
484
459
|
}
|
|
485
|
-
const
|
|
486
|
-
|
|
460
|
+
const uv = tool?.uv;
|
|
461
|
+
const uvDevDeps = uv?.["dev-dependencies"];
|
|
462
|
+
if (Array.isArray(uvDevDeps)) {
|
|
463
|
+
for (const dep of uvDevDeps) {
|
|
464
|
+
const name = extractPkgName(dep);
|
|
465
|
+
if (name && !prod.has(name)) dev.add(name);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const depGroups = data["dependency-groups"];
|
|
469
|
+
if (depGroups && typeof depGroups === "object") {
|
|
470
|
+
for (const group of Object.values(depGroups)) {
|
|
471
|
+
if (Array.isArray(group)) {
|
|
472
|
+
for (const entry of group) {
|
|
473
|
+
if (typeof entry === "string") {
|
|
474
|
+
const name = extractPkgName(entry);
|
|
475
|
+
if (name && !prod.has(name)) dev.add(name);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return { prod, dev };
|
|
487
482
|
}
|
|
488
483
|
|
|
489
|
-
// src/ecosystems/
|
|
490
|
-
var SUPPORTED_LOCKFILES3 = [
|
|
491
|
-
|
|
492
|
-
|
|
484
|
+
// src/ecosystems/python/index.ts
|
|
485
|
+
var SUPPORTED_LOCKFILES3 = [
|
|
486
|
+
{ filename: "uv.lock", type: "uv" },
|
|
487
|
+
{ filename: "poetry.lock", type: "poetry" },
|
|
488
|
+
{ filename: "pdm.lock", type: "pdm" },
|
|
489
|
+
{ filename: "pylock.toml", type: "pylock" }
|
|
490
|
+
// PEP 751
|
|
491
|
+
];
|
|
492
|
+
var lockfileTypeMap2 = new Map(SUPPORTED_LOCKFILES3.map((l) => [l.filename, l.type]));
|
|
493
|
+
var pythonEcosystem = {
|
|
494
|
+
name: "python",
|
|
493
495
|
supportedLockfiles: SUPPORTED_LOCKFILES3,
|
|
494
|
-
manifestName: "
|
|
496
|
+
manifestName: "pyproject.toml",
|
|
495
497
|
getLockfileType(filename) {
|
|
496
|
-
return filename
|
|
498
|
+
return lockfileTypeMap2.get(filename);
|
|
497
499
|
},
|
|
498
500
|
parseLockfile(content, _lockfileType) {
|
|
499
|
-
return
|
|
501
|
+
return parseTomlPackages(content);
|
|
500
502
|
},
|
|
501
503
|
parseDirectDeps(manifestContent) {
|
|
502
504
|
return parseDirectDeps3(manifestContent);
|
|
503
505
|
},
|
|
504
506
|
normalizeName(name) {
|
|
505
|
-
return
|
|
507
|
+
return normalizePythonName(name);
|
|
506
508
|
}
|
|
507
509
|
};
|
|
508
510
|
|
|
@@ -527,6 +529,29 @@ registerEcosystem(pythonEcosystem);
|
|
|
527
529
|
registerEcosystem(javascriptEcosystem);
|
|
528
530
|
registerEcosystem(denoEcosystem);
|
|
529
531
|
|
|
532
|
+
// src/core/diff.ts
|
|
533
|
+
function diffPackages(oldPkgs, newPkgs, directDeps, normalizeName) {
|
|
534
|
+
const allNames = /* @__PURE__ */ new Set([...Object.keys(oldPkgs), ...Object.keys(newPkgs)]);
|
|
535
|
+
const changes = [];
|
|
536
|
+
for (const name of [...allNames].sort()) {
|
|
537
|
+
const inOld = name in oldPkgs;
|
|
538
|
+
const inNew = name in newPkgs;
|
|
539
|
+
if (inOld && inNew && oldPkgs[name] === newPkgs[name]) continue;
|
|
540
|
+
const normalized = normalizeName(name);
|
|
541
|
+
const isProd = directDeps.prod.has(normalized);
|
|
542
|
+
const isDev = directDeps.dev.has(normalized) && !isProd;
|
|
543
|
+
changes.push({
|
|
544
|
+
name,
|
|
545
|
+
change_type: !inOld ? "added" : !inNew ? "removed" : "updated",
|
|
546
|
+
old_version: inOld ? oldPkgs[name] : null,
|
|
547
|
+
new_version: inNew ? newPkgs[name] : null,
|
|
548
|
+
is_direct: isProd || isDev,
|
|
549
|
+
is_dev: isDev
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
return changes;
|
|
553
|
+
}
|
|
554
|
+
|
|
530
555
|
// src/core/discovery.ts
|
|
531
556
|
import { posix } from "path";
|
|
532
557
|
function workspaceFromPath(filePath) {
|
|
@@ -590,6 +615,7 @@ function resolveLockfilePair(baseFiles, headFiles) {
|
|
|
590
615
|
basePath: chosen.path,
|
|
591
616
|
baseType: chosen.type,
|
|
592
617
|
headPath: chosen.path,
|
|
618
|
+
// biome-ignore lint/style/noNonNullAssertion: path is guaranteed present (comes from common set)
|
|
593
619
|
headType: headByPath.get(chosen.path).type,
|
|
594
620
|
migrationNote: null,
|
|
595
621
|
ecosystemName: chosen.ecosystemName
|
|
@@ -632,29 +658,6 @@ function resolveLockfilePair(baseFiles, headFiles) {
|
|
|
632
658
|
return null;
|
|
633
659
|
}
|
|
634
660
|
|
|
635
|
-
// src/core/diff.ts
|
|
636
|
-
function diffPackages(oldPkgs, newPkgs, directDeps, normalizeName) {
|
|
637
|
-
const allNames = /* @__PURE__ */ new Set([...Object.keys(oldPkgs), ...Object.keys(newPkgs)]);
|
|
638
|
-
const changes = [];
|
|
639
|
-
for (const name of [...allNames].sort()) {
|
|
640
|
-
const inOld = name in oldPkgs;
|
|
641
|
-
const inNew = name in newPkgs;
|
|
642
|
-
if (inOld && inNew && oldPkgs[name] === newPkgs[name]) continue;
|
|
643
|
-
const normalized = normalizeName(name);
|
|
644
|
-
const isProd = directDeps.prod.has(normalized);
|
|
645
|
-
const isDev = directDeps.dev.has(normalized) && !isProd;
|
|
646
|
-
changes.push({
|
|
647
|
-
name,
|
|
648
|
-
change_type: !inOld ? "added" : !inNew ? "removed" : "updated",
|
|
649
|
-
old_version: inOld ? oldPkgs[name] : null,
|
|
650
|
-
new_version: inNew ? newPkgs[name] : null,
|
|
651
|
-
is_direct: isProd || isDev,
|
|
652
|
-
is_dev: isDev
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
return changes;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
661
|
// src/core/report.ts
|
|
659
662
|
async function buildLockfileEntry(pair, workspace, getBase, getHead) {
|
|
660
663
|
const ecosystem = getEcosystemByName(pair.ecosystemName);
|
|
@@ -788,7 +791,7 @@ function gitLsTree(ref) {
|
|
|
788
791
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
789
792
|
var API_BASE = "https://api.github.com";
|
|
790
793
|
function token() {
|
|
791
|
-
const t = process.env
|
|
794
|
+
const t = process.env.GITHUB_TOKEN;
|
|
792
795
|
if (!t) throw new Error("GITHUB_TOKEN is required for GitHub API access");
|
|
793
796
|
return t;
|
|
794
797
|
}
|
|
@@ -824,7 +827,7 @@ async function getPrShas(prNumber, repo) {
|
|
|
824
827
|
return { baseRefOid: data.base.sha, headRefOid: data.head.sha };
|
|
825
828
|
}
|
|
826
829
|
function detectRepo() {
|
|
827
|
-
const fromEnv = process.env
|
|
830
|
+
const fromEnv = process.env.GITHUB_REPOSITORY;
|
|
828
831
|
if (fromEnv) return fromEnv;
|
|
829
832
|
try {
|
|
830
833
|
const remote = execFileSync2("git", ["remote", "get-url", "origin"], {
|
|
@@ -835,15 +838,17 @@ function detectRepo() {
|
|
|
835
838
|
if (match) return match[1];
|
|
836
839
|
} catch {
|
|
837
840
|
}
|
|
838
|
-
throw new Error(
|
|
839
|
-
"Could not detect GitHub repo \u2014 set GITHUB_REPOSITORY or pass --repo"
|
|
840
|
-
);
|
|
841
|
+
throw new Error("Could not detect GitHub repo \u2014 set GITHUB_REPOSITORY or pass --repo");
|
|
841
842
|
}
|
|
842
843
|
|
|
843
844
|
// src/index.ts
|
|
844
845
|
async function resolveApiShas(options) {
|
|
845
846
|
if (options.baseSha && options.headSha) {
|
|
846
|
-
return {
|
|
847
|
+
return {
|
|
848
|
+
baseSha: options.baseSha,
|
|
849
|
+
headSha: options.headSha,
|
|
850
|
+
repo: options.repo ?? detectRepo()
|
|
851
|
+
};
|
|
847
852
|
}
|
|
848
853
|
if (options.prNumber) {
|
|
849
854
|
const repo = options.repo ?? detectRepo();
|