Lines 1-37javascript
1// Auto-discovery — the heart of the core/extension model. A check is ANY .mjs that exports `audit`
2// with an `id` + `run()`. No registry to edit; the file IS the registration. We scan MULTIPLE roots in
3// priority order and merge: later roots OVERRIDE earlier by id, so a user's `your-checks/` check shadows
4// a core check of the same id (patch a core check without touching the core). The folder name = the group.
5import { existsSync } from "node:fs";
6import { basename, dirname } from "node:path";
7import { pathToFileURL } from "node:url";
8import { walkFiles } from "./fs-walk.mjs";
10export async function discoverAudits(roots) {
11 const byId = new Map();
12 const loadErrors = [];
14 for (const root of roots) {
15 if (!root || !existsSync(root)) continue;
16 for (const file of walkFiles(root)) {
17 if (!file.endsWith(".mjs") || file.endsWith(".test.mjs")) continue;
20 mod = await import(pathToFileURL(file).href);
21 } catch (e) {
MediumDynamic Require
Package source references dynamic require/import behavior.
src/core/discovery.mjsView on unpkg · L19 22 // A check that won't even load must be LOUD (crash≠silent-pass), never silently dropped.
23 loadErrors.push({ file, error: String(e?.message || e) });
26 const audit = mod.audit;
27 if (!audit || typeof audit.id !== "string" || typeof audit.run !== "function") continue;
28 if (byId.has(audit.id)) shadowed.push({ id: audit.id, by: file });
30 audit.__group = basename(dirname(file));
32 byId.set(audit.id, audit);
35 return { audits: [...byId.values()], loadErrors, shadowed };