circle-ir 4.17.0 → 4.18.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/dist/analysis/advisories.d.ts +186 -0
- package/dist/analysis/advisories.d.ts.map +1 -0
- package/dist/analysis/advisories.js +391 -0
- package/dist/analysis/advisories.js.map +1 -0
- package/dist/analysis/comment-lines.d.ts +14 -0
- package/dist/analysis/comment-lines.d.ts.map +1 -0
- package/dist/analysis/comment-lines.js +34 -0
- package/dist/analysis/comment-lines.js.map +1 -0
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +59 -5
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/constant-caller-gate.d.ts +2 -2
- package/dist/analysis/constant-caller-gate.d.ts.map +1 -1
- package/dist/analysis/constant-caller-gate.js +205 -29
- package/dist/analysis/constant-caller-gate.js.map +1 -1
- package/dist/analysis/constant-propagation/propagator.d.ts +4 -0
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +99 -0
- package/dist/analysis/constant-propagation/propagator.js.map +1 -1
- package/dist/analysis/entry-point-detection.d.ts.map +1 -1
- package/dist/analysis/entry-point-detection.js +72 -0
- package/dist/analysis/entry-point-detection.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +75 -8
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/maven-declared.d.ts +72 -0
- package/dist/analysis/maven-declared.d.ts.map +1 -0
- package/dist/analysis/maven-declared.js +216 -0
- package/dist/analysis/maven-declared.js.map +1 -0
- package/dist/analysis/maven-version.d.ts +28 -0
- package/dist/analysis/maven-version.d.ts.map +1 -0
- package/dist/analysis/maven-version.js +172 -0
- package/dist/analysis/maven-version.js.map +1 -0
- package/dist/analysis/passes/constant-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/constant-propagation-pass.js +29 -1
- package/dist/analysis/passes/constant-propagation-pass.js.map +1 -1
- package/dist/analysis/passes/cross-file-pass.d.ts.map +1 -1
- package/dist/analysis/passes/cross-file-pass.js +81 -1
- package/dist/analysis/passes/cross-file-pass.js.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts +7 -0
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js +44 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
- package/dist/analysis/passes/insecure-cookie-pass.d.ts +2 -0
- package/dist/analysis/passes/insecure-cookie-pass.d.ts.map +1 -1
- package/dist/analysis/passes/insecure-cookie-pass.js +25 -3
- package/dist/analysis/passes/insecure-cookie-pass.js.map +1 -1
- package/dist/analysis/passes/interprocedural-pass.d.ts +11 -14
- package/dist/analysis/passes/interprocedural-pass.d.ts.map +1 -1
- package/dist/analysis/passes/interprocedural-pass.js +562 -0
- package/dist/analysis/passes/interprocedural-pass.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts +18 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +1057 -14
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.d.ts.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.js +66 -3
- package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts +44 -2
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +690 -12
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +133 -10
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analysis/passes/tls-verify-disabled-pass.d.ts +18 -0
- package/dist/analysis/passes/tls-verify-disabled-pass.d.ts.map +1 -1
- package/dist/analysis/passes/tls-verify-disabled-pass.js +161 -0
- package/dist/analysis/passes/tls-verify-disabled-pass.js.map +1 -1
- package/dist/analysis/passes/weak-random-pass.d.ts +6 -0
- package/dist/analysis/passes/weak-random-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-random-pass.js +29 -0
- package/dist/analysis/passes/weak-random-pass.js.map +1 -1
- package/dist/analysis/promote-cross-file-flows.d.ts +17 -0
- package/dist/analysis/promote-cross-file-flows.d.ts.map +1 -0
- package/dist/analysis/promote-cross-file-flows.js +32 -0
- package/dist/analysis/promote-cross-file-flows.js.map +1 -0
- package/dist/analysis/sbom.d.ts +100 -0
- package/dist/analysis/sbom.d.ts.map +1 -1
- package/dist/analysis/sbom.js +514 -3
- package/dist/analysis/sbom.js.map +1 -1
- package/dist/analysis/semver-version.d.ts +30 -0
- package/dist/analysis/semver-version.d.ts.map +1 -0
- package/dist/analysis/semver-version.js +76 -0
- package/dist/analysis/semver-version.js.map +1 -0
- package/dist/analysis/taint-matcher.d.ts +5 -0
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +225 -4
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.d.ts +57 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +69 -6
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +2936 -291
- package/dist/core/circle-ir-core.cjs +634 -203
- package/dist/core/circle-ir-core.js +634 -203
- package/dist/core/extractors/dfg.js +205 -2
- package/dist/core/extractors/dfg.js.map +1 -1
- package/dist/core/extractors/ts-declaration-detail.d.ts +39 -0
- package/dist/core/extractors/ts-declaration-detail.d.ts.map +1 -0
- package/dist/core/extractors/ts-declaration-detail.js +266 -0
- package/dist/core/extractors/ts-declaration-detail.js.map +1 -0
- package/dist/graph/analysis-pass.d.ts +4 -1
- package/dist/graph/analysis-pass.d.ts.map +1 -1
- package/dist/graph/analysis-pass.js +4 -1
- package/dist/graph/analysis-pass.js.map +1 -1
- package/dist/graph-facts/dead-code.d.ts +62 -0
- package/dist/graph-facts/dead-code.d.ts.map +1 -0
- package/dist/graph-facts/dead-code.js +90 -0
- package/dist/graph-facts/dead-code.js.map +1 -0
- package/dist/graph-facts/dependency-calls.d.ts +86 -0
- package/dist/graph-facts/dependency-calls.d.ts.map +1 -0
- package/dist/graph-facts/dependency-calls.js +93 -0
- package/dist/graph-facts/dependency-calls.js.map +1 -0
- package/dist/graph-facts/index.d.ts +10 -0
- package/dist/graph-facts/index.d.ts.map +1 -0
- package/dist/graph-facts/index.js +10 -0
- package/dist/graph-facts/index.js.map +1 -0
- package/dist/graph-facts/maven-packages.d.ts +42 -0
- package/dist/graph-facts/maven-packages.d.ts.map +1 -0
- package/dist/graph-facts/maven-packages.js +133 -0
- package/dist/graph-facts/maven-packages.js.map +1 -0
- package/dist/graph-facts/reach.d.ts +203 -0
- package/dist/graph-facts/reach.d.ts.map +1 -0
- package/dist/graph-facts/reach.js +219 -0
- package/dist/graph-facts/reach.js.map +1 -0
- package/dist/graph-facts/removal-reach.d.ts +73 -0
- package/dist/graph-facts/removal-reach.d.ts.map +1 -0
- package/dist/graph-facts/removal-reach.js +64 -0
- package/dist/graph-facts/removal-reach.js.map +1 -0
- package/dist/index.d.ts +21 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -3
- package/dist/index.js.map +1 -1
- package/dist/navigation/go-graph.d.ts +68 -0
- package/dist/navigation/go-graph.d.ts.map +1 -0
- package/dist/navigation/go-graph.js +753 -0
- package/dist/navigation/go-graph.js.map +1 -0
- package/dist/navigation/index.d.ts +7 -1
- package/dist/navigation/index.d.ts.map +1 -1
- package/dist/navigation/index.js +6 -0
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/java-entry-tables.d.ts +43 -0
- package/dist/navigation/java-entry-tables.d.ts.map +1 -0
- package/dist/navigation/java-entry-tables.js +56 -0
- package/dist/navigation/java-entry-tables.js.map +1 -0
- package/dist/navigation/java-graph.d.ts +64 -0
- package/dist/navigation/java-graph.d.ts.map +1 -0
- package/dist/navigation/java-graph.js +466 -0
- package/dist/navigation/java-graph.js.map +1 -0
- package/dist/navigation/navigation-index.d.ts +51 -1
- package/dist/navigation/navigation-index.d.ts.map +1 -1
- package/dist/navigation/navigation-index.js +72 -0
- package/dist/navigation/navigation-index.js.map +1 -1
- package/dist/navigation/python-graph.d.ts +73 -0
- package/dist/navigation/python-graph.d.ts.map +1 -0
- package/dist/navigation/python-graph.js +867 -0
- package/dist/navigation/python-graph.js.map +1 -0
- package/dist/navigation/ts-call-edges.d.ts +193 -0
- package/dist/navigation/ts-call-edges.d.ts.map +1 -0
- package/dist/navigation/ts-call-edges.js +2089 -0
- package/dist/navigation/ts-call-edges.js.map +1 -0
- package/dist/navigation/ts-entry-tables.d.ts +88 -0
- package/dist/navigation/ts-entry-tables.d.ts.map +1 -0
- package/dist/navigation/ts-entry-tables.js +50 -0
- package/dist/navigation/ts-entry-tables.js.map +1 -0
- package/dist/path-set/branches.d.ts +52 -0
- package/dist/path-set/branches.d.ts.map +1 -0
- package/dist/path-set/branches.js +124 -0
- package/dist/path-set/branches.js.map +1 -0
- package/dist/path-set/finding-diff.d.ts +63 -0
- package/dist/path-set/finding-diff.d.ts.map +1 -0
- package/dist/path-set/finding-diff.js +150 -0
- package/dist/path-set/finding-diff.js.map +1 -0
- package/dist/path-set/index.d.ts +18 -0
- package/dist/path-set/index.d.ts.map +1 -0
- package/dist/path-set/index.js +12 -0
- package/dist/path-set/index.js.map +1 -0
- package/dist/path-set/line-diff.d.ts +19 -0
- package/dist/path-set/line-diff.d.ts.map +1 -0
- package/dist/path-set/line-diff.js +98 -0
- package/dist/path-set/line-diff.js.map +1 -0
- package/dist/path-set/path-set.d.ts +88 -0
- package/dist/path-set/path-set.d.ts.map +1 -0
- package/dist/path-set/path-set.js +716 -0
- package/dist/path-set/path-set.js.map +1 -0
- package/dist/path-set/templates.d.ts +42 -0
- package/dist/path-set/templates.d.ts.map +1 -0
- package/dist/path-set/templates.js +119 -0
- package/dist/path-set/templates.js.map +1 -0
- package/dist/path-set/types.d.ts +195 -0
- package/dist/path-set/types.d.ts.map +1 -0
- package/dist/path-set/types.js +8 -0
- package/dist/path-set/types.js.map +1 -0
- package/dist/remedy-class/index.d.ts +12 -0
- package/dist/remedy-class/index.d.ts.map +1 -0
- package/dist/remedy-class/index.js +9 -0
- package/dist/remedy-class/index.js.map +1 -0
- package/dist/remedy-class/match.d.ts +165 -0
- package/dist/remedy-class/match.d.ts.map +1 -0
- package/dist/remedy-class/match.js +705 -0
- package/dist/remedy-class/match.js.map +1 -0
- package/dist/remedy-class/output-context.d.ts +25 -0
- package/dist/remedy-class/output-context.d.ts.map +1 -0
- package/dist/remedy-class/output-context.js +150 -0
- package/dist/remedy-class/output-context.js.map +1 -0
- package/dist/remedy-class/sha256.d.ts +18 -0
- package/dist/remedy-class/sha256.d.ts.map +1 -0
- package/dist/remedy-class/sha256.js +89 -0
- package/dist/remedy-class/sha256.js.map +1 -0
- package/dist/remedy-class/table.d.ts +130 -0
- package/dist/remedy-class/table.d.ts.map +1 -0
- package/dist/remedy-class/table.js +457 -0
- package/dist/remedy-class/table.js.map +1 -0
- package/dist/resolution/cross-file.d.ts +9 -0
- package/dist/resolution/cross-file.d.ts.map +1 -1
- package/dist/resolution/cross-file.js +60 -5
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/stable-ids/builder.d.ts +20 -0
- package/dist/stable-ids/builder.d.ts.map +1 -0
- package/dist/stable-ids/builder.js +65 -0
- package/dist/stable-ids/builder.js.map +1 -0
- package/dist/stable-ids/diff.d.ts +18 -0
- package/dist/stable-ids/diff.d.ts.map +1 -0
- package/dist/stable-ids/diff.js +37 -0
- package/dist/stable-ids/diff.js.map +1 -0
- package/dist/stable-ids/go.d.ts +28 -0
- package/dist/stable-ids/go.d.ts.map +1 -0
- package/dist/stable-ids/go.js +160 -0
- package/dist/stable-ids/go.js.map +1 -0
- package/dist/stable-ids/hash.d.ts +22 -0
- package/dist/stable-ids/hash.d.ts.map +1 -0
- package/dist/stable-ids/hash.js +167 -0
- package/dist/stable-ids/hash.js.map +1 -0
- package/dist/stable-ids/index.d.ts +30 -0
- package/dist/stable-ids/index.d.ts.map +1 -0
- package/dist/stable-ids/index.js +59 -0
- package/dist/stable-ids/index.js.map +1 -0
- package/dist/stable-ids/java.d.ts +28 -0
- package/dist/stable-ids/java.d.ts.map +1 -0
- package/dist/stable-ids/java.js +224 -0
- package/dist/stable-ids/java.js.map +1 -0
- package/dist/stable-ids/moves.d.ts +87 -0
- package/dist/stable-ids/moves.d.ts.map +1 -0
- package/dist/stable-ids/moves.js +222 -0
- package/dist/stable-ids/moves.js.map +1 -0
- package/dist/stable-ids/nodes.d.ts +32 -0
- package/dist/stable-ids/nodes.d.ts.map +1 -0
- package/dist/stable-ids/nodes.js +92 -0
- package/dist/stable-ids/nodes.js.map +1 -0
- package/dist/stable-ids/normalize.d.ts +35 -0
- package/dist/stable-ids/normalize.d.ts.map +1 -0
- package/dist/stable-ids/normalize.js +174 -0
- package/dist/stable-ids/normalize.js.map +1 -0
- package/dist/stable-ids/parse-errors.d.ts +24 -0
- package/dist/stable-ids/parse-errors.d.ts.map +1 -0
- package/dist/stable-ids/parse-errors.js +46 -0
- package/dist/stable-ids/parse-errors.js.map +1 -0
- package/dist/stable-ids/python.d.ts +27 -0
- package/dist/stable-ids/python.d.ts.map +1 -0
- package/dist/stable-ids/python.js +212 -0
- package/dist/stable-ids/python.js.map +1 -0
- package/dist/stable-ids/ts-parse-errors.d.ts +27 -0
- package/dist/stable-ids/ts-parse-errors.d.ts.map +1 -0
- package/dist/stable-ids/ts-parse-errors.js +75 -0
- package/dist/stable-ids/ts-parse-errors.js.map +1 -0
- package/dist/stable-ids/types.d.ts +106 -0
- package/dist/stable-ids/types.d.ts.map +1 -0
- package/dist/stable-ids/types.js +16 -0
- package/dist/stable-ids/types.js.map +1 -0
- package/dist/stable-ids/typescript.d.ts +30 -0
- package/dist/stable-ids/typescript.d.ts.map +1 -0
- package/dist/stable-ids/typescript.js +362 -0
- package/dist/stable-ids/typescript.js.map +1 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +35 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +3 -3
package/dist/analysis/sbom.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* runtime dependencies circle-ir permits are `web-tree-sitter` and `yaml`,
|
|
21
21
|
* and a manifest dependency list needs nothing heavier.
|
|
22
22
|
*/
|
|
23
|
+
import { parse as parseYaml } from 'yaml';
|
|
23
24
|
// ---------------------------------------------------------------------------
|
|
24
25
|
// purl helpers
|
|
25
26
|
// ---------------------------------------------------------------------------
|
|
@@ -185,6 +186,332 @@ export function parseNpmLockDependencies(packageLock) {
|
|
|
185
186
|
return true;
|
|
186
187
|
});
|
|
187
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Scope every node by reachability from the project roots: reached from a
|
|
191
|
+
* production root → `required`; else from an optional root → `optional`;
|
|
192
|
+
* else from a dev root → `dev`. A node no root reaches keeps the lockfile's
|
|
193
|
+
* own flag (pnpm v5/v6 `dev` / `optional`), else `required`.
|
|
194
|
+
* `direct` is set when any root names the node; `undefined` when `hasRoots`
|
|
195
|
+
* is false (the input did not carry the project's declarations).
|
|
196
|
+
*/
|
|
197
|
+
function scopeLockNodes(nodes, roots, hasRoots) {
|
|
198
|
+
const reach = (starts) => {
|
|
199
|
+
const seen = new Set();
|
|
200
|
+
const stack = starts.filter((k) => nodes.has(k));
|
|
201
|
+
while (stack.length > 0) {
|
|
202
|
+
const k = stack.pop();
|
|
203
|
+
if (seen.has(k))
|
|
204
|
+
continue;
|
|
205
|
+
seen.add(k);
|
|
206
|
+
for (const next of nodes.get(k)?.deps ?? [])
|
|
207
|
+
if (nodes.has(next) && !seen.has(next))
|
|
208
|
+
stack.push(next);
|
|
209
|
+
}
|
|
210
|
+
return seen;
|
|
211
|
+
};
|
|
212
|
+
const prod = reach(roots.required);
|
|
213
|
+
const opt = reach(roots.optional);
|
|
214
|
+
const dev = reach(roots.dev);
|
|
215
|
+
const direct = new Set([...roots.required, ...roots.optional, ...roots.dev]);
|
|
216
|
+
const out = [];
|
|
217
|
+
const seen = new Set();
|
|
218
|
+
for (const [key, n] of [...nodes.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {
|
|
219
|
+
let scope;
|
|
220
|
+
if (prod.has(key))
|
|
221
|
+
scope = 'required';
|
|
222
|
+
else if (opt.has(key))
|
|
223
|
+
scope = 'optional';
|
|
224
|
+
else if (dev.has(key))
|
|
225
|
+
scope = 'dev';
|
|
226
|
+
else
|
|
227
|
+
scope = n.flagDev ? 'dev' : n.flagOptional ? 'optional' : 'required';
|
|
228
|
+
const id = `${n.name}|${n.version}|${scope}`;
|
|
229
|
+
if (seen.has(id))
|
|
230
|
+
continue;
|
|
231
|
+
seen.add(id);
|
|
232
|
+
const d = dep('npm', n.name, n.version, scope);
|
|
233
|
+
if (hasRoots)
|
|
234
|
+
d.direct = direct.has(key);
|
|
235
|
+
out.push(d);
|
|
236
|
+
}
|
|
237
|
+
return out;
|
|
238
|
+
}
|
|
239
|
+
/** Split `name@version` (name may be `@scope/name`). */
|
|
240
|
+
function splitAt(spec) {
|
|
241
|
+
const at = spec.lastIndexOf('@');
|
|
242
|
+
if (at <= 0)
|
|
243
|
+
return null;
|
|
244
|
+
return { name: spec.slice(0, at), rest: spec.slice(at + 1) };
|
|
245
|
+
}
|
|
246
|
+
/** Drop pnpm's peer suffix: `1.2.3(react@18.2.0)` → `1.2.3`; v5 `1.2.3_react@18` → `1.2.3`. */
|
|
247
|
+
function stripPnpmPeers(v) {
|
|
248
|
+
const paren = v.indexOf('(');
|
|
249
|
+
let out = paren >= 0 ? v.slice(0, paren) : v;
|
|
250
|
+
const us = out.indexOf('_');
|
|
251
|
+
if (us > 0)
|
|
252
|
+
out = out.slice(0, us);
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
/** Local or workspace references are the project's own code, not npm packages. */
|
|
256
|
+
const PNPM_LOCAL = /^(link:|file:|workspace:|portal:)/;
|
|
257
|
+
/**
|
|
258
|
+
* Turn a pnpm package key into `{ name, version }`:
|
|
259
|
+
* - v9: `name@1.2.3` / `name@1.2.3(peer@x)`
|
|
260
|
+
* - v6: `/name@1.2.3(peer@x)`
|
|
261
|
+
* - v5: `/name/1.2.3_peer@x` / `/@scope/name/1.2.3`
|
|
262
|
+
*/
|
|
263
|
+
function parsePnpmKey(rawKey) {
|
|
264
|
+
let key = rawKey.startsWith('/') ? rawKey.slice(1) : rawKey;
|
|
265
|
+
// v5 slash form first: its peer suffix (`_react@18`) contains an `@`.
|
|
266
|
+
const v5 = key.match(/^(@[^/@]+\/[^/@]+|[^/@]+)\/(\d[^/]*)$/);
|
|
267
|
+
if (v5)
|
|
268
|
+
return { name: v5[1], version: stripPnpmPeers(v5[2]) };
|
|
269
|
+
const paren = key.indexOf('(');
|
|
270
|
+
if (paren >= 0)
|
|
271
|
+
key = key.slice(0, paren);
|
|
272
|
+
const at = splitAt(key);
|
|
273
|
+
if (at && /^\d/.test(at.rest))
|
|
274
|
+
return { name: at.name, version: stripPnpmPeers(at.rest) };
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Resolve a dependency reference inside a pnpm lockfile to a node key
|
|
279
|
+
* (`name@version`). A reference is a version (`1.2.3`, `1.2.3(peer)`), a
|
|
280
|
+
* path key (`/other@1.0.0`, v5 `/other/1.0.0`) or an alias (`other@1.0.0`).
|
|
281
|
+
* Returns `null` for local/workspace links.
|
|
282
|
+
*/
|
|
283
|
+
function pnpmRef(depName, ref) {
|
|
284
|
+
if (typeof ref !== 'string')
|
|
285
|
+
return null;
|
|
286
|
+
if (PNPM_LOCAL.test(ref))
|
|
287
|
+
return null;
|
|
288
|
+
if (/^\d/.test(ref))
|
|
289
|
+
return `${depName}@${stripPnpmPeers(ref)}`;
|
|
290
|
+
const parsed = parsePnpmKey(ref);
|
|
291
|
+
return parsed ? `${parsed.name}@${parsed.version}` : null;
|
|
292
|
+
}
|
|
293
|
+
/** The version of an importer entry: v6/v9 `{ specifier, version }`, v5 a plain string. */
|
|
294
|
+
function importerVersion(entry) {
|
|
295
|
+
if (entry && typeof entry === 'object' && 'version' in entry) {
|
|
296
|
+
return entry.version;
|
|
297
|
+
}
|
|
298
|
+
return entry;
|
|
299
|
+
}
|
|
300
|
+
function asRecord(v) {
|
|
301
|
+
return v && typeof v === 'object' && !Array.isArray(v) ? v : undefined;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Parse a `pnpm-lock.yaml` (lockfile formats 5.x, 6.0 and 9.0) into exact
|
|
305
|
+
* resolved npm dependencies, direct and transitive.
|
|
306
|
+
*
|
|
307
|
+
* - **Roots:** every importer (`importers['.']`, `importers['packages/x']`, …),
|
|
308
|
+
* or, in single-project v5/v6 files, the top-level `dependencies` /
|
|
309
|
+
* `devDependencies` / `optionalDependencies`. Workspace and local links
|
|
310
|
+
* (`link:`, `file:`, `workspace:`) are the project's own packages and are
|
|
311
|
+
* not listed.
|
|
312
|
+
* - **Scope:** by reachability from the roots (prod → `required`, optional
|
|
313
|
+
* roots → `optional`, dev-only → `dev`); a package no root reaches keeps
|
|
314
|
+
* pnpm v5/v6's own `dev` / `optional` flag.
|
|
315
|
+
* - **direct:** `true` for a package an importer declares, else `false`.
|
|
316
|
+
*
|
|
317
|
+
* Peer-dependency variants of one version (`a@1.0.0(react@17)`,
|
|
318
|
+
* `a@1.0.0(react@18)`) collapse into one entry. Malformed YAML yields `[]`.
|
|
319
|
+
*/
|
|
320
|
+
export function parsePnpmLockDependencies(pnpmLock) {
|
|
321
|
+
if (!pnpmLock)
|
|
322
|
+
return [];
|
|
323
|
+
let root;
|
|
324
|
+
try {
|
|
325
|
+
root = asRecord(parseYaml(pnpmLock, { uniqueKeys: false }));
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
if (!root)
|
|
331
|
+
return [];
|
|
332
|
+
const nodes = new Map();
|
|
333
|
+
const addNode = (rawKey, info) => {
|
|
334
|
+
const id = parsePnpmKey(rawKey);
|
|
335
|
+
if (!id)
|
|
336
|
+
return;
|
|
337
|
+
const key = `${id.name}@${id.version}`;
|
|
338
|
+
const node = nodes.get(key) ?? { name: id.name, version: id.version, deps: [] };
|
|
339
|
+
if (info) {
|
|
340
|
+
for (const section of ['dependencies', 'optionalDependencies']) {
|
|
341
|
+
for (const [n, ref] of Object.entries(asRecord(info[section]) ?? {})) {
|
|
342
|
+
const k = pnpmRef(n, ref);
|
|
343
|
+
if (k && !node.deps.includes(k))
|
|
344
|
+
node.deps.push(k);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (info.dev === true)
|
|
348
|
+
node.flagDev = true;
|
|
349
|
+
if (info.optional === true)
|
|
350
|
+
node.flagOptional = true;
|
|
351
|
+
}
|
|
352
|
+
nodes.set(key, node);
|
|
353
|
+
};
|
|
354
|
+
for (const [k, v] of Object.entries(asRecord(root.packages) ?? {}))
|
|
355
|
+
addNode(k, asRecord(v));
|
|
356
|
+
// v9 keeps the graph in `snapshots`, identities in `packages`.
|
|
357
|
+
for (const [k, v] of Object.entries(asRecord(root.snapshots) ?? {}))
|
|
358
|
+
addNode(k, asRecord(v));
|
|
359
|
+
const roots = { required: [], optional: [], dev: [] };
|
|
360
|
+
const importers = asRecord(root.importers);
|
|
361
|
+
const projects = importers
|
|
362
|
+
? Object.values(importers).map(asRecord).filter((x) => Boolean(x))
|
|
363
|
+
: [root];
|
|
364
|
+
for (const project of projects) {
|
|
365
|
+
for (const [section, bucket] of [
|
|
366
|
+
['dependencies', roots.required],
|
|
367
|
+
['optionalDependencies', roots.optional],
|
|
368
|
+
['devDependencies', roots.dev],
|
|
369
|
+
]) {
|
|
370
|
+
for (const [n, entry] of Object.entries(asRecord(project[section]) ?? {})) {
|
|
371
|
+
const k = pnpmRef(n, importerVersion(entry));
|
|
372
|
+
if (k)
|
|
373
|
+
bucket.push(k);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return scopeLockNodes(nodes, roots, true);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* The project directories a `pnpm-lock.yaml` covers, relative to the
|
|
381
|
+
* lockfile (`.` for the root). A workspace lockfile resolves every listed
|
|
382
|
+
* package, so it supersedes each of their `package.json` files (DT4).
|
|
383
|
+
* Single-project lockfiles return `['.']`; malformed YAML returns `[]`.
|
|
384
|
+
*/
|
|
385
|
+
export function pnpmLockImporters(pnpmLock) {
|
|
386
|
+
if (!pnpmLock)
|
|
387
|
+
return [];
|
|
388
|
+
let root;
|
|
389
|
+
try {
|
|
390
|
+
root = asRecord(parseYaml(pnpmLock, { uniqueKeys: false }));
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
return [];
|
|
394
|
+
}
|
|
395
|
+
if (!root)
|
|
396
|
+
return [];
|
|
397
|
+
const importers = asRecord(root.importers);
|
|
398
|
+
return importers ? Object.keys(importers).sort() : ['.'];
|
|
399
|
+
}
|
|
400
|
+
/** Unquote a yarn v1 token: `"@babel/core@^7.0.0"` → `@babel/core@^7.0.0`. */
|
|
401
|
+
function yarnUnquote(s) {
|
|
402
|
+
const t = s.trim();
|
|
403
|
+
return t.length >= 2 && t.startsWith('"') && t.endsWith('"') ? t.slice(1, -1) : t;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Parse a Yarn classic (v1) `yarn.lock` into exact resolved npm
|
|
407
|
+
* dependencies. Yarn v1 records no dev or direct marker, so:
|
|
408
|
+
*
|
|
409
|
+
* - with only the lockfile, every package is `required` and `direct` is
|
|
410
|
+
* absent (unknown);
|
|
411
|
+
* - with the project's `package.json` as the second argument, scope and
|
|
412
|
+
* `direct` come from reachability, as for pnpm.
|
|
413
|
+
*
|
|
414
|
+
* Yarn Berry (v2+, a YAML file with `__metadata`) is not handled and yields
|
|
415
|
+
* `[]`. Malformed entries are skipped.
|
|
416
|
+
*/
|
|
417
|
+
export function parseYarnLockDependencies(yarnLock, packageJson) {
|
|
418
|
+
if (!yarnLock)
|
|
419
|
+
return [];
|
|
420
|
+
if (/^__metadata:/m.test(yarnLock))
|
|
421
|
+
return [];
|
|
422
|
+
const nodes = new Map();
|
|
423
|
+
const specToKey = new Map();
|
|
424
|
+
const pendingDeps = [];
|
|
425
|
+
let specs = [];
|
|
426
|
+
let version;
|
|
427
|
+
let deps = [];
|
|
428
|
+
let inDeps = false;
|
|
429
|
+
const flush = () => {
|
|
430
|
+
if (specs.length > 0 && version) {
|
|
431
|
+
const first = splitAt(specs[0]);
|
|
432
|
+
if (first) {
|
|
433
|
+
const key = `${first.name}@${version}`;
|
|
434
|
+
if (!nodes.has(key))
|
|
435
|
+
nodes.set(key, { name: first.name, version, deps: [] });
|
|
436
|
+
for (const s of specs)
|
|
437
|
+
specToKey.set(s, key);
|
|
438
|
+
pendingDeps.push([key, deps]);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
specs = [];
|
|
442
|
+
version = undefined;
|
|
443
|
+
deps = [];
|
|
444
|
+
inDeps = false;
|
|
445
|
+
};
|
|
446
|
+
for (const rawLine of yarnLock.split(/\r?\n/)) {
|
|
447
|
+
if (!rawLine.trim() || rawLine.trimStart().startsWith('#'))
|
|
448
|
+
continue;
|
|
449
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
450
|
+
const line = rawLine.trim();
|
|
451
|
+
if (indent === 0) {
|
|
452
|
+
flush();
|
|
453
|
+
if (!line.endsWith(':'))
|
|
454
|
+
continue;
|
|
455
|
+
specs = line.slice(0, -1).split(',').map(yarnUnquote).filter(Boolean);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (indent <= 2) {
|
|
459
|
+
inDeps = false;
|
|
460
|
+
const m = line.match(/^(\S+)\s+(.+)$/);
|
|
461
|
+
if (line === 'dependencies:' || line === 'optionalDependencies:') {
|
|
462
|
+
inDeps = true;
|
|
463
|
+
}
|
|
464
|
+
else if (m && yarnUnquote(m[1]) === 'version') {
|
|
465
|
+
version = yarnUnquote(m[2]);
|
|
466
|
+
}
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (inDeps) {
|
|
470
|
+
const m = line.match(/^(\S+)\s+(.+)$/);
|
|
471
|
+
if (m)
|
|
472
|
+
deps.push(`${yarnUnquote(m[1])}@${yarnUnquote(m[2])}`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
flush();
|
|
476
|
+
for (const [key, ds] of pendingDeps) {
|
|
477
|
+
const node = nodes.get(key);
|
|
478
|
+
if (!node)
|
|
479
|
+
continue;
|
|
480
|
+
for (const s of ds) {
|
|
481
|
+
const k = specToKey.get(s);
|
|
482
|
+
if (k && !node.deps.includes(k))
|
|
483
|
+
node.deps.push(k);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const roots = { required: [], optional: [], dev: [] };
|
|
487
|
+
let hasRoots = false;
|
|
488
|
+
if (packageJson) {
|
|
489
|
+
try {
|
|
490
|
+
const pkg = asRecord(JSON.parse(packageJson));
|
|
491
|
+
if (pkg) {
|
|
492
|
+
hasRoots = true;
|
|
493
|
+
for (const [section, bucket] of [
|
|
494
|
+
['dependencies', roots.required],
|
|
495
|
+
['peerDependencies', roots.required],
|
|
496
|
+
['optionalDependencies', roots.optional],
|
|
497
|
+
['devDependencies', roots.dev],
|
|
498
|
+
]) {
|
|
499
|
+
for (const [n, range] of Object.entries(asRecord(pkg[section]) ?? {})) {
|
|
500
|
+
if (typeof range !== 'string')
|
|
501
|
+
continue;
|
|
502
|
+
const k = specToKey.get(`${n}@${range}`);
|
|
503
|
+
if (k)
|
|
504
|
+
bucket.push(k);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
hasRoots = false;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return scopeLockNodes(nodes, roots, hasRoots);
|
|
514
|
+
}
|
|
188
515
|
/**
|
|
189
516
|
* Parse a TOML `[[package]]` array (the shared shape of `Cargo.lock` and
|
|
190
517
|
* `poetry.lock`): a sequence of blocks each carrying `name = "..."` and
|
|
@@ -404,6 +731,125 @@ export function parseMavenDependencies(pomXml) {
|
|
|
404
731
|
}
|
|
405
732
|
return out;
|
|
406
733
|
}
|
|
734
|
+
/**
|
|
735
|
+
* Parse a Gradle dependency lockfile — the resolved, exact versions of every
|
|
736
|
+
* direct and transitive dependency, per configuration. Two layouts:
|
|
737
|
+
*
|
|
738
|
+
* - **`gradle.lockfile`** (Gradle 6.4+, one file per project):
|
|
739
|
+
* `group:artifact:version=conf1,conf2,…`; the `empty=…` line lists
|
|
740
|
+
* configurations that resolved nothing and is skipped.
|
|
741
|
+
* - **`gradle/dependency-locks/<configuration>.lockfile`** (older, one file
|
|
742
|
+
* per configuration): bare `group:artifact:version` lines. The
|
|
743
|
+
* configuration is the file name, which the caller may pass as
|
|
744
|
+
* `configuration`; without it the entries count as `required`.
|
|
745
|
+
*
|
|
746
|
+
* A dependency is `dev` only when every configuration that resolved it is a
|
|
747
|
+
* test configuration (`test…`, or `…Test…` such as `integrationTestRuntimeClasspath`);
|
|
748
|
+
* any other configuration makes it `required`. Comment lines (`#`) are skipped.
|
|
749
|
+
*/
|
|
750
|
+
export function parseGradleLockfile(lockfile, configuration) {
|
|
751
|
+
if (!lockfile)
|
|
752
|
+
return [];
|
|
753
|
+
const out = [];
|
|
754
|
+
const seen = new Set();
|
|
755
|
+
for (const raw of lockfile.split(/\r?\n/)) {
|
|
756
|
+
const line = raw.trim();
|
|
757
|
+
if (!line || line.startsWith('#'))
|
|
758
|
+
continue;
|
|
759
|
+
const eq = line.indexOf('=');
|
|
760
|
+
const coordPart = eq >= 0 ? line.slice(0, eq).trim() : line;
|
|
761
|
+
if (coordPart === 'empty')
|
|
762
|
+
continue;
|
|
763
|
+
const coord = coordPart.split(':');
|
|
764
|
+
if (coord.length !== 3 || coord.some((c) => !c))
|
|
765
|
+
continue;
|
|
766
|
+
const configs = eq >= 0
|
|
767
|
+
? line.slice(eq + 1).split(',').map((c) => c.trim()).filter(Boolean)
|
|
768
|
+
: configuration
|
|
769
|
+
? [configuration]
|
|
770
|
+
: [];
|
|
771
|
+
const scope = configs.length > 0 && configs.every(isGradleTestConfiguration) ? 'dev' : 'required';
|
|
772
|
+
const name = `${coord[0]}:${coord[1]}`;
|
|
773
|
+
const key = `${name}|${coord[2]}|${scope}`;
|
|
774
|
+
if (seen.has(key))
|
|
775
|
+
continue;
|
|
776
|
+
seen.add(key);
|
|
777
|
+
out.push(dep('maven', name, coord[2], scope));
|
|
778
|
+
}
|
|
779
|
+
return out;
|
|
780
|
+
}
|
|
781
|
+
function isGradleTestConfiguration(config) {
|
|
782
|
+
return /^test[A-Z]?/.test(config) || /Test[A-Z]/.test(config);
|
|
783
|
+
}
|
|
784
|
+
/** Maven scopes as printed by `dependency:list` / `dependency:tree`. */
|
|
785
|
+
const MAVEN_SCOPES = 'compile|provided|runtime|test|system|import';
|
|
786
|
+
/**
|
|
787
|
+
* Coordinate as Maven prints it in resolved output:
|
|
788
|
+
* `groupId:artifactId:type[:classifier]:version:scope`.
|
|
789
|
+
*/
|
|
790
|
+
const MAVEN_RESOLVED_COORD = new RegExp(`^([\\w.-]+):([\\w.-]+):([\\w.-]+)(?::([\\w.-]+))?:([^:\\s()]+):(${MAVEN_SCOPES})\\b(.*)$`);
|
|
791
|
+
/**
|
|
792
|
+
* Parse the text output of `mvn dependency:list` or `mvn dependency:tree`
|
|
793
|
+
* (console or `-DoutputFile`): the dependency set Maven actually resolved,
|
|
794
|
+
* direct and transitive, after mediation. A `pom.xml` alone cannot give this —
|
|
795
|
+
* versions come from parents, BOMs and nearest-wins mediation — so the caller
|
|
796
|
+
* runs Maven (in a build sandbox) and hands the text here; the engine never
|
|
797
|
+
* runs a build.
|
|
798
|
+
*
|
|
799
|
+
* Accepts `[INFO]` prefixes, tree drawing (`+-`, `|`, `\-`), the module
|
|
800
|
+
* suffix newer plugins print (`-- module x`), and the root project line of a
|
|
801
|
+
* tree (skipped: it has no scope). Verbose-tree entries that Maven did **not**
|
|
802
|
+
* resolve (`omitted for conflict with …`) are skipped; `omitted for duplicate`
|
|
803
|
+
* entries collapse into the one kept. `test` / `provided` → `dev`, a trailing
|
|
804
|
+
* `(optional)` → `optional`, everything else → `required`. From a tree, each
|
|
805
|
+
* entry also carries `direct` and `via` (its path from a direct dependency).
|
|
806
|
+
*/
|
|
807
|
+
export function parseMavenResolvedDependencies(output) {
|
|
808
|
+
if (!output)
|
|
809
|
+
return [];
|
|
810
|
+
const out = [];
|
|
811
|
+
const seen = new Set();
|
|
812
|
+
const lines = output.split(/\r?\n/).map((raw) => raw.replace(/^\s*\[(?:INFO|WARNING|DEBUG)\]\s?/, ''));
|
|
813
|
+
// A tree carries the graph: depth from indentation (3 columns per level),
|
|
814
|
+
// the path from the stack of ancestors. A list does not.
|
|
815
|
+
const isTree = lines.some((l) => /^[\s|]*[+\\]- /.test(l));
|
|
816
|
+
const ancestors = [];
|
|
817
|
+
for (const raw of lines) {
|
|
818
|
+
const depth = isTree ? Math.round(raw.search(/[A-Za-z0-9(]/) / 3) : -1;
|
|
819
|
+
if (/omitted for conflict/.test(raw))
|
|
820
|
+
continue;
|
|
821
|
+
// Strip tree drawing and the verbose-tree parenthesis around an entry.
|
|
822
|
+
const line = raw.replace(/^[\s|+\\`-]*/, '').replace(/^\(/, '').trim();
|
|
823
|
+
const m = MAVEN_RESOLVED_COORD.exec(line);
|
|
824
|
+
if (!m) {
|
|
825
|
+
// A root (project) line of a tree starts a new graph.
|
|
826
|
+
if (isTree && depth === 0 && /^[\w.-]+:[\w.-]+:/.test(line))
|
|
827
|
+
ancestors.length = 0;
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
const [, gid, aid, , , version, mvnScope, rest] = m;
|
|
831
|
+
const name = `${gid}:${aid}`;
|
|
832
|
+
let via;
|
|
833
|
+
if (isTree && depth >= 1) {
|
|
834
|
+
ancestors.length = depth - 1;
|
|
835
|
+
via = ancestors.slice();
|
|
836
|
+
ancestors.push(name);
|
|
837
|
+
}
|
|
838
|
+
const optional = /\(optional\)/.test(rest);
|
|
839
|
+
const scope = mvnScope === 'test' || mvnScope === 'provided' ? 'dev' : optional ? 'optional' : 'required';
|
|
840
|
+
const key = `${name}|${version}|${scope}`;
|
|
841
|
+
if (seen.has(key))
|
|
842
|
+
continue;
|
|
843
|
+
seen.add(key);
|
|
844
|
+
const d = dep('maven', name, version, scope);
|
|
845
|
+
if (via) {
|
|
846
|
+
d.direct = via.length === 0;
|
|
847
|
+
d.via = via;
|
|
848
|
+
}
|
|
849
|
+
out.push(d);
|
|
850
|
+
}
|
|
851
|
+
return out;
|
|
852
|
+
}
|
|
407
853
|
/**
|
|
408
854
|
* Parse a Cargo `Cargo.toml`. `[dependencies]` → required,
|
|
409
855
|
* `[dev-dependencies]` → dev, `[build-dependencies]` → optional. Handles both
|
|
@@ -503,15 +949,29 @@ export function collectDependencies(ctx) {
|
|
|
503
949
|
if (!ctx)
|
|
504
950
|
return [];
|
|
505
951
|
const all = [];
|
|
506
|
-
|
|
952
|
+
// npm resolved sets (exact + transitive) supersede `package.json` (DT4):
|
|
953
|
+
// pnpm > package-lock > yarn when more than one is supplied.
|
|
954
|
+
if (ctx.js?.pnpmLock)
|
|
955
|
+
all.push(...parsePnpmLockDependencies(ctx.js.pnpmLock));
|
|
956
|
+
else if (ctx.js?.packageLock)
|
|
957
|
+
all.push(...parseNpmLockDependencies(ctx.js.packageLock));
|
|
958
|
+
else if (ctx.js?.yarnLock)
|
|
959
|
+
all.push(...parseYarnLockDependencies(ctx.js.yarnLock, ctx.js.packageJson));
|
|
960
|
+
else if (ctx.js?.packageJson)
|
|
507
961
|
all.push(...parseNpmDependencies(ctx.js.packageJson));
|
|
508
962
|
if (ctx.python?.requirementsTxt)
|
|
509
963
|
all.push(...parsePypiDependencies(ctx.python.requirementsTxt));
|
|
510
964
|
if (ctx.python?.pyprojectToml)
|
|
511
965
|
all.push(...parsePyprojectDependencies(ctx.python.pyprojectToml));
|
|
512
|
-
|
|
966
|
+
// Resolved Java dependency sets (exact + transitive) supersede the declared
|
|
967
|
+
// manifest they came from, as a lockfile does for npm, Cargo and Poetry.
|
|
968
|
+
if (ctx.java?.mavenResolved)
|
|
969
|
+
all.push(...parseMavenResolvedDependencies(ctx.java.mavenResolved));
|
|
970
|
+
else if (ctx.java?.pomXml)
|
|
513
971
|
all.push(...parseMavenDependencies(ctx.java.pomXml));
|
|
514
|
-
if (ctx.java?.
|
|
972
|
+
if (ctx.java?.gradleLockfile)
|
|
973
|
+
all.push(...parseGradleLockfile(ctx.java.gradleLockfile));
|
|
974
|
+
else if (ctx.java?.buildGradle)
|
|
515
975
|
all.push(...parseGradleDependencies(ctx.java.buildGradle));
|
|
516
976
|
if (ctx.rust?.cargoToml)
|
|
517
977
|
all.push(...parseCargoDependencies(ctx.rust.cargoToml));
|
|
@@ -526,6 +986,57 @@ export function collectDependencies(ctx) {
|
|
|
526
986
|
}
|
|
527
987
|
return deduped;
|
|
528
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* The licence a package's own `package.json` declares, as one expression:
|
|
991
|
+
* `license` (string, or the legacy `{ type }` object), else the legacy
|
|
992
|
+
* `licenses: [{ type }]` array joined with ` OR `. `undefined` when the text
|
|
993
|
+
* does not parse or declares none.
|
|
994
|
+
*/
|
|
995
|
+
export function licenseFromPackageJson(packageJson) {
|
|
996
|
+
let pkg;
|
|
997
|
+
try {
|
|
998
|
+
pkg = JSON.parse(packageJson);
|
|
999
|
+
}
|
|
1000
|
+
catch {
|
|
1001
|
+
return undefined;
|
|
1002
|
+
}
|
|
1003
|
+
if (!pkg || typeof pkg !== 'object')
|
|
1004
|
+
return undefined;
|
|
1005
|
+
const rec = pkg;
|
|
1006
|
+
const one = (v) => {
|
|
1007
|
+
if (typeof v === 'string' && v.trim())
|
|
1008
|
+
return v.trim();
|
|
1009
|
+
if (v && typeof v === 'object') {
|
|
1010
|
+
const t = v.type;
|
|
1011
|
+
if (typeof t === 'string' && t.trim())
|
|
1012
|
+
return t.trim();
|
|
1013
|
+
}
|
|
1014
|
+
return undefined;
|
|
1015
|
+
};
|
|
1016
|
+
const direct = one(rec.license);
|
|
1017
|
+
if (direct)
|
|
1018
|
+
return direct;
|
|
1019
|
+
if (Array.isArray(rec.licenses)) {
|
|
1020
|
+
const all = rec.licenses.map(one).filter((x) => !!x);
|
|
1021
|
+
if (all.length === 1)
|
|
1022
|
+
return all[0];
|
|
1023
|
+
if (all.length > 1)
|
|
1024
|
+
return `(${all.join(' OR ')})`;
|
|
1025
|
+
}
|
|
1026
|
+
return undefined;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Fill in `license` from an offline map for dependencies that have none.
|
|
1030
|
+
* Returns new objects; an existing `license` is never overwritten.
|
|
1031
|
+
*/
|
|
1032
|
+
export function applyLicenses(deps, map) {
|
|
1033
|
+
return deps.map((d) => {
|
|
1034
|
+
if (d.license)
|
|
1035
|
+
return d;
|
|
1036
|
+
const lic = map[`${d.name}@${d.version}`] ?? map[d.name];
|
|
1037
|
+
return lic ? { ...d, license: lic } : d;
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
529
1040
|
// ---------------------------------------------------------------------------
|
|
530
1041
|
// emitters
|
|
531
1042
|
// ---------------------------------------------------------------------------
|