From 23e254eee903d686e23eb0bff9e11beff42e3be5 Mon Sep 17 00:00:00 2001
From: Sergio Zharinov <zharinov@users.noreply.github.com>
Date: Sun, 16 Feb 2020 08:57:12 +0400
Subject: [PATCH] refactor: Extract module loading code out to utils (#5444)

---
 lib/datasource/index.ts | 30 ++-------------------------
 lib/manager/index.ts    | 35 +++----------------------------
 lib/util/modules.ts     | 46 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 51 insertions(+), 60 deletions(-)
 create mode 100644 lib/util/modules.ts

diff --git a/lib/datasource/index.ts b/lib/datasource/index.ts
index aa4f88c693..a8773e176a 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 9a691c0a57..c76274c740 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 0000000000..f61deada35
--- /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;
+}
-- 
GitLab