@vibgrate/cli 1.0.15 → 1.0.17
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.
|
@@ -23,7 +23,7 @@ var Semaphore = class {
|
|
|
23
23
|
this.available--;
|
|
24
24
|
return Promise.resolve();
|
|
25
25
|
}
|
|
26
|
-
return new Promise((
|
|
26
|
+
return new Promise((resolve7) => this.queue.push(resolve7));
|
|
27
27
|
}
|
|
28
28
|
release() {
|
|
29
29
|
const next = this.queue.shift();
|
|
@@ -50,6 +50,140 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
50
50
|
"packages",
|
|
51
51
|
"TestResults"
|
|
52
52
|
]);
|
|
53
|
+
var TEXT_CACHE_MAX_BYTES = 1048576;
|
|
54
|
+
var FileCache = class _FileCache {
|
|
55
|
+
/** Directory walk results keyed by rootDir */
|
|
56
|
+
walkCache = /* @__PURE__ */ new Map();
|
|
57
|
+
/** File content keyed by absolute path (only files ≤ TEXT_CACHE_MAX_BYTES) */
|
|
58
|
+
textCache = /* @__PURE__ */ new Map();
|
|
59
|
+
/** Parsed JSON keyed by absolute path */
|
|
60
|
+
jsonCache = /* @__PURE__ */ new Map();
|
|
61
|
+
/** pathExists keyed by absolute path */
|
|
62
|
+
existsCache = /* @__PURE__ */ new Map();
|
|
63
|
+
// ── Directory walking ──
|
|
64
|
+
/**
|
|
65
|
+
* Walk the directory tree from `rootDir` once, skipping SKIP_DIRS plus
|
|
66
|
+
* common framework output dirs (.nuxt, .output, .svelte-kit).
|
|
67
|
+
*
|
|
68
|
+
* The result is memoised so every scanner filters the same array.
|
|
69
|
+
* Consumers that need additional filtering (e.g. SOURCE_EXTENSIONS,
|
|
70
|
+
* SKIP_EXTENSIONS) do so on the returned entries — no separate walk.
|
|
71
|
+
*/
|
|
72
|
+
walkDir(rootDir) {
|
|
73
|
+
const cached = this.walkCache.get(rootDir);
|
|
74
|
+
if (cached) return cached;
|
|
75
|
+
const promise = this._doWalk(rootDir);
|
|
76
|
+
this.walkCache.set(rootDir, promise);
|
|
77
|
+
return promise;
|
|
78
|
+
}
|
|
79
|
+
/** Additional dirs skipped only by the cached walk (framework outputs) */
|
|
80
|
+
static EXTRA_SKIP = /* @__PURE__ */ new Set([".nuxt", ".output", ".svelte-kit"]);
|
|
81
|
+
async _doWalk(rootDir) {
|
|
82
|
+
const results = [];
|
|
83
|
+
const maxConcurrentReads = Math.max(8, Math.min(64, os.availableParallelism() * 4));
|
|
84
|
+
const sem = new Semaphore(maxConcurrentReads);
|
|
85
|
+
const extraSkip = _FileCache.EXTRA_SKIP;
|
|
86
|
+
async function walk(dir) {
|
|
87
|
+
let entries;
|
|
88
|
+
try {
|
|
89
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
90
|
+
} catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const subWalks = [];
|
|
94
|
+
for (const e of entries) {
|
|
95
|
+
const absPath = path.join(dir, e.name);
|
|
96
|
+
const relPath = path.relative(rootDir, absPath);
|
|
97
|
+
if (e.isDirectory()) {
|
|
98
|
+
if (SKIP_DIRS.has(e.name) || extraSkip.has(e.name)) continue;
|
|
99
|
+
results.push({ absPath, relPath, name: e.name, isFile: false, isDirectory: true });
|
|
100
|
+
subWalks.push(sem.run(() => walk(absPath)));
|
|
101
|
+
} else if (e.isFile()) {
|
|
102
|
+
results.push({ absPath, relPath, name: e.name, isFile: true, isDirectory: false });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
await Promise.all(subWalks);
|
|
106
|
+
}
|
|
107
|
+
await sem.run(() => walk(rootDir));
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Find files matching a predicate from the cached walk.
|
|
112
|
+
* Returns absolute paths (same contract as the standalone `findFiles`).
|
|
113
|
+
*/
|
|
114
|
+
async findFiles(rootDir, predicate) {
|
|
115
|
+
const entries = await this.walkDir(rootDir);
|
|
116
|
+
return entries.filter((e) => e.isFile && predicate(e.name)).map((e) => e.absPath);
|
|
117
|
+
}
|
|
118
|
+
async findPackageJsonFiles(rootDir) {
|
|
119
|
+
return this.findFiles(rootDir, (name) => name === "package.json");
|
|
120
|
+
}
|
|
121
|
+
async findCsprojFiles(rootDir) {
|
|
122
|
+
return this.findFiles(rootDir, (name) => name.endsWith(".csproj"));
|
|
123
|
+
}
|
|
124
|
+
async findSolutionFiles(rootDir) {
|
|
125
|
+
return this.findFiles(rootDir, (name) => name.endsWith(".sln"));
|
|
126
|
+
}
|
|
127
|
+
// ── File content reading ──
|
|
128
|
+
/**
|
|
129
|
+
* Read a text file. Files ≤ 1 MB are cached so subsequent calls from
|
|
130
|
+
* different scanners return the same string. Files > 1 MB (lockfiles,
|
|
131
|
+
* large generated files) are read directly and never retained.
|
|
132
|
+
*/
|
|
133
|
+
readTextFile(filePath) {
|
|
134
|
+
const abs = path.resolve(filePath);
|
|
135
|
+
const cached = this.textCache.get(abs);
|
|
136
|
+
if (cached) return cached;
|
|
137
|
+
const promise = fs.readFile(abs, "utf8").then((content) => {
|
|
138
|
+
if (content.length > TEXT_CACHE_MAX_BYTES) {
|
|
139
|
+
this.textCache.delete(abs);
|
|
140
|
+
}
|
|
141
|
+
return content;
|
|
142
|
+
});
|
|
143
|
+
this.textCache.set(abs, promise);
|
|
144
|
+
return promise;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Read and parse a JSON file. The parsed object is cached; the raw
|
|
148
|
+
* text is evicted immediately so we never hold both representations.
|
|
149
|
+
*/
|
|
150
|
+
readJsonFile(filePath) {
|
|
151
|
+
const abs = path.resolve(filePath);
|
|
152
|
+
const cached = this.jsonCache.get(abs);
|
|
153
|
+
if (cached) return cached;
|
|
154
|
+
const promise = this.readTextFile(abs).then((txt) => {
|
|
155
|
+
this.textCache.delete(abs);
|
|
156
|
+
return JSON.parse(txt);
|
|
157
|
+
});
|
|
158
|
+
this.jsonCache.set(abs, promise);
|
|
159
|
+
return promise;
|
|
160
|
+
}
|
|
161
|
+
// ── Existence checks ──
|
|
162
|
+
pathExists(p) {
|
|
163
|
+
const abs = path.resolve(p);
|
|
164
|
+
const cached = this.existsCache.get(abs);
|
|
165
|
+
if (cached) return cached;
|
|
166
|
+
const promise = fs.access(abs).then(() => true, () => false);
|
|
167
|
+
this.existsCache.set(abs, promise);
|
|
168
|
+
return promise;
|
|
169
|
+
}
|
|
170
|
+
// ── Lifecycle ──
|
|
171
|
+
/** Release all cached data. Call after the scan completes. */
|
|
172
|
+
clear() {
|
|
173
|
+
this.walkCache.clear();
|
|
174
|
+
this.textCache.clear();
|
|
175
|
+
this.jsonCache.clear();
|
|
176
|
+
this.existsCache.clear();
|
|
177
|
+
}
|
|
178
|
+
/** Number of file content entries currently held */
|
|
179
|
+
get textCacheSize() {
|
|
180
|
+
return this.textCache.size;
|
|
181
|
+
}
|
|
182
|
+
/** Number of parsed JSON entries currently held */
|
|
183
|
+
get jsonCacheSize() {
|
|
184
|
+
return this.jsonCache.size;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
53
187
|
async function findFiles(rootDir, predicate) {
|
|
54
188
|
const results = [];
|
|
55
189
|
const maxConcurrentReads = Math.max(8, Math.min(64, os.availableParallelism() * 4));
|
|
@@ -396,6 +530,9 @@ function formatText(artifact) {
|
|
|
396
530
|
lines.push("");
|
|
397
531
|
}
|
|
398
532
|
}
|
|
533
|
+
if (artifact.extended?.architecture) {
|
|
534
|
+
lines.push(...formatArchitectureDiagram(artifact.extended.architecture));
|
|
535
|
+
}
|
|
399
536
|
const scoreColor = artifact.drift.score >= 70 ? chalk.green : artifact.drift.score >= 40 ? chalk.yellow : chalk.red;
|
|
400
537
|
lines.push(chalk.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
401
538
|
lines.push(chalk.bold.cyan("\u2551 Drift Score Summary \u2551"));
|
|
@@ -585,9 +722,6 @@ function formatExtended(ext) {
|
|
|
585
722
|
lines.push("");
|
|
586
723
|
}
|
|
587
724
|
}
|
|
588
|
-
if (ext.architecture) {
|
|
589
|
-
lines.push(...formatArchitectureDiagram(ext.architecture));
|
|
590
|
-
}
|
|
591
725
|
return lines;
|
|
592
726
|
}
|
|
593
727
|
var LAYER_LABELS = {
|
|
@@ -1186,7 +1320,7 @@ function maxStable(versions) {
|
|
|
1186
1320
|
return stable.sort(semver.rcompare)[0] ?? null;
|
|
1187
1321
|
}
|
|
1188
1322
|
async function npmViewJson(args, cwd) {
|
|
1189
|
-
return new Promise((
|
|
1323
|
+
return new Promise((resolve7, reject) => {
|
|
1190
1324
|
const child = spawn("npm", ["view", ...args, "--json"], {
|
|
1191
1325
|
cwd,
|
|
1192
1326
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -1203,13 +1337,13 @@ async function npmViewJson(args, cwd) {
|
|
|
1203
1337
|
}
|
|
1204
1338
|
const trimmed = out.trim();
|
|
1205
1339
|
if (!trimmed) {
|
|
1206
|
-
|
|
1340
|
+
resolve7(null);
|
|
1207
1341
|
return;
|
|
1208
1342
|
}
|
|
1209
1343
|
try {
|
|
1210
|
-
|
|
1344
|
+
resolve7(JSON.parse(trimmed));
|
|
1211
1345
|
} catch {
|
|
1212
|
-
|
|
1346
|
+
resolve7(trimmed.replace(/^"|"$/g, ""));
|
|
1213
1347
|
}
|
|
1214
1348
|
});
|
|
1215
1349
|
});
|
|
@@ -1345,12 +1479,12 @@ var KNOWN_FRAMEWORKS = {
|
|
|
1345
1479
|
"storybook": "Storybook",
|
|
1346
1480
|
"@storybook/react": "Storybook"
|
|
1347
1481
|
};
|
|
1348
|
-
async function scanNodeProjects(rootDir, npmCache) {
|
|
1349
|
-
const packageJsonFiles = await findPackageJsonFiles(rootDir);
|
|
1482
|
+
async function scanNodeProjects(rootDir, npmCache, cache) {
|
|
1483
|
+
const packageJsonFiles = cache ? await cache.findPackageJsonFiles(rootDir) : await findPackageJsonFiles(rootDir);
|
|
1350
1484
|
const results = [];
|
|
1351
1485
|
for (const pjPath of packageJsonFiles) {
|
|
1352
1486
|
try {
|
|
1353
|
-
const scan = await scanOnePackageJson(pjPath, rootDir, npmCache);
|
|
1487
|
+
const scan = await scanOnePackageJson(pjPath, rootDir, npmCache, cache);
|
|
1354
1488
|
results.push(scan);
|
|
1355
1489
|
} catch (e) {
|
|
1356
1490
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -1359,8 +1493,8 @@ async function scanNodeProjects(rootDir, npmCache) {
|
|
|
1359
1493
|
}
|
|
1360
1494
|
return results;
|
|
1361
1495
|
}
|
|
1362
|
-
async function scanOnePackageJson(packageJsonPath, rootDir, npmCache) {
|
|
1363
|
-
const pj = await readJsonFile(packageJsonPath);
|
|
1496
|
+
async function scanOnePackageJson(packageJsonPath, rootDir, npmCache, cache) {
|
|
1497
|
+
const pj = cache ? await cache.readJsonFile(packageJsonPath) : await readJsonFile(packageJsonPath);
|
|
1364
1498
|
const absProjectPath = path4.dirname(packageJsonPath);
|
|
1365
1499
|
const projectPath = path4.relative(rootDir, absProjectPath) || ".";
|
|
1366
1500
|
const nodeEngine = pj.engines?.node ?? void 0;
|
|
@@ -1687,13 +1821,13 @@ function parseCsproj(xml, filePath) {
|
|
|
1687
1821
|
projectName: path5.basename(filePath, ".csproj")
|
|
1688
1822
|
};
|
|
1689
1823
|
}
|
|
1690
|
-
async function scanDotnetProjects(rootDir) {
|
|
1691
|
-
const csprojFiles = await findCsprojFiles(rootDir);
|
|
1692
|
-
const slnFiles = await findSolutionFiles(rootDir);
|
|
1824
|
+
async function scanDotnetProjects(rootDir, cache) {
|
|
1825
|
+
const csprojFiles = cache ? await cache.findCsprojFiles(rootDir) : await findCsprojFiles(rootDir);
|
|
1826
|
+
const slnFiles = cache ? await cache.findSolutionFiles(rootDir) : await findSolutionFiles(rootDir);
|
|
1693
1827
|
const slnCsprojPaths = /* @__PURE__ */ new Set();
|
|
1694
1828
|
for (const slnPath of slnFiles) {
|
|
1695
1829
|
try {
|
|
1696
|
-
const slnContent = await readTextFile(slnPath);
|
|
1830
|
+
const slnContent = cache ? await cache.readTextFile(slnPath) : await readTextFile(slnPath);
|
|
1697
1831
|
const slnDir = path5.dirname(slnPath);
|
|
1698
1832
|
const projectRegex = /Project\("[^"]*"\)\s*=\s*"[^"]*",\s*"([^"]+\.csproj)"/g;
|
|
1699
1833
|
let match;
|
|
@@ -1710,7 +1844,7 @@ async function scanDotnetProjects(rootDir) {
|
|
|
1710
1844
|
const results = [];
|
|
1711
1845
|
for (const csprojPath of allCsprojFiles) {
|
|
1712
1846
|
try {
|
|
1713
|
-
const scan = await scanOneCsproj(csprojPath, rootDir);
|
|
1847
|
+
const scan = await scanOneCsproj(csprojPath, rootDir, cache);
|
|
1714
1848
|
results.push(scan);
|
|
1715
1849
|
} catch (e) {
|
|
1716
1850
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -1719,8 +1853,8 @@ async function scanDotnetProjects(rootDir) {
|
|
|
1719
1853
|
}
|
|
1720
1854
|
return results;
|
|
1721
1855
|
}
|
|
1722
|
-
async function scanOneCsproj(csprojPath, rootDir) {
|
|
1723
|
-
const xml = await readTextFile(csprojPath);
|
|
1856
|
+
async function scanOneCsproj(csprojPath, rootDir, cache) {
|
|
1857
|
+
const xml = cache ? await cache.readTextFile(csprojPath) : await readTextFile(csprojPath);
|
|
1724
1858
|
const data = parseCsproj(xml, csprojPath);
|
|
1725
1859
|
const primaryTfm = data.targetFrameworks[0];
|
|
1726
1860
|
let runtimeMajorsBehind;
|
|
@@ -2328,7 +2462,7 @@ var OS_PATTERNS = [
|
|
|
2328
2462
|
{ pattern: /\bbash\b|#!\/bin\/bash/i, label: "bash-scripts" },
|
|
2329
2463
|
{ pattern: /\\\\/g, label: "backslash-paths" }
|
|
2330
2464
|
];
|
|
2331
|
-
async function scanPlatformMatrix(rootDir) {
|
|
2465
|
+
async function scanPlatformMatrix(rootDir, cache) {
|
|
2332
2466
|
const result = {
|
|
2333
2467
|
dotnetTargetFrameworks: [],
|
|
2334
2468
|
nativeModules: [],
|
|
@@ -2336,12 +2470,12 @@ async function scanPlatformMatrix(rootDir) {
|
|
|
2336
2470
|
dockerBaseImages: [],
|
|
2337
2471
|
nodeVersionFiles: []
|
|
2338
2472
|
};
|
|
2339
|
-
const pkgFiles = await findPackageJsonFiles(rootDir);
|
|
2473
|
+
const pkgFiles = cache ? await cache.findPackageJsonFiles(rootDir) : await findPackageJsonFiles(rootDir);
|
|
2340
2474
|
const allDeps = /* @__PURE__ */ new Set();
|
|
2341
2475
|
const osAssumptions = /* @__PURE__ */ new Set();
|
|
2342
2476
|
for (const pjPath of pkgFiles) {
|
|
2343
2477
|
try {
|
|
2344
|
-
const pj = await readJsonFile(pjPath);
|
|
2478
|
+
const pj = cache ? await cache.readJsonFile(pjPath) : await readJsonFile(pjPath);
|
|
2345
2479
|
if (pj.engines?.node && !result.nodeEngines) result.nodeEngines = pj.engines.node;
|
|
2346
2480
|
if (pj.engines?.npm && !result.npmEngines) result.npmEngines = pj.engines.npm;
|
|
2347
2481
|
if (pj.engines?.pnpm && !result.pnpmEngines) {
|
|
@@ -2377,11 +2511,11 @@ async function scanPlatformMatrix(rootDir) {
|
|
|
2377
2511
|
}
|
|
2378
2512
|
result.nativeModules.sort();
|
|
2379
2513
|
result.osAssumptions = [...osAssumptions].sort();
|
|
2380
|
-
const csprojFiles = await findFiles(rootDir, (name) => name.endsWith(".csproj"));
|
|
2514
|
+
const csprojFiles = cache ? await cache.findCsprojFiles(rootDir) : await findFiles(rootDir, (name) => name.endsWith(".csproj"));
|
|
2381
2515
|
const tfms = /* @__PURE__ */ new Set();
|
|
2382
2516
|
for (const csprojPath of csprojFiles) {
|
|
2383
2517
|
try {
|
|
2384
|
-
const xml = await readTextFile(csprojPath);
|
|
2518
|
+
const xml = cache ? await cache.readTextFile(csprojPath) : await readTextFile(csprojPath);
|
|
2385
2519
|
const tfMatch = xml.match(/<TargetFramework>(.*?)<\/TargetFramework>/);
|
|
2386
2520
|
if (tfMatch?.[1]) tfms.add(tfMatch[1]);
|
|
2387
2521
|
const tfsMatch = xml.match(/<TargetFrameworks>(.*?)<\/TargetFrameworks>/);
|
|
@@ -2394,14 +2528,17 @@ async function scanPlatformMatrix(rootDir) {
|
|
|
2394
2528
|
}
|
|
2395
2529
|
}
|
|
2396
2530
|
result.dotnetTargetFrameworks = [...tfms].sort();
|
|
2397
|
-
const dockerfiles = await findFiles(
|
|
2531
|
+
const dockerfiles = cache ? await cache.findFiles(
|
|
2532
|
+
rootDir,
|
|
2533
|
+
(name) => name === "Dockerfile" || name.startsWith("Dockerfile.")
|
|
2534
|
+
) : await findFiles(
|
|
2398
2535
|
rootDir,
|
|
2399
2536
|
(name) => name === "Dockerfile" || name.startsWith("Dockerfile.")
|
|
2400
2537
|
);
|
|
2401
2538
|
const baseImages = /* @__PURE__ */ new Set();
|
|
2402
2539
|
for (const df of dockerfiles) {
|
|
2403
2540
|
try {
|
|
2404
|
-
const content = await readTextFile(df);
|
|
2541
|
+
const content = cache ? await cache.readTextFile(df) : await readTextFile(df);
|
|
2405
2542
|
for (const line of content.split("\n")) {
|
|
2406
2543
|
const trimmed = line.trim();
|
|
2407
2544
|
if (/^FROM\s+/i.test(trimmed)) {
|
|
@@ -2419,7 +2556,8 @@ async function scanPlatformMatrix(rootDir) {
|
|
|
2419
2556
|
}
|
|
2420
2557
|
result.dockerBaseImages = [...baseImages].sort();
|
|
2421
2558
|
for (const file of [".nvmrc", ".node-version", ".tool-versions"]) {
|
|
2422
|
-
|
|
2559
|
+
const exists = cache ? await cache.pathExists(path8.join(rootDir, file)) : await pathExists(path8.join(rootDir, file));
|
|
2560
|
+
if (exists) {
|
|
2423
2561
|
result.nodeVersionFiles.push(file);
|
|
2424
2562
|
}
|
|
2425
2563
|
}
|
|
@@ -2545,7 +2683,7 @@ function parseYarnLock(content) {
|
|
|
2545
2683
|
}
|
|
2546
2684
|
return entries;
|
|
2547
2685
|
}
|
|
2548
|
-
async function scanDependencyGraph(rootDir) {
|
|
2686
|
+
async function scanDependencyGraph(rootDir, cache) {
|
|
2549
2687
|
const result = {
|
|
2550
2688
|
lockfileType: null,
|
|
2551
2689
|
totalUnique: 0,
|
|
@@ -2557,17 +2695,19 @@ async function scanDependencyGraph(rootDir) {
|
|
|
2557
2695
|
const pnpmLock = path9.join(rootDir, "pnpm-lock.yaml");
|
|
2558
2696
|
const npmLock = path9.join(rootDir, "package-lock.json");
|
|
2559
2697
|
const yarnLock = path9.join(rootDir, "yarn.lock");
|
|
2560
|
-
|
|
2698
|
+
const _pathExists = cache ? (p) => cache.pathExists(p) : pathExists;
|
|
2699
|
+
const _readTextFile = cache ? (p) => cache.readTextFile(p) : readTextFile;
|
|
2700
|
+
if (await _pathExists(pnpmLock)) {
|
|
2561
2701
|
result.lockfileType = "pnpm";
|
|
2562
|
-
const content = await
|
|
2702
|
+
const content = await _readTextFile(pnpmLock);
|
|
2563
2703
|
entries = parsePnpmLock(content);
|
|
2564
|
-
} else if (await
|
|
2704
|
+
} else if (await _pathExists(npmLock)) {
|
|
2565
2705
|
result.lockfileType = "npm";
|
|
2566
|
-
const content = await
|
|
2706
|
+
const content = await _readTextFile(npmLock);
|
|
2567
2707
|
entries = parseNpmLock(content);
|
|
2568
|
-
} else if (await
|
|
2708
|
+
} else if (await _pathExists(yarnLock)) {
|
|
2569
2709
|
result.lockfileType = "yarn";
|
|
2570
|
-
const content = await
|
|
2710
|
+
const content = await _readTextFile(yarnLock);
|
|
2571
2711
|
entries = parseYarnLock(content);
|
|
2572
2712
|
}
|
|
2573
2713
|
if (entries.length === 0) return result;
|
|
@@ -2595,12 +2735,12 @@ async function scanDependencyGraph(rootDir) {
|
|
|
2595
2735
|
duplicated.sort((a, b) => b.versions.length - a.versions.length || a.name.localeCompare(b.name));
|
|
2596
2736
|
result.duplicatedPackages = duplicated;
|
|
2597
2737
|
const lockedNames = new Set(versionMap.keys());
|
|
2598
|
-
const pkgFiles = await findPackageJsonFiles(rootDir);
|
|
2738
|
+
const pkgFiles = cache ? await cache.findPackageJsonFiles(rootDir) : await findPackageJsonFiles(rootDir);
|
|
2599
2739
|
const phantoms = /* @__PURE__ */ new Set();
|
|
2600
2740
|
const phantomDetails = [];
|
|
2601
2741
|
for (const pjPath of pkgFiles) {
|
|
2602
2742
|
try {
|
|
2603
|
-
const pj = await readJsonFile(pjPath);
|
|
2743
|
+
const pj = cache ? await cache.readJsonFile(pjPath) : await readJsonFile(pjPath);
|
|
2604
2744
|
const relPath = path9.relative(rootDir, pjPath);
|
|
2605
2745
|
for (const section of ["dependencies", "devDependencies"]) {
|
|
2606
2746
|
const deps = pj[section];
|
|
@@ -2985,7 +3125,7 @@ var IAC_EXTENSIONS = {
|
|
|
2985
3125
|
".tf": "terraform",
|
|
2986
3126
|
".bicep": "bicep"
|
|
2987
3127
|
};
|
|
2988
|
-
async function scanBuildDeploy(rootDir) {
|
|
3128
|
+
async function scanBuildDeploy(rootDir, cache) {
|
|
2989
3129
|
const result = {
|
|
2990
3130
|
ci: [],
|
|
2991
3131
|
ciWorkflowCount: 0,
|
|
@@ -2995,26 +3135,37 @@ async function scanBuildDeploy(rootDir) {
|
|
|
2995
3135
|
packageManagers: [],
|
|
2996
3136
|
monorepoTools: []
|
|
2997
3137
|
};
|
|
3138
|
+
const _pathExists = cache ? (p) => cache.pathExists(p) : pathExists;
|
|
3139
|
+
const _findFiles = cache ? (dir, pred) => cache.findFiles(dir, pred) : findFiles;
|
|
3140
|
+
const _readTextFile = cache ? (p) => cache.readTextFile(p) : readTextFile;
|
|
2998
3141
|
const ciSystems = /* @__PURE__ */ new Set();
|
|
2999
3142
|
for (const [file, system] of Object.entries(CI_FILES)) {
|
|
3000
3143
|
const fullPath = path10.join(rootDir, file);
|
|
3001
|
-
if (await
|
|
3144
|
+
if (await _pathExists(fullPath)) {
|
|
3002
3145
|
ciSystems.add(system);
|
|
3003
3146
|
}
|
|
3004
3147
|
}
|
|
3005
3148
|
const ghWorkflowDir = path10.join(rootDir, ".github", "workflows");
|
|
3006
|
-
if (await
|
|
3149
|
+
if (await _pathExists(ghWorkflowDir)) {
|
|
3007
3150
|
try {
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3151
|
+
if (cache) {
|
|
3152
|
+
const entries = await cache.walkDir(rootDir);
|
|
3153
|
+
const ghPrefix = path10.relative(rootDir, ghWorkflowDir) + path10.sep;
|
|
3154
|
+
result.ciWorkflowCount = entries.filter(
|
|
3155
|
+
(e) => e.isFile && e.relPath.startsWith(ghPrefix) && (e.name.endsWith(".yml") || e.name.endsWith(".yaml"))
|
|
3156
|
+
).length;
|
|
3157
|
+
} else {
|
|
3158
|
+
const files = await _findFiles(
|
|
3159
|
+
ghWorkflowDir,
|
|
3160
|
+
(name) => name.endsWith(".yml") || name.endsWith(".yaml")
|
|
3161
|
+
);
|
|
3162
|
+
result.ciWorkflowCount = files.length;
|
|
3163
|
+
}
|
|
3013
3164
|
} catch {
|
|
3014
3165
|
}
|
|
3015
3166
|
}
|
|
3016
3167
|
result.ci = [...ciSystems].sort();
|
|
3017
|
-
const dockerfiles = await
|
|
3168
|
+
const dockerfiles = await _findFiles(
|
|
3018
3169
|
rootDir,
|
|
3019
3170
|
(name) => name === "Dockerfile" || name.startsWith("Dockerfile.")
|
|
3020
3171
|
);
|
|
@@ -3022,7 +3173,7 @@ async function scanBuildDeploy(rootDir) {
|
|
|
3022
3173
|
const baseImages = /* @__PURE__ */ new Set();
|
|
3023
3174
|
for (const df of dockerfiles) {
|
|
3024
3175
|
try {
|
|
3025
|
-
const content = await
|
|
3176
|
+
const content = await _readTextFile(df);
|
|
3026
3177
|
for (const line of content.split("\n")) {
|
|
3027
3178
|
const trimmed = line.trim();
|
|
3028
3179
|
if (/^FROM\s+/i.test(trimmed)) {
|
|
@@ -3042,24 +3193,24 @@ async function scanBuildDeploy(rootDir) {
|
|
|
3042
3193
|
result.docker.baseImages = [...baseImages].sort();
|
|
3043
3194
|
const iacSystems = /* @__PURE__ */ new Set();
|
|
3044
3195
|
for (const [ext, system] of Object.entries(IAC_EXTENSIONS)) {
|
|
3045
|
-
const files = await
|
|
3196
|
+
const files = await _findFiles(rootDir, (name) => name.endsWith(ext));
|
|
3046
3197
|
if (files.length > 0) iacSystems.add(system);
|
|
3047
3198
|
}
|
|
3048
|
-
const cfnFiles = await
|
|
3199
|
+
const cfnFiles = await _findFiles(
|
|
3049
3200
|
rootDir,
|
|
3050
3201
|
(name) => name.endsWith(".cfn.json") || name.endsWith(".cfn.yaml")
|
|
3051
3202
|
);
|
|
3052
3203
|
if (cfnFiles.length > 0) iacSystems.add("cloudformation");
|
|
3053
|
-
if (await
|
|
3204
|
+
if (await _pathExists(path10.join(rootDir, "Pulumi.yaml"))) iacSystems.add("pulumi");
|
|
3054
3205
|
result.iac = [...iacSystems].sort();
|
|
3055
3206
|
const releaseTools = /* @__PURE__ */ new Set();
|
|
3056
3207
|
for (const [file, tool] of Object.entries(RELEASE_FILES)) {
|
|
3057
|
-
if (await
|
|
3208
|
+
if (await _pathExists(path10.join(rootDir, file))) releaseTools.add(tool);
|
|
3058
3209
|
}
|
|
3059
|
-
const pkgFiles = await findPackageJsonFiles(rootDir);
|
|
3210
|
+
const pkgFiles = cache ? await cache.findPackageJsonFiles(rootDir) : await findPackageJsonFiles(rootDir);
|
|
3060
3211
|
for (const pjPath of pkgFiles) {
|
|
3061
3212
|
try {
|
|
3062
|
-
const pj = await readJsonFile(pjPath);
|
|
3213
|
+
const pj = cache ? await cache.readJsonFile(pjPath) : await readJsonFile(pjPath);
|
|
3063
3214
|
for (const section of ["dependencies", "devDependencies"]) {
|
|
3064
3215
|
const deps = pj[section];
|
|
3065
3216
|
if (!deps) continue;
|
|
@@ -3079,12 +3230,12 @@ async function scanBuildDeploy(rootDir) {
|
|
|
3079
3230
|
};
|
|
3080
3231
|
const managers = /* @__PURE__ */ new Set();
|
|
3081
3232
|
for (const [file, manager] of Object.entries(lockfileMap)) {
|
|
3082
|
-
if (await
|
|
3233
|
+
if (await _pathExists(path10.join(rootDir, file))) managers.add(manager);
|
|
3083
3234
|
}
|
|
3084
3235
|
result.packageManagers = [...managers].sort();
|
|
3085
3236
|
const monoTools = /* @__PURE__ */ new Set();
|
|
3086
3237
|
for (const [file, tool] of Object.entries(MONOREPO_FILES)) {
|
|
3087
|
-
if (await
|
|
3238
|
+
if (await _pathExists(path10.join(rootDir, file))) monoTools.add(tool);
|
|
3088
3239
|
}
|
|
3089
3240
|
result.monorepoTools = [...monoTools].sort();
|
|
3090
3241
|
return result;
|
|
@@ -3092,7 +3243,7 @@ async function scanBuildDeploy(rootDir) {
|
|
|
3092
3243
|
|
|
3093
3244
|
// src/scanners/ts-modernity.ts
|
|
3094
3245
|
import * as path11 from "path";
|
|
3095
|
-
async function scanTsModernity(rootDir) {
|
|
3246
|
+
async function scanTsModernity(rootDir, cache) {
|
|
3096
3247
|
const result = {
|
|
3097
3248
|
typescriptVersion: null,
|
|
3098
3249
|
strict: null,
|
|
@@ -3104,12 +3255,12 @@ async function scanTsModernity(rootDir) {
|
|
|
3104
3255
|
moduleType: null,
|
|
3105
3256
|
exportsField: false
|
|
3106
3257
|
};
|
|
3107
|
-
const pkgFiles = await findPackageJsonFiles(rootDir);
|
|
3258
|
+
const pkgFiles = cache ? await cache.findPackageJsonFiles(rootDir) : await findPackageJsonFiles(rootDir);
|
|
3108
3259
|
let hasEsm = false;
|
|
3109
3260
|
let hasCjs = false;
|
|
3110
3261
|
for (const pjPath of pkgFiles) {
|
|
3111
3262
|
try {
|
|
3112
|
-
const pj = await readJsonFile(pjPath);
|
|
3263
|
+
const pj = cache ? await cache.readJsonFile(pjPath) : await readJsonFile(pjPath);
|
|
3113
3264
|
if (!result.typescriptVersion) {
|
|
3114
3265
|
const tsVer = pj.devDependencies?.["typescript"] ?? pj.dependencies?.["typescript"];
|
|
3115
3266
|
if (tsVer) {
|
|
@@ -3130,8 +3281,9 @@ async function scanTsModernity(rootDir) {
|
|
|
3130
3281
|
else if (hasEsm) result.moduleType = "esm";
|
|
3131
3282
|
else if (hasCjs) result.moduleType = "cjs";
|
|
3132
3283
|
let tsConfigPath = path11.join(rootDir, "tsconfig.json");
|
|
3133
|
-
|
|
3134
|
-
|
|
3284
|
+
const tsConfigExists = cache ? await cache.pathExists(tsConfigPath) : await pathExists(tsConfigPath);
|
|
3285
|
+
if (!tsConfigExists) {
|
|
3286
|
+
const tsConfigs = cache ? await cache.findFiles(rootDir, (name) => name === "tsconfig.json") : await findFiles(rootDir, (name) => name === "tsconfig.json");
|
|
3135
3287
|
if (tsConfigs.length > 0) {
|
|
3136
3288
|
tsConfigPath = tsConfigs[0];
|
|
3137
3289
|
} else {
|
|
@@ -3139,7 +3291,7 @@ async function scanTsModernity(rootDir) {
|
|
|
3139
3291
|
}
|
|
3140
3292
|
}
|
|
3141
3293
|
try {
|
|
3142
|
-
const raw = await readTextFile(tsConfigPath);
|
|
3294
|
+
const raw = cache ? await cache.readTextFile(tsConfigPath) : await readTextFile(tsConfigPath);
|
|
3143
3295
|
const stripped = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,(\s*[}\]])/g, "$1");
|
|
3144
3296
|
const tsConfig = JSON.parse(stripped);
|
|
3145
3297
|
const co = tsConfig?.compilerOptions;
|
|
@@ -3510,43 +3662,63 @@ var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
3510
3662
|
".mp4",
|
|
3511
3663
|
".webm"
|
|
3512
3664
|
]);
|
|
3513
|
-
async function scanFileHotspots(rootDir) {
|
|
3665
|
+
async function scanFileHotspots(rootDir, cache) {
|
|
3514
3666
|
const extensionCounts = {};
|
|
3515
3667
|
const allFiles = [];
|
|
3516
3668
|
let maxDepth = 0;
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
const
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3669
|
+
if (cache) {
|
|
3670
|
+
const entries = await cache.walkDir(rootDir);
|
|
3671
|
+
for (const entry of entries) {
|
|
3672
|
+
if (!entry.isFile) continue;
|
|
3673
|
+
const ext = path12.extname(entry.name).toLowerCase();
|
|
3674
|
+
if (SKIP_EXTENSIONS.has(ext)) continue;
|
|
3675
|
+
const depth = entry.relPath.split(path12.sep).length - 1;
|
|
3676
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
3677
|
+
extensionCounts[ext] = (extensionCounts[ext] ?? 0) + 1;
|
|
3678
|
+
try {
|
|
3679
|
+
const stat3 = await fs4.stat(entry.absPath);
|
|
3680
|
+
allFiles.push({
|
|
3681
|
+
path: entry.relPath,
|
|
3682
|
+
bytes: stat3.size
|
|
3683
|
+
});
|
|
3684
|
+
} catch {
|
|
3685
|
+
}
|
|
3529
3686
|
}
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
const
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3687
|
+
} else {
|
|
3688
|
+
async function walk(dir, depth) {
|
|
3689
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
3690
|
+
let entries;
|
|
3691
|
+
try {
|
|
3692
|
+
const dirents = await fs4.readdir(dir, { withFileTypes: true });
|
|
3693
|
+
entries = dirents.map((d) => ({
|
|
3694
|
+
name: d.name,
|
|
3695
|
+
isDirectory: d.isDirectory(),
|
|
3696
|
+
isFile: d.isFile()
|
|
3697
|
+
}));
|
|
3698
|
+
} catch {
|
|
3699
|
+
return;
|
|
3700
|
+
}
|
|
3701
|
+
for (const e of entries) {
|
|
3702
|
+
if (e.isDirectory) {
|
|
3703
|
+
if (SKIP_DIRS2.has(e.name)) continue;
|
|
3704
|
+
await walk(path12.join(dir, e.name), depth + 1);
|
|
3705
|
+
} else if (e.isFile) {
|
|
3706
|
+
const ext = path12.extname(e.name).toLowerCase();
|
|
3707
|
+
if (SKIP_EXTENSIONS.has(ext)) continue;
|
|
3708
|
+
extensionCounts[ext] = (extensionCounts[ext] ?? 0) + 1;
|
|
3709
|
+
try {
|
|
3710
|
+
const stat3 = await fs4.stat(path12.join(dir, e.name));
|
|
3711
|
+
allFiles.push({
|
|
3712
|
+
path: path12.relative(rootDir, path12.join(dir, e.name)),
|
|
3713
|
+
bytes: stat3.size
|
|
3714
|
+
});
|
|
3715
|
+
} catch {
|
|
3716
|
+
}
|
|
3545
3717
|
}
|
|
3546
3718
|
}
|
|
3547
3719
|
}
|
|
3720
|
+
await walk(rootDir, 0);
|
|
3548
3721
|
}
|
|
3549
|
-
await walk(rootDir, 0);
|
|
3550
3722
|
allFiles.sort((a, b) => b.bytes - a.bytes);
|
|
3551
3723
|
const largestFiles = allFiles.slice(0, 20);
|
|
3552
3724
|
return {
|
|
@@ -3568,7 +3740,7 @@ var LOCKFILES = {
|
|
|
3568
3740
|
"bun.lockb": "bun",
|
|
3569
3741
|
"packages.lock.json": "nuget"
|
|
3570
3742
|
};
|
|
3571
|
-
async function scanSecurityPosture(rootDir) {
|
|
3743
|
+
async function scanSecurityPosture(rootDir, cache) {
|
|
3572
3744
|
const result = {
|
|
3573
3745
|
lockfilePresent: false,
|
|
3574
3746
|
multipleLockfileTypes: false,
|
|
@@ -3577,9 +3749,11 @@ async function scanSecurityPosture(rootDir) {
|
|
|
3577
3749
|
envFilesTracked: false,
|
|
3578
3750
|
lockfileTypes: []
|
|
3579
3751
|
};
|
|
3752
|
+
const _pathExists = cache ? (p) => cache.pathExists(p) : pathExists;
|
|
3753
|
+
const _readTextFile = cache ? (p) => cache.readTextFile(p) : readTextFile;
|
|
3580
3754
|
const foundLockfiles = [];
|
|
3581
3755
|
for (const [file, type] of Object.entries(LOCKFILES)) {
|
|
3582
|
-
if (await
|
|
3756
|
+
if (await _pathExists(path13.join(rootDir, file))) {
|
|
3583
3757
|
foundLockfiles.push(type);
|
|
3584
3758
|
}
|
|
3585
3759
|
}
|
|
@@ -3587,9 +3761,9 @@ async function scanSecurityPosture(rootDir) {
|
|
|
3587
3761
|
result.multipleLockfileTypes = foundLockfiles.length > 1;
|
|
3588
3762
|
result.lockfileTypes = foundLockfiles.sort();
|
|
3589
3763
|
const gitignorePath = path13.join(rootDir, ".gitignore");
|
|
3590
|
-
if (await
|
|
3764
|
+
if (await _pathExists(gitignorePath)) {
|
|
3591
3765
|
try {
|
|
3592
|
-
const content = await
|
|
3766
|
+
const content = await _readTextFile(gitignorePath);
|
|
3593
3767
|
const lines = content.split("\n").map((l) => l.trim());
|
|
3594
3768
|
result.gitignoreCoversEnv = lines.some(
|
|
3595
3769
|
(line) => line === ".env" || line === ".env*" || line === ".env.*" || line === ".env.local" || line === "*.env"
|
|
@@ -3601,7 +3775,7 @@ async function scanSecurityPosture(rootDir) {
|
|
|
3601
3775
|
}
|
|
3602
3776
|
}
|
|
3603
3777
|
for (const envFile of [".env", ".env.local", ".env.development", ".env.production"]) {
|
|
3604
|
-
if (await
|
|
3778
|
+
if (await _pathExists(path13.join(rootDir, envFile))) {
|
|
3605
3779
|
if (!result.gitignoreCoversEnv) {
|
|
3606
3780
|
result.envFilesTracked = true;
|
|
3607
3781
|
break;
|
|
@@ -4320,7 +4494,17 @@ var PACKAGE_LAYER_MAP = {
|
|
|
4320
4494
|
};
|
|
4321
4495
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cts", ".cjs", ".svelte", ".vue"]);
|
|
4322
4496
|
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".nuxt", ".output", ".svelte-kit", "coverage", ".vibgrate"]);
|
|
4323
|
-
async function walkSourceFiles(rootDir) {
|
|
4497
|
+
async function walkSourceFiles(rootDir, cache) {
|
|
4498
|
+
if (cache) {
|
|
4499
|
+
const entries = await cache.walkDir(rootDir);
|
|
4500
|
+
return entries.filter((e) => {
|
|
4501
|
+
if (!e.isFile) return false;
|
|
4502
|
+
const name = path14.basename(e.absPath);
|
|
4503
|
+
if (name.startsWith(".") && name !== ".") return false;
|
|
4504
|
+
const ext = path14.extname(name);
|
|
4505
|
+
return SOURCE_EXTENSIONS.has(ext);
|
|
4506
|
+
}).map((e) => e.relPath);
|
|
4507
|
+
}
|
|
4324
4508
|
const files = [];
|
|
4325
4509
|
async function walk(dir) {
|
|
4326
4510
|
let entries;
|
|
@@ -4446,9 +4630,9 @@ function mapToolingToLayers(tooling, services, depsByLayer) {
|
|
|
4446
4630
|
}
|
|
4447
4631
|
return { layerTooling, layerServices };
|
|
4448
4632
|
}
|
|
4449
|
-
async function scanArchitecture(rootDir, projects, tooling, services) {
|
|
4633
|
+
async function scanArchitecture(rootDir, projects, tooling, services, cache) {
|
|
4450
4634
|
const { archetype, confidence: archetypeConfidence } = detectArchetype(projects);
|
|
4451
|
-
const sourceFiles = await walkSourceFiles(rootDir);
|
|
4635
|
+
const sourceFiles = await walkSourceFiles(rootDir, cache);
|
|
4452
4636
|
const classifications = [];
|
|
4453
4637
|
let unclassified = 0;
|
|
4454
4638
|
for (const file of sourceFiles) {
|
|
@@ -4553,6 +4737,7 @@ async function runScan(rootDir, opts) {
|
|
|
4553
4737
|
const config = await loadConfig(rootDir);
|
|
4554
4738
|
const sem = new Semaphore(opts.concurrency);
|
|
4555
4739
|
const npmCache = new NpmCache(rootDir, sem);
|
|
4740
|
+
const fileCache = new FileCache();
|
|
4556
4741
|
const scanners = config.scanners;
|
|
4557
4742
|
let filesScanned = 0;
|
|
4558
4743
|
const progress = new ScanProgress(rootDir);
|
|
@@ -4584,7 +4769,7 @@ async function runScan(rootDir, opts) {
|
|
|
4584
4769
|
const vcsDetail = vcs.type !== "unknown" ? `${vcs.type}${vcs.branch ? ` ${vcs.branch}` : ""}${vcs.shortSha ? ` @ ${vcs.shortSha}` : ""}` : "none detected";
|
|
4585
4770
|
progress.completeStep("vcs", vcsDetail);
|
|
4586
4771
|
progress.startStep("node");
|
|
4587
|
-
const nodeProjects = await scanNodeProjects(rootDir, npmCache);
|
|
4772
|
+
const nodeProjects = await scanNodeProjects(rootDir, npmCache, fileCache);
|
|
4588
4773
|
for (const p of nodeProjects) {
|
|
4589
4774
|
progress.addDependencies(p.dependencies.length);
|
|
4590
4775
|
progress.addFrameworks(p.frameworks.length);
|
|
@@ -4593,7 +4778,7 @@ async function runScan(rootDir, opts) {
|
|
|
4593
4778
|
progress.addProjects(nodeProjects.length);
|
|
4594
4779
|
progress.completeStep("node", `${nodeProjects.length} project${nodeProjects.length !== 1 ? "s" : ""}`, nodeProjects.length);
|
|
4595
4780
|
progress.startStep("dotnet");
|
|
4596
|
-
const dotnetProjects = await scanDotnetProjects(rootDir);
|
|
4781
|
+
const dotnetProjects = await scanDotnetProjects(rootDir, fileCache);
|
|
4597
4782
|
for (const p of dotnetProjects) {
|
|
4598
4783
|
progress.addDependencies(p.dependencies.length);
|
|
4599
4784
|
progress.addFrameworks(p.frameworks.length);
|
|
@@ -4615,7 +4800,7 @@ async function runScan(rootDir, opts) {
|
|
|
4615
4800
|
if (scanners?.platformMatrix?.enabled !== false) {
|
|
4616
4801
|
progress.startStep("platform");
|
|
4617
4802
|
scannerTasks.push(
|
|
4618
|
-
scanPlatformMatrix(rootDir).then((result) => {
|
|
4803
|
+
scanPlatformMatrix(rootDir, fileCache).then((result) => {
|
|
4619
4804
|
extended.platformMatrix = result;
|
|
4620
4805
|
const nativeCount = result.nativeModules.length;
|
|
4621
4806
|
const dockerCount = result.dockerBaseImages.length;
|
|
@@ -4664,7 +4849,7 @@ async function runScan(rootDir, opts) {
|
|
|
4664
4849
|
if (scanners?.securityPosture?.enabled !== false) {
|
|
4665
4850
|
progress.startStep("security");
|
|
4666
4851
|
scannerTasks.push(
|
|
4667
|
-
scanSecurityPosture(rootDir).then((result) => {
|
|
4852
|
+
scanSecurityPosture(rootDir, fileCache).then((result) => {
|
|
4668
4853
|
extended.securityPosture = result;
|
|
4669
4854
|
const secDetail = result.lockfilePresent ? `lockfile \u2714${result.gitignoreCoversEnv ? " \xB7 .env \u2714" : " \xB7 .env \u2716"}` : "no lockfile";
|
|
4670
4855
|
progress.completeStep("security", secDetail);
|
|
@@ -4674,7 +4859,7 @@ async function runScan(rootDir, opts) {
|
|
|
4674
4859
|
if (scanners?.buildDeploy?.enabled !== false) {
|
|
4675
4860
|
progress.startStep("build");
|
|
4676
4861
|
scannerTasks.push(
|
|
4677
|
-
scanBuildDeploy(rootDir).then((result) => {
|
|
4862
|
+
scanBuildDeploy(rootDir, fileCache).then((result) => {
|
|
4678
4863
|
extended.buildDeploy = result;
|
|
4679
4864
|
const bdParts = [];
|
|
4680
4865
|
if (result.ci.length > 0) bdParts.push(result.ci.join(", "));
|
|
@@ -4686,7 +4871,7 @@ async function runScan(rootDir, opts) {
|
|
|
4686
4871
|
if (scanners?.tsModernity?.enabled !== false) {
|
|
4687
4872
|
progress.startStep("ts");
|
|
4688
4873
|
scannerTasks.push(
|
|
4689
|
-
scanTsModernity(rootDir).then((result) => {
|
|
4874
|
+
scanTsModernity(rootDir, fileCache).then((result) => {
|
|
4690
4875
|
extended.tsModernity = result;
|
|
4691
4876
|
const tsParts = [];
|
|
4692
4877
|
if (result.typescriptVersion) tsParts.push(`v${result.typescriptVersion}`);
|
|
@@ -4699,7 +4884,7 @@ async function runScan(rootDir, opts) {
|
|
|
4699
4884
|
if (scanners?.fileHotspots?.enabled !== false) {
|
|
4700
4885
|
progress.startStep("hotspots");
|
|
4701
4886
|
scannerTasks.push(
|
|
4702
|
-
scanFileHotspots(rootDir).then((result) => {
|
|
4887
|
+
scanFileHotspots(rootDir, fileCache).then((result) => {
|
|
4703
4888
|
extended.fileHotspots = result;
|
|
4704
4889
|
progress.completeStep("hotspots", `${result.totalFiles} files`, result.totalFiles);
|
|
4705
4890
|
})
|
|
@@ -4708,7 +4893,7 @@ async function runScan(rootDir, opts) {
|
|
|
4708
4893
|
if (scanners?.dependencyGraph?.enabled !== false) {
|
|
4709
4894
|
progress.startStep("depgraph");
|
|
4710
4895
|
scannerTasks.push(
|
|
4711
|
-
scanDependencyGraph(rootDir).then((result) => {
|
|
4896
|
+
scanDependencyGraph(rootDir, fileCache).then((result) => {
|
|
4712
4897
|
extended.dependencyGraph = result;
|
|
4713
4898
|
const dgDetail = result.lockfileType ? `${result.lockfileType} \xB7 ${result.totalUnique} unique` : "no lockfile";
|
|
4714
4899
|
progress.completeStep("depgraph", dgDetail, result.totalUnique);
|
|
@@ -4735,7 +4920,8 @@ async function runScan(rootDir, opts) {
|
|
|
4735
4920
|
rootDir,
|
|
4736
4921
|
allProjects,
|
|
4737
4922
|
extended.toolingInventory,
|
|
4738
|
-
extended.serviceDependencies
|
|
4923
|
+
extended.serviceDependencies,
|
|
4924
|
+
fileCache
|
|
4739
4925
|
);
|
|
4740
4926
|
const arch = extended.architecture;
|
|
4741
4927
|
const layerCount = arch.layers.filter((l) => l.fileCount > 0).length;
|
|
@@ -4761,6 +4947,7 @@ async function runScan(rootDir, opts) {
|
|
|
4761
4947
|
if (noteCount > 0) findingParts.push(`${noteCount} note${noteCount !== 1 ? "s" : ""}`);
|
|
4762
4948
|
progress.completeStep("findings", findingParts.join(", ") || "none");
|
|
4763
4949
|
progress.finish();
|
|
4950
|
+
fileCache.clear();
|
|
4764
4951
|
if (allProjects.length === 0) {
|
|
4765
4952
|
console.log(chalk5.yellow("No projects found."));
|
|
4766
4953
|
}
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-VXZT34Y5.js";
|
|
5
5
|
import {
|
|
6
6
|
baselineCommand
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XVGUFKAC.js";
|
|
8
8
|
import {
|
|
9
9
|
VERSION,
|
|
10
10
|
dsnCommand,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
readJsonFile,
|
|
16
16
|
scanCommand,
|
|
17
17
|
writeDefaultConfig
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-EVLKMTYE.js";
|
|
19
19
|
|
|
20
20
|
// src/cli.ts
|
|
21
21
|
import { Command as Command4 } from "commander";
|
|
@@ -38,7 +38,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
38
38
|
console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
|
|
39
39
|
}
|
|
40
40
|
if (opts.baseline) {
|
|
41
|
-
const { runBaseline } = await import("./baseline-
|
|
41
|
+
const { runBaseline } = await import("./baseline-TJLWFSMH.js");
|
|
42
42
|
await runBaseline(rootDir);
|
|
43
43
|
}
|
|
44
44
|
console.log("");
|
package/dist/index.js
CHANGED