diff --git a/lib/datasource/index.ts b/lib/datasource/index.ts index aa4f88c693a58183305434729a0db72fefc670f2..a8773e176ad7b688a06ab79e846e33c7e7f4cef8 100644 --- a/lib/datasource/index.ts +++ b/lib/datasource/index.ts @@ -1,4 +1,3 @@ -import fs from 'fs'; import { logger } from '../logger'; import { addMetaData } from './metadata'; import * as versioning from '../versioning'; @@ -12,36 +11,11 @@ import { DigestConfig, } from './common'; import { VERSION_SCHEME_SEMVER } from '../constants/version-schemes'; +import { loadModules } from '../util/modules'; export * from './common'; -const datasources: Record<string, Datasource> = {}; - -function isValidDatasourceModule( - datasourceName: string, - module: unknown -): module is Datasource { - return !!module; -} - -function loadDatasources(): void { - const datasourceDirs = fs - .readdirSync(__dirname, { withFileTypes: true }) - .filter(dirent => dirent.isDirectory()) - .map(dirent => dirent.name) - .filter(name => !name.startsWith('__')) - .sort(); - for (const datasourceName of datasourceDirs) { - const module = require(`./${datasourceName}`); // eslint-disable-line - if (isValidDatasourceModule(datasourceName, module)) { - datasources[datasourceName] = module; - } /* istanbul ignore next */ else { - throw new Error(`Datasource module "${datasourceName}" is invalid.`); - } - } -} - -loadDatasources(); +const datasources = loadModules<Datasource>(__dirname); const cacheNamespace = 'datasource-releases'; diff --git a/lib/manager/index.ts b/lib/manager/index.ts index 9a691c0a57f8cf2367473f20cbbddeadec3a5f65..c76274c7403cf5364091eb7aafe5cfec57284d57 100644 --- a/lib/manager/index.ts +++ b/lib/manager/index.ts @@ -1,6 +1,3 @@ -import fs from 'fs'; -import { logger } from '../logger'; - import { ExtractConfig, ManagerApi, @@ -24,37 +21,11 @@ import { LANGUAGE_RUBY, LANGUAGE_RUST, } from '../constants/languages'; +import { loadModules } from '../util/modules'; -const managerList = []; -const managers: Record<string, ManagerApi> = {}; - -function isValidManagerModule(module: unknown): module is ManagerApi { - // TODO: check interface and fail-fast? - return !!module; -} - -function loadManagers(): void { - const managerDirs = fs - .readdirSync(__dirname, { withFileTypes: true }) - .filter(dirent => dirent.isDirectory()) - .map(dirent => dirent.name) - .sort(); - for (const manager of managerDirs) { - let module = null; - try { - module = require(`./${manager}`); // eslint-disable-line - } catch (err) /* istanbul ignore next */ { - logger.fatal({ err }, `Can not load manager "${manager}".`); - process.exit(1); - } +const managers = loadModules<ManagerApi>(__dirname); - if (isValidManagerModule(module)) { - managers[manager] = module; - managerList.push(manager); - } - } -} -loadManagers(); +const managerList = Object.keys(managers); const languageList = [ LANGUAGE_DART, diff --git a/lib/util/modules.ts b/lib/util/modules.ts new file mode 100644 index 0000000000000000000000000000000000000000..f61deada3527dd8f7defdbf77c284b5128aa05f0 --- /dev/null +++ b/lib/util/modules.ts @@ -0,0 +1,46 @@ +import fs from 'fs'; +import { join, normalizeTrim } from 'upath'; + +function relatePath(here: string, there: string): string { + const thereParts = normalizeTrim(there).split(/[\\/]/); + const hereParts = normalizeTrim(here).split(/[\\/]/); + + let idx = 0; + while ( + typeof thereParts[idx] === 'string' && + typeof hereParts[idx] === 'string' && + thereParts[idx] === hereParts[idx] + ) { + idx += 1; + } + + const result = []; + for (let x = 0; x < hereParts.length - idx; x += 1) result.push('..'); + for (let y = idx; y < thereParts.length; y += 1) result.push(thereParts[idx]); + return result.join('/'); +} + +export function loadModules<T>( + dirname: string, + validate?: (x: unknown) => x is T +): Record<string, T> { + const result: Record<string, T> = {}; + + const moduleNames: string[] = fs + .readdirSync(dirname, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => dirent.name) + .filter(name => !name.startsWith('__')) + .sort(); + + for (const moduleName of moduleNames) { + const modulePath = join(relatePath(__dirname, dirname), moduleName); + const module = require(modulePath); // eslint-disable-line + // istanbul ignore if + if (!module || (validate && !validate(module))) + throw new Error(`Invalid module: ${modulePath}`); + result[moduleName] = module as T; + } + + return result; +}