Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ export const cacheRules = [
},
];

/**
* Find the catalog rule for a path relative to a scan root.
*
* Windows paths are normalized to lowercase to match the platform filesystem's
* case-insensitive behavior.
*
* @param {string} relativePath Path relative to the scan root.
* @returns {{ id: string, label: string, matches: Array<Function> } | undefined}
*/
export function findCacheRule(relativePath) {
const parts = relativePath
.split(/[\\/]+/)
Expand All @@ -53,6 +62,13 @@ export function findCacheRule(relativePath) {
);
}

/**
* Resolve a candidate path into the relative form used by cache catalog checks.
*
* @param {string} root Scan root.
* @param {string} candidate Candidate cache path.
* @returns {string} Candidate path relative to the resolved root.
*/
export function relativeCachePath(root, candidate) {
return path.relative(path.resolve(root), path.resolve(candidate));
}
10 changes: 10 additions & 0 deletions src/cleaner.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ function assertSafeTarget(root, candidate) {
return rule;
}

/**
* Remove cache directories that still match the catalog under the scan root.
*
* Each target is resolved again before deletion so stale scan results cannot
* remove paths outside the root or paths that no longer look like known caches.
*
* @param {string} root Original scan root.
* @param {Array<object>} caches Cache entries returned by the scanner/filter.
* @returns {Promise<Array<object>>} Removal results with `removed` or `failed` status.
*/
export async function removeCaches(root, caches) {
const resolvedRoot = path.resolve(root);
const results = [];
Expand Down
7 changes: 7 additions & 0 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ async function readVersion() {
return packageJson.version;
}

/**
* Execute the CLI command and return a process-style exit code.
*
* @param {string[]} args Raw command-line arguments without the executable name.
* @param {{ log: Function, error: Function }} [io] Output adapter for tests.
* @returns {Promise<number>} Exit code.
*/
export async function run(args, io = console) {
let options;
try {
Expand Down
8 changes: 8 additions & 0 deletions src/filter.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/**
* Apply user-selected type, size, and age constraints to scanned cache entries.
*
* @param {Array<object>} caches Cache entries returned by the scanner.
* @param {{ minSize?: number, olderThan?: number, types?: Set<string> }} options
* @param {number} [now] Timestamp used for age comparisons.
* @returns {Array<object>} Cache entries that match all active filters.
*/
export function filterCaches(
caches,
{ minSize = 0, olderThan = 0, types = new Set() },
Expand Down
7 changes: 7 additions & 0 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ function readValue(args, index, flag) {
return value;
}

/**
* Parse CLI arguments into normalized scan, filter, and output options.
*
* @param {string[]} args Raw command-line arguments without the executable name.
* @param {string} [cwd] Current working directory used to resolve the root.
* @returns {object} Normalized CLI options.
*/
export function parseOptions(args, cwd = process.cwd()) {
const options = {
root: cwd,
Expand Down
18 changes: 18 additions & 0 deletions src/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ function pad(value, width) {
return String(value).padEnd(width);
}

/**
* Build aggregate counts for a cache result set.
*
* @param {Array<object>} caches Cache entries included in a report.
* @returns {{ directories: number, files: number, bytes: number }}
*/
export function createSummary(caches) {
return {
directories: caches.length,
Expand All @@ -12,6 +18,12 @@ export function createSummary(caches) {
};
}

/**
* Render a human-readable scan report for terminal output.
*
* @param {object} report Scan, warning, and optional removal details.
* @returns {string} Formatted text report.
*/
export function formatTextReport({
root,
caches,
Expand Down Expand Up @@ -82,6 +94,12 @@ export function formatTextReport({
return lines.join("\n");
}

/**
* Render a stable JSON report for scripts and CI jobs.
*
* @param {object} report Scan, warning, and optional removal details.
* @returns {string} Pretty-printed JSON report.
*/
export function formatJsonReport({
root,
caches,
Expand Down
10 changes: 10 additions & 0 deletions src/scanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ async function measureDirectory(directory, warnings) {
return measurement;
}

/**
* Walk a repository tree and return directories that match the cache catalog.
*
* The scanner skips symbolic links, avoids `.git`, records read failures as
* warnings, and measures each matched cache before returning it.
*
* @param {string} root Directory to scan.
* @param {{ maxDepth?: number }} [options] Traversal options.
* @returns {Promise<{ root: string, caches: Array<object>, warnings: string[] }>}
*/
export async function scanCaches(root, { maxDepth = 12 } = {}) {
const resolvedRoot = path.resolve(root);
const rootStats = await lstat(resolvedRoot);
Expand Down
25 changes: 25 additions & 0 deletions src/units.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,32 @@ function parseUnitValue(input, units, label) {
return Number(match[1]) * multiplier;
}

/**
* Parse a size string such as `10mb` into bytes.
*
* @param {string} input Size value with a supported unit suffix.
* @returns {number} Rounded byte count.
*/
export function parseSize(input) {
return Math.round(parseUnitValue(input, sizeUnits, "size"));
}

/**
* Parse a duration string such as `7d` into milliseconds.
*
* @param {string} input Duration value with a supported unit suffix.
* @returns {number} Rounded millisecond count.
*/
export function parseDuration(input) {
return Math.round(parseUnitValue(input, durationUnits, "duration"));
}

/**
* Format a byte count for terminal reports.
*
* @param {number} bytes Byte count.
* @returns {string} Human-readable size.
*/
export function formatBytes(bytes) {
if (bytes < 1024) {
return `${bytes} B`;
Expand All @@ -56,6 +74,13 @@ export function formatBytes(bytes) {
return `${value >= 10 ? value.toFixed(1) : value.toFixed(2)} ${unit}`;
}

/**
* Format elapsed time since a cache entry was last modified.
*
* @param {number} modifiedAtMs Last modified timestamp in milliseconds.
* @param {number} [now] Current timestamp for deterministic tests.
* @returns {string} Compact age string.
*/
export function formatAge(modifiedAtMs, now = Date.now()) {
const elapsed = Math.max(0, now - modifiedAtMs);
const hours = Math.floor(elapsed / (60 * 60_000));
Expand Down
Loading