check / scan: project walks skip dot folders, worktrees and git-ignored output, no stat per entry (244 s -> 46 s on a big game repo)
This commit is contained in:
parent
f2d7beb7ce
commit
164ba390dd
6 changed files with 106 additions and 30 deletions
|
|
@ -2,9 +2,10 @@
|
|||
// .xcframework, SwiftPM checkouts, Capacitor / Cordova / React Native plugins in node_modules, a built .app's
|
||||
// Frameworks and bundles) and every PrivacyInfo.xcprivacy inside an .ipa or .app (XML or binary). Read-only; the
|
||||
// directory walk is bounded.
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { type Dirent, readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { basename, join, relative, sep } from 'node:path';
|
||||
import { nativeIosPlugins } from './node-packages';
|
||||
import { skipDir } from './walk-skip';
|
||||
import { parseBinaryPlist } from './plist';
|
||||
import { type Manifest, manifestFromPlist, parseManifest, trackingIssue } from './privacy-manifest';
|
||||
import { zipEntries, zipRead } from './zip';
|
||||
|
|
@ -63,15 +64,16 @@ export function bundleTrackingIssues(list: BundleManifest[]): string[] {
|
|||
|
||||
function walk(dir: string, visit: (p: string, isDir: boolean) => boolean | void, depth = 0, budget = { n: 60_000 }): void {
|
||||
if (depth > 16 || budget.n <= 0) return;
|
||||
let names: string[] = [];
|
||||
try { names = readdirSync(dir).sort(); } catch { return; }
|
||||
for (const n of names) {
|
||||
let ents: Dirent[] = [];
|
||||
try { ents = readdirSync(dir, { withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); } catch { return; }
|
||||
for (const e of ents) {
|
||||
if (--budget.n <= 0) return;
|
||||
const p = join(dir, n);
|
||||
let st;
|
||||
try { st = statSync(p); } catch { continue; }
|
||||
const descend = visit(p, st.isDirectory());
|
||||
if (st.isDirectory() && descend !== false) walk(p, visit, depth + 1, budget);
|
||||
const p = join(dir, e.name);
|
||||
// the entry type comes with the listing (no stat per file: a stat each made a big tree take minutes on Windows)
|
||||
let isDir = e.isDirectory();
|
||||
if (e.isSymbolicLink()) { try { isDir = statSync(p).isDirectory(); } catch { continue; } }
|
||||
const descend = visit(p, isDir);
|
||||
if (isDir && descend !== false) walk(p, visit, depth + 1, budget);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +104,7 @@ export function projectSdkManifests(root: string): SdkManifest[] {
|
|||
try { out.push({ sdk, manifest: manifestFromBytes(relative(root, abs).split(sep).join('/'), readFileSync(abs)) }); } catch { /* unreadable */ }
|
||||
};
|
||||
walk(root, (p, isDir) => {
|
||||
if (isDir) return !SKIP.has(basename(p));
|
||||
if (isDir) return !SKIP.has(basename(p)) && !skipDir(root, p, basename(p));
|
||||
if (basename(p) !== 'PrivacyInfo.xcprivacy') return;
|
||||
const sdk = sdkOfProjectPath(relative(root, p).split(sep).join('/'));
|
||||
if (sdk) add(p, sdk);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue