@westbayberry/dg 2.0.10 → 2.0.11
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/scan/collect.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdirSync } from "node:fs";
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
|
+
import { gitIgnoredDirectories } from "./discovery.js";
|
|
3
4
|
import { parseLockfilePackages } from "../verify/preflight.js";
|
|
4
5
|
export const LOCKFILE_ECOSYSTEMS = {
|
|
5
6
|
"package-lock.json": "npm",
|
|
@@ -49,11 +50,15 @@ function lockfilesPerEcosystem(entries) {
|
|
|
49
50
|
}
|
|
50
51
|
return matches;
|
|
51
52
|
}
|
|
52
|
-
function shouldDescend(entry) {
|
|
53
|
-
return entry.isDirectory() &&
|
|
53
|
+
function shouldDescend(entry, directory, gitIgnored) {
|
|
54
|
+
return (entry.isDirectory() &&
|
|
55
|
+
!IGNORED_DIRECTORIES.has(entry.name) &&
|
|
56
|
+
!entry.name.startsWith(".") &&
|
|
57
|
+
!gitIgnored.has(join(directory, entry.name)));
|
|
54
58
|
}
|
|
55
59
|
export function discoverScanProjects(root) {
|
|
56
60
|
const projects = [];
|
|
61
|
+
const gitIgnored = gitIgnoredDirectories(root);
|
|
57
62
|
walk(root, 0);
|
|
58
63
|
return projects;
|
|
59
64
|
function walk(directory, depth) {
|
|
@@ -71,7 +76,7 @@ export function discoverScanProjects(root) {
|
|
|
71
76
|
});
|
|
72
77
|
}
|
|
73
78
|
for (const entry of entries) {
|
|
74
|
-
if (shouldDescend(entry)) {
|
|
79
|
+
if (shouldDescend(entry, directory, gitIgnored)) {
|
|
75
80
|
walk(join(directory, entry.name), depth + 1);
|
|
76
81
|
}
|
|
77
82
|
}
|
|
@@ -79,6 +84,7 @@ export function discoverScanProjects(root) {
|
|
|
79
84
|
}
|
|
80
85
|
export async function discoverScanProjectsAsync(root, onProgress) {
|
|
81
86
|
const projects = [];
|
|
87
|
+
const gitIgnored = gitIgnoredDirectories(root);
|
|
82
88
|
let lastYield = Date.now();
|
|
83
89
|
await walk(root, 0);
|
|
84
90
|
return projects;
|
|
@@ -103,7 +109,7 @@ export async function discoverScanProjectsAsync(root, onProgress) {
|
|
|
103
109
|
onProgress?.({ path: relativePath === "." ? depFile : `${relativePath}/${depFile}`, found: projects.length });
|
|
104
110
|
}
|
|
105
111
|
for (const entry of entries) {
|
|
106
|
-
if (shouldDescend(entry)) {
|
|
112
|
+
if (shouldDescend(entry, directory, gitIgnored)) {
|
|
107
113
|
await walk(join(directory, entry.name), depth + 1);
|
|
108
114
|
}
|
|
109
115
|
}
|
package/dist/scan/discovery.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
1
2
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
3
|
import { basename, dirname, relative, resolve, sep } from "node:path";
|
|
3
4
|
const IGNORED_DIRECTORIES = new Set([
|
|
@@ -75,10 +76,71 @@ function resolveStatus(counts) {
|
|
|
75
76
|
}
|
|
76
77
|
function discoverPackageManifests(root) {
|
|
77
78
|
const manifests = [];
|
|
78
|
-
walk(root, 0, manifests);
|
|
79
|
+
walk(root, 0, manifests, gitIgnoredDirectories(root));
|
|
79
80
|
return manifests.sort((left, right) => displayPath(root, left).localeCompare(displayPath(root, right)));
|
|
80
81
|
}
|
|
81
|
-
function
|
|
82
|
+
export function gitIgnoredDirectories(root) {
|
|
83
|
+
const ignored = new Set();
|
|
84
|
+
if (!insideGitWorkTree(root)) {
|
|
85
|
+
return ignored;
|
|
86
|
+
}
|
|
87
|
+
let level = [root];
|
|
88
|
+
for (let depth = 0; depth <= MAX_DISCOVERY_DEPTH && level.length > 0; depth++) {
|
|
89
|
+
const candidates = [];
|
|
90
|
+
for (const directory of level) {
|
|
91
|
+
let entries;
|
|
92
|
+
try {
|
|
93
|
+
entries = readdirSync(directory, { withFileTypes: true });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
if (entry.isDirectory() && !IGNORED_DIRECTORIES.has(entry.name)) {
|
|
100
|
+
candidates.push(resolve(directory, entry.name));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (candidates.length === 0) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
const flagged = checkIgnoreBatch(root, candidates);
|
|
108
|
+
for (const directory of flagged) {
|
|
109
|
+
ignored.add(directory);
|
|
110
|
+
}
|
|
111
|
+
level = candidates.filter((directory) => !flagged.has(directory));
|
|
112
|
+
}
|
|
113
|
+
return ignored;
|
|
114
|
+
}
|
|
115
|
+
function insideGitWorkTree(root) {
|
|
116
|
+
try {
|
|
117
|
+
const out = execFileSync("git", ["-C", root, "rev-parse", "--is-inside-work-tree"], {
|
|
118
|
+
encoding: "utf8",
|
|
119
|
+
timeout: 3000,
|
|
120
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
121
|
+
});
|
|
122
|
+
return out.trim() === "true";
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function checkIgnoreBatch(root, directories) {
|
|
129
|
+
try {
|
|
130
|
+
const out = execFileSync("git", ["-C", root, "check-ignore", "--stdin", "-z"], {
|
|
131
|
+
input: directories.join("\0"),
|
|
132
|
+
encoding: "utf8",
|
|
133
|
+
timeout: 5000,
|
|
134
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
135
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
136
|
+
});
|
|
137
|
+
return new Set(out.split("\0").filter(Boolean));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return new Set();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function walk(directory, depth, manifests, gitIgnored) {
|
|
82
144
|
if (depth > MAX_DISCOVERY_DEPTH) {
|
|
83
145
|
return;
|
|
84
146
|
}
|
|
@@ -94,8 +156,8 @@ function walk(directory, depth, manifests) {
|
|
|
94
156
|
for (const entry of entries) {
|
|
95
157
|
const absolutePath = resolve(directory, entry.name);
|
|
96
158
|
if (entry.isDirectory()) {
|
|
97
|
-
if (!IGNORED_DIRECTORIES.has(entry.name)) {
|
|
98
|
-
walk(absolutePath, depth + 1, manifests);
|
|
159
|
+
if (!IGNORED_DIRECTORIES.has(entry.name) && !gitIgnored.has(absolutePath)) {
|
|
160
|
+
walk(absolutePath, depth + 1, manifests, gitIgnored);
|
|
99
161
|
}
|
|
100
162
|
continue;
|
|
101
163
|
}
|
|
@@ -1157,7 +1157,7 @@ export const InteractiveResultsView = ({ result, config: _config, durationMs, on
|
|
|
1157
1157
|
: chalk.yellow;
|
|
1158
1158
|
const arrow = level === "summary" ? "\u25BE" : "\u25B8"; // ▾ expanded, ▸ collapsed
|
|
1159
1159
|
return (_jsxs(Box, { flexDirection: "column", children: [isCursor ? (_jsxs(Text, { backgroundColor: "#1a1a2e", wrap: "truncate-end", children: [chalk.cyan("\u258C"), " ", chalk.cyan(arrow), " ", ` `, color(pad(label, BADGE_COL)), chalk.bold(pad(truncate(names, nameCol - 2), nameCol)), lcColor(pad(lcStr, lcCol)), color(scoreStr.padStart(3)), " "] })) : (_jsxs(Text, { wrap: "truncate-end", children: [` ${chalk.dim(arrow)} `, color(pad(label, BADGE_COL)), pad(truncate(names, nameCol - 2), nameCol), lcColor(pad(lcStr, lcCol)), color(scoreStr.padStart(3))] })), level === "summary" && (_jsx(FindingsSummary, { group: group, maxWidth: innerWidth - 8, maxLines: group.key === view.expandedKey ? animVisibleLines : undefined }))] }, group.key));
|
|
1160
|
-
}), belowCount > 0 && (_jsxs(Text, { dimColor: true, children: [chalk.cyan(" \u2193"), " ", belowCount, " more below"] }))] })] })), searchQuery && groups.length === 0 && (_jsx(Text, { dimColor: true, children: ` No packages match "${searchQuery}"` })), !compact && _jsx(Text, { dimColor: true, children: chalk.dim("\u2500".repeat(Math.max(20, termCols - 4))) }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, width: "100%", children: [discoveredTotal !== undefined && discoveredTotal > total && (_jsxs(Text, { dimColor: true, children: ["Scanned ", total, " of ", discoveredTotal, " packages"] })), _jsxs(Box, { justifyContent: "space-between", children: [clean.length > 0 ? (_jsxs(Text, { wrap: "truncate-end", children: [chalk.green("\u2713"), " ", chalk.green.bold(String(clean.length)), " ", chalk.dim(`package${clean.length !== 1 ? "s" : ""} passed`), " ", chalk.dim(`\u00b7 ${(durationMs / 1000).toFixed(1)}s`)] })) : (_jsxs(Text, { dimColor: true, children: [(durationMs / 1000).toFixed(1), "s"] })), result.freeScansRemaining !== undefined && (_jsx(Text, { dimColor: true, children: "Free tier \u00B7 dg login for higher scan limits" }))] })] }), !compact && _jsx(Text, { dimColor: true, children: chalk.dim("\u2500".repeat(Math.max(20, termCols - 4))) }), searchMode ? (_jsxs(Text, { wrap: "truncate-end", children: [" ", chalk.bold.cyan("/"), " ", searchQuery, chalk.cyan("\u2588"), " ", chalk.dim("Esc clear")] })) : searchQuery ? (_jsxs(Text, { wrap: "truncate-end", children: [" ", chalk.bold.cyan("/"), " ", searchQuery, " ", chalk.dim(`${matchCount} of ${total} packages`), " ", chalk.bold.cyan("Esc"), " ", chalk.dim("clear"), " ", chalk.bold.cyan("q"), " ", chalk.dim("quit")
|
|
1160
|
+
}), belowCount > 0 && (_jsxs(Text, { dimColor: true, children: [chalk.cyan(" \u2193"), " ", belowCount, " more below"] }))] })] })), searchQuery && groups.length === 0 && (_jsx(Text, { dimColor: true, children: ` No packages match "${searchQuery}"` })), !compact && _jsx(Text, { dimColor: true, children: chalk.dim("\u2500".repeat(Math.max(20, termCols - 4))) }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, width: "100%", children: [discoveredTotal !== undefined && discoveredTotal > total && (_jsxs(Text, { dimColor: true, children: ["Scanned ", total, " of ", discoveredTotal, " packages"] })), _jsxs(Box, { justifyContent: "space-between", children: [clean.length > 0 ? (_jsxs(Text, { wrap: "truncate-end", children: [chalk.green("\u2713"), " ", chalk.green.bold(String(clean.length)), " ", chalk.dim(`package${clean.length !== 1 ? "s" : ""} passed`), " ", chalk.dim(`\u00b7 ${(durationMs / 1000).toFixed(1)}s`)] })) : (_jsxs(Text, { dimColor: true, children: [(durationMs / 1000).toFixed(1), "s"] })), result.freeScansRemaining !== undefined && (_jsx(Text, { dimColor: true, children: "Free tier \u00B7 dg login for higher scan limits" }))] })] }), !compact && _jsx(Text, { dimColor: true, children: chalk.dim("\u2500".repeat(Math.max(20, termCols - 4))) }), searchMode ? (_jsxs(Text, { wrap: "truncate-end", children: [" ", chalk.bold.cyan("/"), " ", searchQuery, chalk.cyan("\u2588"), " ", chalk.dim("Esc clear")] })) : exportMsg ? (_jsxs(Text, { wrap: "truncate-end", children: [" ", chalk.green(exportMsg)] })) : searchQuery ? (_jsxs(Text, { wrap: "truncate-end", children: [" ", chalk.bold.cyan("/"), " ", searchQuery, " ", chalk.dim(`${matchCount} of ${total} packages`), " ", chalk.bold.cyan("Esc"), " ", chalk.dim("clear"), " ", chalk.bold.cyan("q"), " ", chalk.dim("quit")] })) : (_jsxs(Text, { wrap: "truncate-end", children: [" ", groups.length > 0 && (_jsxs(_Fragment, { children: [chalk.bold.cyan("\u2191\u2193"), " ", chalk.dim("navigate"), " ", chalk.bold.cyan("\u23CE"), " ", chalk.dim("expand"), " "] })), total > 0 && (_jsxs(_Fragment, { children: [chalk.bold.cyan("/"), " ", chalk.dim("search"), " "] })), chalk.bold.cyan("l"), " ", chalk.dim("licenses"), " ", chalk.bold.cyan("e"), " ", chalk.dim("export"), " ", onBack && _jsxs(_Fragment, { children: [chalk.bold.cyan("Esc"), " ", chalk.dim("back"), " "] }), chalk.bold.cyan("q"), " ", chalk.dim("quit")] }))] }));
|
|
1161
1161
|
};
|
|
1162
1162
|
const T = {
|
|
1163
1163
|
branch: chalk.dim("\u251C\u2500\u2500"),
|
package/dist/scan-ui/launch.js
CHANGED
|
@@ -31,11 +31,14 @@ export async function launchScanTui(initialView = "results") {
|
|
|
31
31
|
? `Update available: ${update.current} → ${update.latest} · run dg update`
|
|
32
32
|
: undefined;
|
|
33
33
|
enterTui();
|
|
34
|
+
const instance = render(react.default.createElement(app.App, { config, initialView, updateAvailable }), { exitOnCtrlC: true });
|
|
35
|
+
const clearStaleFrameOnResize = () => instance.clear();
|
|
36
|
+
process.stdout.on("resize", clearStaleFrameOnResize);
|
|
34
37
|
try {
|
|
35
|
-
const instance = render(react.default.createElement(app.App, { config, initialView, updateAvailable }), { exitOnCtrlC: true });
|
|
36
38
|
await instance.waitUntilExit();
|
|
37
39
|
}
|
|
38
40
|
finally {
|
|
41
|
+
process.stdout.off("resize", clearStaleFrameOnResize);
|
|
39
42
|
leaveTui();
|
|
40
43
|
}
|
|
41
44
|
}
|