@trustify-da/trustify-da-javascript-client 0.3.0-ea.320ec49 → 0.3.0-ea.3621bb7
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/src/provider.js
CHANGED
|
@@ -7,7 +7,6 @@ import Javascript_npm from './providers/javascript_npm.js';
|
|
|
7
7
|
import Javascript_pnpm from './providers/javascript_pnpm.js';
|
|
8
8
|
import Javascript_yarn from './providers/javascript_yarn.js';
|
|
9
9
|
import pythonPipProvider from './providers/python_pip.js';
|
|
10
|
-
import Python_pip_pyproject from './providers/python_pip_pyproject.js';
|
|
11
10
|
import Python_poetry from './providers/python_poetry.js';
|
|
12
11
|
import Python_uv from './providers/python_uv.js';
|
|
13
12
|
import rustCargoProvider from './providers/rust_cargo.js';
|
|
@@ -28,7 +27,6 @@ export const availableProviders = [
|
|
|
28
27
|
pythonPipProvider,
|
|
29
28
|
new Python_poetry(),
|
|
30
29
|
new Python_uv(),
|
|
31
|
-
new Python_pip_pyproject(),
|
|
32
30
|
rustCargoProvider
|
|
33
31
|
];
|
|
34
32
|
/**
|
|
@@ -102,14 +102,26 @@ export default class Base_pyproject {
|
|
|
102
102
|
*/
|
|
103
103
|
protected _getIgnoredDeps(manifestPath: string): Set<string>;
|
|
104
104
|
/**
|
|
105
|
-
*
|
|
105
|
+
* Build dependency tree from graph, starting from direct deps.
|
|
106
106
|
* @param {Map<string, GraphEntry>} graph
|
|
107
|
-
* @param {string[]} directDeps
|
|
107
|
+
* @param {string[]} directDeps - canonical names of direct deps
|
|
108
108
|
* @param {Set<string>} ignoredDeps
|
|
109
|
-
* @
|
|
109
|
+
* @param {boolean} includeTransitive
|
|
110
|
+
* @returns {DepTreeEntry[]}
|
|
111
|
+
* @protected
|
|
112
|
+
*/
|
|
113
|
+
protected _buildDependencyTree(graph: Map<string, GraphEntry>, directDeps: string[], ignoredDeps: Set<string>, includeTransitive: boolean): DepTreeEntry[];
|
|
114
|
+
/**
|
|
115
|
+
* Recursively collect transitive dependencies.
|
|
116
|
+
* @param {Map<string, GraphEntry>} graph
|
|
117
|
+
* @param {string[]} childKeys
|
|
118
|
+
* @param {DepTreeEntry[]} result - mutated in place
|
|
119
|
+
* @param {Set<string>} ignoredDeps
|
|
120
|
+
* @param {Set<string>} visited
|
|
121
|
+
* @returns {void}
|
|
110
122
|
* @protected
|
|
111
123
|
*/
|
|
112
|
-
protected
|
|
124
|
+
protected _collectTransitive(graph: Map<string, GraphEntry>, childKeys: string[], result: DepTreeEntry[], ignoredDeps: Set<string>, visited: Set<string>): void;
|
|
113
125
|
/**
|
|
114
126
|
* @param {string} name
|
|
115
127
|
* @param {string} version
|
|
@@ -117,6 +129,15 @@ export default class Base_pyproject {
|
|
|
117
129
|
* @protected
|
|
118
130
|
*/
|
|
119
131
|
protected _toPurl(name: string, version: string): PackageURL;
|
|
132
|
+
/**
|
|
133
|
+
* Recursively add a dependency and its transitive deps to the SBOM.
|
|
134
|
+
* @param {PackageURL} source
|
|
135
|
+
* @param {DepTreeEntry} dep
|
|
136
|
+
* @param {Sbom} sbom
|
|
137
|
+
* @returns {void}
|
|
138
|
+
* @private
|
|
139
|
+
*/
|
|
140
|
+
private _addAllDependencies;
|
|
120
141
|
/**
|
|
121
142
|
* Create SBOM json string for a pyproject.toml project.
|
|
122
143
|
* @param {string} manifest - path to pyproject.toml
|
|
@@ -220,29 +220,64 @@ export default class Base_pyproject {
|
|
|
220
220
|
return ignored;
|
|
221
221
|
}
|
|
222
222
|
/**
|
|
223
|
-
*
|
|
223
|
+
* Build dependency tree from graph, starting from direct deps.
|
|
224
224
|
* @param {Map<string, GraphEntry>} graph
|
|
225
|
-
* @param {string[]} directDeps
|
|
225
|
+
* @param {string[]} directDeps - canonical names of direct deps
|
|
226
226
|
* @param {Set<string>} ignoredDeps
|
|
227
|
-
* @
|
|
227
|
+
* @param {boolean} includeTransitive
|
|
228
|
+
* @returns {DepTreeEntry[]}
|
|
228
229
|
* @protected
|
|
229
230
|
*/
|
|
230
|
-
|
|
231
|
-
let
|
|
232
|
-
let
|
|
233
|
-
|
|
234
|
-
let key = queue.shift();
|
|
235
|
-
if (reachable.has(key)) {
|
|
231
|
+
_buildDependencyTree(graph, directDeps, ignoredDeps, includeTransitive) {
|
|
232
|
+
let result = [];
|
|
233
|
+
for (let key of directDeps) {
|
|
234
|
+
if (ignoredDeps.has(key)) {
|
|
236
235
|
continue;
|
|
237
236
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
queue.push(child);
|
|
242
|
-
}
|
|
237
|
+
let entry = graph.get(key);
|
|
238
|
+
if (!entry) {
|
|
239
|
+
continue;
|
|
243
240
|
}
|
|
241
|
+
let depTree = [];
|
|
242
|
+
if (includeTransitive) {
|
|
243
|
+
let visited = new Set();
|
|
244
|
+
visited.add(key);
|
|
245
|
+
this._collectTransitive(graph, entry.children, depTree, ignoredDeps, visited);
|
|
246
|
+
}
|
|
247
|
+
result.push({ name: entry.name, version: entry.version, dependencies: depTree });
|
|
244
248
|
}
|
|
245
|
-
|
|
249
|
+
result.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Recursively collect transitive dependencies.
|
|
254
|
+
* @param {Map<string, GraphEntry>} graph
|
|
255
|
+
* @param {string[]} childKeys
|
|
256
|
+
* @param {DepTreeEntry[]} result - mutated in place
|
|
257
|
+
* @param {Set<string>} ignoredDeps
|
|
258
|
+
* @param {Set<string>} visited
|
|
259
|
+
* @returns {void}
|
|
260
|
+
* @protected
|
|
261
|
+
*/
|
|
262
|
+
_collectTransitive(graph, childKeys, result, ignoredDeps, visited) {
|
|
263
|
+
for (let childKey of childKeys) {
|
|
264
|
+
let canonKey = this._canonicalize(childKey);
|
|
265
|
+
if (ignoredDeps.has(canonKey)) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (visited.has(canonKey)) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
visited.add(canonKey);
|
|
272
|
+
let entry = graph.get(canonKey);
|
|
273
|
+
if (!entry) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
let childDeps = [];
|
|
277
|
+
this._collectTransitive(graph, entry.children, childDeps, ignoredDeps, visited);
|
|
278
|
+
result.push({ name: entry.name, version: entry.version, dependencies: childDeps });
|
|
279
|
+
}
|
|
280
|
+
result.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
246
281
|
}
|
|
247
282
|
/**
|
|
248
283
|
* @param {string} name
|
|
@@ -253,6 +288,21 @@ export default class Base_pyproject {
|
|
|
253
288
|
_toPurl(name, version) {
|
|
254
289
|
return new PackageURL('pypi', undefined, name, version, undefined, undefined);
|
|
255
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Recursively add a dependency and its transitive deps to the SBOM.
|
|
293
|
+
* @param {PackageURL} source
|
|
294
|
+
* @param {DepTreeEntry} dep
|
|
295
|
+
* @param {Sbom} sbom
|
|
296
|
+
* @returns {void}
|
|
297
|
+
* @private
|
|
298
|
+
*/
|
|
299
|
+
_addAllDependencies(source, dep, sbom) {
|
|
300
|
+
let targetPurl = this._toPurl(dep.name, dep.version);
|
|
301
|
+
sbom.addDependency(source, targetPurl);
|
|
302
|
+
if (dep.dependencies && dep.dependencies.length > 0) {
|
|
303
|
+
dep.dependencies.forEach(child => this._addAllDependencies(this._toPurl(dep.name, dep.version), child, sbom));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
256
306
|
/**
|
|
257
307
|
* Create SBOM json string for a pyproject.toml project.
|
|
258
308
|
* @param {string} manifest - path to pyproject.toml
|
|
@@ -268,47 +318,21 @@ export default class Base_pyproject {
|
|
|
268
318
|
let workspaceDir = this._findLockFileDir(manifestDir, opts) || manifestDir;
|
|
269
319
|
let { directDeps, graph } = await this._getDependencyData(manifestDir, workspaceDir, parsed, opts);
|
|
270
320
|
let ignoredDeps = this._getIgnoredDeps(manifest);
|
|
321
|
+
let dependencies = this._buildDependencyTree(graph, directDeps, ignoredDeps, includeTransitive);
|
|
271
322
|
let sbom = new Sbom();
|
|
272
323
|
let rootName = this._getProjectName(parsed) || DEFAULT_ROOT_NAME;
|
|
273
324
|
let rootVersion = this._getProjectVersion(parsed) || DEFAULT_ROOT_VERSION;
|
|
274
325
|
let rootPurl = this._toPurl(rootName, rootVersion);
|
|
275
326
|
let license = this.readLicenseFromManifest(manifest);
|
|
276
327
|
sbom.addRoot(rootPurl, license);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (!reachable.has(key)) {
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
let entry = graph.get(key);
|
|
284
|
-
sbom.addDependency(rootPurl, this._toPurl(entry.name, entry.version));
|
|
328
|
+
dependencies.forEach(dep => {
|
|
329
|
+
if (includeTransitive) {
|
|
330
|
+
this._addAllDependencies(rootPurl, dep, sbom);
|
|
285
331
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
let parentPurl = this._toPurl(entry.name, entry.version);
|
|
291
|
-
for (let child of entry.children) {
|
|
292
|
-
if (!reachable.has(child)) {
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
let childEntry = graph.get(child);
|
|
296
|
-
sbom.addDependency(parentPurl, this._toPurl(childEntry.name, childEntry.version));
|
|
297
|
-
}
|
|
332
|
+
else {
|
|
333
|
+
sbom.addDependency(rootPurl, this._toPurl(dep.name, dep.version));
|
|
298
334
|
}
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
for (let key of directDeps) {
|
|
302
|
-
if (ignoredDeps.has(key)) {
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
let entry = graph.get(key);
|
|
306
|
-
if (!entry) {
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
sbom.addDependency(rootPurl, this._toPurl(entry.name, entry.version));
|
|
310
|
-
}
|
|
311
|
-
}
|
|
335
|
+
});
|
|
312
336
|
return sbom.getAsJsonString(opts);
|
|
313
337
|
}
|
|
314
338
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trustify-da/trustify-da-javascript-client",
|
|
3
|
-
"version": "0.3.0-ea.
|
|
3
|
+
"version": "0.3.0-ea.3621bb7",
|
|
4
4
|
"description": "Code-Ready Dependency Analytics JavaScript API.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/guacsec/trustify-da-javascript-client#README.md",
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Python provider for pyproject.toml files using PEP 621 format without a lock file.
|
|
3
|
-
* Uses `pip install --dry-run --ignore-installed --report` to resolve the full dependency tree.
|
|
4
|
-
* Acts as the fallback provider when no lock file (uv.lock/poetry.lock) is found.
|
|
5
|
-
*/
|
|
6
|
-
export default class Python_pip_pyproject extends Base_pyproject {
|
|
7
|
-
/**
|
|
8
|
-
* Always returns true — pip provider is the fallback when no lock file is found.
|
|
9
|
-
* @param {string} manifestDir
|
|
10
|
-
* @param {{}} [opts={}]
|
|
11
|
-
* @returns {boolean}
|
|
12
|
-
*/
|
|
13
|
-
validateLockFile(manifestDir: string, opts?: {}): boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Get pip report output from env var override or by running pip.
|
|
16
|
-
* @param {string} manifestDir - directory containing pyproject.toml
|
|
17
|
-
* @param {{}} [opts={}]
|
|
18
|
-
* @returns {string} pip report JSON string
|
|
19
|
-
*/
|
|
20
|
-
_getPipReportOutput(manifestDir: string, opts?: {}): string;
|
|
21
|
-
/**
|
|
22
|
-
* Parse pip report JSON and build dependency graph.
|
|
23
|
-
* @param {string} reportJson - pip report JSON string
|
|
24
|
-
* @returns {{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}}
|
|
25
|
-
*/
|
|
26
|
-
_parsePipReport(reportJson: string): {
|
|
27
|
-
directDeps: string[];
|
|
28
|
-
graph: Map<string, {
|
|
29
|
-
name: string;
|
|
30
|
-
version: string;
|
|
31
|
-
children: string[];
|
|
32
|
-
}>;
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Check if a requires_dist entry is an extras-only dependency.
|
|
36
|
-
* @param {string} req - e.g. "PySocks!=1.5.7,>=1.5.6; extra == \"socks\""
|
|
37
|
-
* @returns {boolean}
|
|
38
|
-
*/
|
|
39
|
-
_hasExtraMarker(req: string): boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Extract package name from a requires_dist entry.
|
|
42
|
-
* @param {string} req - e.g. "charset_normalizer<4,>=2"
|
|
43
|
-
* @returns {string|null}
|
|
44
|
-
*/
|
|
45
|
-
_extractDepName(req: string): string | null;
|
|
46
|
-
/**
|
|
47
|
-
* Resolve dependencies using pip install --dry-run --report.
|
|
48
|
-
* @param {string} manifestDir
|
|
49
|
-
* @param {string} _workspaceDir - unused (pip resolves from manifest directory)
|
|
50
|
-
* @param {object} parsed - parsed pyproject.toml
|
|
51
|
-
* @param {{}} [opts={}]
|
|
52
|
-
* @returns {Promise<{directDeps: string[], graph: Map}>}
|
|
53
|
-
*/
|
|
54
|
-
_getDependencyData(manifestDir: string, _workspaceDir: string, parsed: object, opts?: {}): Promise<{
|
|
55
|
-
directDeps: string[];
|
|
56
|
-
graph: Map<any, any>;
|
|
57
|
-
}>;
|
|
58
|
-
_findEggInfoDirs(dir: any): string[];
|
|
59
|
-
_cleanupEggInfo(dir: any, existing: any): void;
|
|
60
|
-
}
|
|
61
|
-
import Base_pyproject from './base_pyproject.js';
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { environmentVariableIsPopulated, getCustomPath, invokeCommand } from '../tools.js';
|
|
4
|
-
import Base_pyproject from './base_pyproject.js';
|
|
5
|
-
/**
|
|
6
|
-
* Python provider for pyproject.toml files using PEP 621 format without a lock file.
|
|
7
|
-
* Uses `pip install --dry-run --ignore-installed --report` to resolve the full dependency tree.
|
|
8
|
-
* Acts as the fallback provider when no lock file (uv.lock/poetry.lock) is found.
|
|
9
|
-
*/
|
|
10
|
-
export default class Python_pip_pyproject extends Base_pyproject {
|
|
11
|
-
/** @returns {string} */
|
|
12
|
-
_lockFileName() {
|
|
13
|
-
return '.pip-lock-nonexistent';
|
|
14
|
-
}
|
|
15
|
-
/** @returns {string} */
|
|
16
|
-
_cmdName() {
|
|
17
|
-
return 'pip';
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Always returns true — pip provider is the fallback when no lock file is found.
|
|
21
|
-
* @param {string} manifestDir
|
|
22
|
-
* @param {{}} [opts={}]
|
|
23
|
-
* @returns {boolean}
|
|
24
|
-
*/
|
|
25
|
-
// eslint-disable-next-line no-unused-vars
|
|
26
|
-
validateLockFile(manifestDir, opts = {}) {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Get pip report output from env var override or by running pip.
|
|
31
|
-
* @param {string} manifestDir - directory containing pyproject.toml
|
|
32
|
-
* @param {{}} [opts={}]
|
|
33
|
-
* @returns {string} pip report JSON string
|
|
34
|
-
*/
|
|
35
|
-
_getPipReportOutput(manifestDir, opts) {
|
|
36
|
-
if (environmentVariableIsPopulated('TRUSTIFY_DA_PIP_REPORT')) {
|
|
37
|
-
return Buffer.from(process.env['TRUSTIFY_DA_PIP_REPORT'], 'base64').toString('ascii');
|
|
38
|
-
}
|
|
39
|
-
let pipBin = getCustomPath('pip3', opts);
|
|
40
|
-
try {
|
|
41
|
-
invokeCommand(pipBin, ['--version']);
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
pipBin = getCustomPath('pip', opts);
|
|
45
|
-
}
|
|
46
|
-
let eggInfoDirs = this._findEggInfoDirs(manifestDir);
|
|
47
|
-
let result = invokeCommand(pipBin, [
|
|
48
|
-
'install', '--dry-run', '--ignore-installed', '--quiet', '--report', '-', '.'
|
|
49
|
-
], { cwd: manifestDir }).toString();
|
|
50
|
-
this._cleanupEggInfo(manifestDir, eggInfoDirs);
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Parse pip report JSON and build dependency graph.
|
|
55
|
-
* @param {string} reportJson - pip report JSON string
|
|
56
|
-
* @returns {{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}}
|
|
57
|
-
*/
|
|
58
|
-
_parsePipReport(reportJson) {
|
|
59
|
-
let report = JSON.parse(reportJson);
|
|
60
|
-
let packages = report.install || [];
|
|
61
|
-
let rootEntry = packages.find(p => p.download_info?.dir_info !== undefined);
|
|
62
|
-
let rootRequires = rootEntry?.metadata?.requires_dist || [];
|
|
63
|
-
let directDepNames = new Set();
|
|
64
|
-
for (let req of rootRequires) {
|
|
65
|
-
if (this._hasExtraMarker(req)) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
let name = this._extractDepName(req);
|
|
69
|
-
if (name) {
|
|
70
|
-
directDepNames.add(this._canonicalize(name));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
let graph = new Map();
|
|
74
|
-
let nonRootPackages = packages.filter(p => p !== rootEntry);
|
|
75
|
-
for (let pkg of nonRootPackages) {
|
|
76
|
-
let name = pkg.metadata.name;
|
|
77
|
-
let version = pkg.metadata.version;
|
|
78
|
-
let key = this._canonicalize(name);
|
|
79
|
-
graph.set(key, { name, version, children: [] });
|
|
80
|
-
}
|
|
81
|
-
for (let pkg of nonRootPackages) {
|
|
82
|
-
let key = this._canonicalize(pkg.metadata.name);
|
|
83
|
-
let entry = graph.get(key);
|
|
84
|
-
let requires = pkg.metadata.requires_dist || [];
|
|
85
|
-
for (let req of requires) {
|
|
86
|
-
let depName = this._extractDepName(req);
|
|
87
|
-
if (!depName) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
let depKey = this._canonicalize(depName);
|
|
91
|
-
if (graph.has(depKey)) {
|
|
92
|
-
entry.children.push(depKey);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
let directDeps = [...directDepNames].filter(key => graph.has(key));
|
|
97
|
-
return { directDeps, graph };
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Check if a requires_dist entry is an extras-only dependency.
|
|
101
|
-
* @param {string} req - e.g. "PySocks!=1.5.7,>=1.5.6; extra == \"socks\""
|
|
102
|
-
* @returns {boolean}
|
|
103
|
-
*/
|
|
104
|
-
_hasExtraMarker(req) {
|
|
105
|
-
return /;\s*.*extra\s*==/.test(req);
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Extract package name from a requires_dist entry.
|
|
109
|
-
* @param {string} req - e.g. "charset_normalizer<4,>=2"
|
|
110
|
-
* @returns {string|null}
|
|
111
|
-
*/
|
|
112
|
-
_extractDepName(req) {
|
|
113
|
-
let match = req.match(/^([A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?)/);
|
|
114
|
-
return match ? match[1] : null;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Resolve dependencies using pip install --dry-run --report.
|
|
118
|
-
* @param {string} manifestDir
|
|
119
|
-
* @param {string} _workspaceDir - unused (pip resolves from manifest directory)
|
|
120
|
-
* @param {object} parsed - parsed pyproject.toml
|
|
121
|
-
* @param {{}} [opts={}]
|
|
122
|
-
* @returns {Promise<{directDeps: string[], graph: Map}>}
|
|
123
|
-
*/
|
|
124
|
-
// eslint-disable-next-line no-unused-vars
|
|
125
|
-
async _getDependencyData(manifestDir, _workspaceDir, parsed, opts) {
|
|
126
|
-
let reportOutput = this._getPipReportOutput(manifestDir, opts);
|
|
127
|
-
return this._parsePipReport(reportOutput);
|
|
128
|
-
}
|
|
129
|
-
_findEggInfoDirs(dir) {
|
|
130
|
-
try {
|
|
131
|
-
return fs.readdirSync(dir).filter(f => f.endsWith('.egg-info'));
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
return [];
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
_cleanupEggInfo(dir, existing) {
|
|
138
|
-
for (let entry of this._findEggInfoDirs(dir)) {
|
|
139
|
-
if (!existing.includes(entry)) {
|
|
140
|
-
fs.rmSync(path.join(dir, entry), { recursive: true, force: true });
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|