// oxlint-disable-next-line renovate/prefer-fs-util -- synchronously enumerates Renovate's own installed module directories at import time; not repository or cache file access import fs from 'node:fs'; import { pathToFileURL } from 'node:url'; import upath from 'upath'; // TODO: move to `test/util.ts` or `test/modules.ts` export async function loadModules( dirname: string, validate?: (module: T, moduleName: string) => boolean, filter: (moduleName: string) => boolean = () => true, ): Promise> { const result: Record = {}; const moduleNames: string[] = fs .readdirSync(dirname, { withFileTypes: true }) .filter((dirent) => dirent.isDirectory()) .map((dirent) => dirent.name) .filter((name) => !name.startsWith('__')) .filter(filter) .sort(); for (const moduleName of moduleNames) { const modulePath = upath.join(dirname, moduleName); const moduleUrl = pathToFileURL(modulePath).href; const module = await import(moduleUrl); // istanbul ignore if if (!module || (validate && !validate(module, moduleName))) { throw new Error(`Invalid module: ${modulePath}`); } result[moduleName] = module as T; } return result; }