From 9c1b7d78cd9054ded00ee179ecbe97fad10a46b1 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@keylocation.sg>
Date: Fri, 3 Nov 2017 09:06:42 +0100
Subject: [PATCH] refactor: move monorepos logic to npm manager (#1091)

* refactor: move monorepos logic to npm manager

* fix test
---
 lib/manager/npm/monorepos.js                  | 56 +++++++++++++
 lib/workers/repository/apis.js                | 56 -------------
 lib/workers/repository/index.js               |  4 +-
 .../npm/__snapshots__/monorepo.spec.js.snap   | 24 ++++++
 test/manager/npm/monorepo.spec.js             | 80 +++++++++++++++++++
 .../__snapshots__/apis.spec.js.snap           | 23 ------
 test/workers/repository/apis.spec.js          | 80 -------------------
 7 files changed, 163 insertions(+), 160 deletions(-)
 create mode 100644 lib/manager/npm/monorepos.js
 create mode 100644 test/manager/npm/__snapshots__/monorepo.spec.js.snap
 create mode 100644 test/manager/npm/monorepo.spec.js

diff --git a/lib/manager/npm/monorepos.js b/lib/manager/npm/monorepos.js
new file mode 100644
index 0000000000..a0a5870c01
--- /dev/null
+++ b/lib/manager/npm/monorepos.js
@@ -0,0 +1,56 @@
+const minimatch = require('minimatch');
+const path = require('path');
+
+module.exports = {
+  checkMonorepos,
+};
+
+async function checkMonorepos(config) {
+  const { logger } = config;
+  const monorepoPackages = [];
+  let workspaceDir;
+  // yarn workspaces
+  let workspaces = [];
+  for (const packageFile of config.packageFiles) {
+    if (
+      packageFile.packageFile &&
+      packageFile.packageFile.endsWith('package.json') &&
+      packageFile.content.workspaces
+    ) {
+      workspaceDir = path.dirname(packageFile.packageFile);
+      logger.info(`workspaceDir=${config.workspaceDir}`);
+      ({ workspaces } = packageFile.content);
+    }
+  }
+  if (workspaces.length) {
+    logger.debug({ workspaces }, 'Found yarn workspaces');
+  }
+  for (const workspace of workspaces) {
+    const basePath = path.join(workspaceDir, workspace);
+    logger.info(`basePath=${basePath}`);
+    for (const packageFile of config.packageFiles) {
+      if (minimatch(path.dirname(packageFile.packageFile), basePath)) {
+        logger.info(`Matched ${packageFile.packageFile}`);
+        const depName = packageFile.content.name;
+        monorepoPackages.push(depName);
+        packageFile.workspaceDir = workspaceDir;
+      }
+    }
+  }
+  // lerna
+  const lernaJson = await config.api.getFileJson('lerna.json');
+  if (lernaJson && lernaJson.packages) {
+    logger.debug({ lernaJson }, 'Found lerna config');
+    for (const packageGlob of lernaJson.packages) {
+      for (const packageFile of config.packageFiles) {
+        if (minimatch(path.dirname(packageFile.packageFile), packageGlob)) {
+          const depName = packageFile.content.name;
+          if (!monorepoPackages.includes(depName)) {
+            monorepoPackages.push(depName);
+          }
+        }
+      }
+    }
+  }
+  return { ...config, monorepoPackages, workspaceDir };
+}
diff --git a/lib/workers/repository/apis.js b/lib/workers/repository/apis.js
index 5c76652271..e78af34e8b 100644
--- a/lib/workers/repository/apis.js
+++ b/lib/workers/repository/apis.js
@@ -1,4 +1,3 @@
-const minimatch = require('minimatch');
 const conventionalCommitsDetector = require('conventional-commits-detector');
 const path = require('path');
 const jsonValidator = require('json-dup-key-validator');
@@ -14,7 +13,6 @@ const { migrateAndValidate } = require('../../config/migrate-validate');
 
 module.exports = {
   detectSemanticCommits,
-  checkMonorepos,
   getNpmrc,
   initApis,
   mergeRenovateJson,
@@ -36,60 +34,6 @@ async function detectSemanticCommits(config) {
   return true;
 }
 
-async function checkMonorepos(input) {
-  const config = { ...input };
-  const { logger } = config;
-  config.monorepoPackages = [];
-  // yarn workspaces
-  let workspaces = [];
-  for (const packageFile of config.packageFiles) {
-    if (
-      packageFile.packageFile &&
-      packageFile.packageFile.endsWith('package.json') &&
-      packageFile.content.workspaces
-    ) {
-      config.workspaceDir = path.dirname(packageFile.packageFile);
-      logger.info(`workspaceDir=${config.workspaceDir}`);
-      ({ workspaces } = packageFile.content);
-    }
-  }
-  if (workspaces.length) {
-    logger.debug({ workspaces }, 'Found yarn workspaces');
-  }
-  for (const workspace of workspaces) {
-    const basePath = path.join(config.workspaceDir, workspace);
-    logger.info(`basePath=${basePath}`);
-    for (const packageFile of config.packageFiles) {
-      if (minimatch(path.dirname(packageFile.packageFile), basePath)) {
-        logger.info(`Matched ${packageFile.packageFile}`);
-        const depName = packageFile.content.name;
-        config.monorepoPackages.push(depName);
-        packageFile.workspaceDir = config.workspaceDir;
-      }
-    }
-  }
-  // lerna
-  const lernaJson = await config.api.getFileJson('lerna.json');
-  if (!lernaJson) {
-    return config;
-  }
-  logger.debug({ lernaJson }, 'Found lerna config');
-  if (!lernaJson.packages) {
-    return config;
-  }
-  for (const packageGlob of lernaJson.packages) {
-    for (const packageFile of config.packageFiles) {
-      if (minimatch(path.dirname(packageFile.packageFile), packageGlob)) {
-        const depName = packageFile.content.name;
-        if (!config.monorepoPackages.includes(depName)) {
-          config.monorepoPackages.push(depName);
-        }
-      }
-    }
-  }
-  return config;
-}
-
 // Check for .npmrc in repository and pass it to npm api if found
 async function getNpmrc(config) {
   if (config.ignoreNpmrcFile) {
diff --git a/lib/workers/repository/index.js b/lib/workers/repository/index.js
index 73d9b14079..a65e7c28ba 100644
--- a/lib/workers/repository/index.js
+++ b/lib/workers/repository/index.js
@@ -9,6 +9,8 @@ const onboarding = require('./onboarding');
 const upgrades = require('./upgrades');
 const cleanup = require('./cleanup');
 
+const { checkMonorepos } = require('../../manager/npm/monorepos');
+
 module.exports = {
   pinDependenciesFirst,
   renovateRepository,
@@ -104,7 +106,7 @@ async function renovateRepository(repoConfig, token) {
     }
     logger.debug('Resolving package files and content');
     config = await apis.resolvePackageFiles(config);
-    config = await apis.checkMonorepos(config);
+    config = await checkMonorepos(config);
     logger.trace({ config }, 'post-packageFiles config');
     // TODO: why is this fix needed?!
     config.logger = logger;
diff --git a/test/manager/npm/__snapshots__/monorepo.spec.js.snap b/test/manager/npm/__snapshots__/monorepo.spec.js.snap
new file mode 100644
index 0000000000..95ba2aea02
--- /dev/null
+++ b/test/manager/npm/__snapshots__/monorepo.spec.js.snap
@@ -0,0 +1,24 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`manager/npm/monorepo checkMonorepos adds lerna packages 1`] = `
+Array [
+  "@a/b",
+  "@a/c",
+]
+`;
+
+exports[`manager/npm/monorepo checkMonorepos adds nested yarn workspaces 1`] = `
+Array [
+  "@a/b",
+  "@a/c",
+]
+`;
+
+exports[`manager/npm/monorepo checkMonorepos adds yarn workspaces 1`] = `
+Array [
+  "@a/b",
+  "@a/c",
+]
+`;
+
+exports[`manager/npm/monorepo checkMonorepos skips if no lerna packages 1`] = `Array []`;
diff --git a/test/manager/npm/monorepo.spec.js b/test/manager/npm/monorepo.spec.js
new file mode 100644
index 0000000000..4a4339a41f
--- /dev/null
+++ b/test/manager/npm/monorepo.spec.js
@@ -0,0 +1,80 @@
+const { checkMonorepos } = require('../../../lib/manager/npm/monorepos');
+
+let config;
+beforeEach(() => {
+  jest.resetAllMocks();
+  config = { ...require('../../_fixtures/config') };
+  config.errors = [];
+  config.warnings = [];
+});
+
+describe('manager/npm/monorepo', () => {
+  describe('checkMonorepos', () => {
+    it('adds yarn workspaces', async () => {
+      config.packageFiles = [
+        {
+          packageFile: 'package.json',
+          content: { workspaces: ['packages/*'] },
+        },
+        {
+          packageFile: 'packages/something/package.json',
+          content: { name: '@a/b' },
+        },
+        {
+          packageFile: 'packages/something-else/package.json',
+          content: { name: '@a/c' },
+        },
+      ];
+      const res = await checkMonorepos(config);
+      expect(res.monorepoPackages).toMatchSnapshot();
+    });
+    it('adds nested yarn workspaces', async () => {
+      config.packageFiles = [
+        {
+          packageFile: 'frontend/package.json',
+          content: { workspaces: ['packages/*'] },
+        },
+        {
+          packageFile: 'frontend/packages/something/package.json',
+          content: { name: '@a/b' },
+        },
+        {
+          packageFile: 'frontend/packages/something-else/package.json',
+          content: { name: '@a/c' },
+        },
+      ];
+      const res = await checkMonorepos(config);
+      expect(res.monorepoPackages).toMatchSnapshot();
+    });
+    it('adds lerna packages', async () => {
+      config.packageFiles = [
+        {
+          packageFile: 'package.json',
+          content: {},
+        },
+        {
+          packageFile: 'packages/something/package.json',
+          content: { name: '@a/b' },
+        },
+        {
+          packageFile: 'packages/something-else/package.json',
+          content: { name: '@a/c' },
+        },
+      ];
+      config.api.getFileJson.mockReturnValue({ packages: ['packages/*'] });
+      const res = await checkMonorepos(config);
+      expect(res.monorepoPackages).toMatchSnapshot();
+    });
+    it('skips if no lerna packages', async () => {
+      config.packageFiles = [
+        {
+          packageFile: 'package.json',
+          content: {},
+        },
+      ];
+      config.api.getFileJson.mockReturnValue({});
+      const res = await checkMonorepos(config);
+      expect(res.monorepoPackages).toMatchSnapshot();
+    });
+  });
+});
diff --git a/test/workers/repository/__snapshots__/apis.spec.js.snap b/test/workers/repository/__snapshots__/apis.spec.js.snap
index 8d5fe7bdd6..e1fd6165dd 100644
--- a/test/workers/repository/__snapshots__/apis.spec.js.snap
+++ b/test/workers/repository/__snapshots__/apis.spec.js.snap
@@ -1,28 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`workers/repository/apis checkMonorepos adds lerna packages 1`] = `
-Array [
-  "@a/b",
-  "@a/c",
-]
-`;
-
-exports[`workers/repository/apis checkMonorepos adds nested yarn workspaces 1`] = `
-Array [
-  "@a/b",
-  "@a/c",
-]
-`;
-
-exports[`workers/repository/apis checkMonorepos adds yarn workspaces 1`] = `
-Array [
-  "@a/b",
-  "@a/c",
-]
-`;
-
-exports[`workers/repository/apis checkMonorepos skips if no lerna packages 1`] = `Array []`;
-
 exports[`workers/repository/apis initApis(config) throws if unknown platform 1`] = `"Unknown platform: foo"`;
 
 exports[`workers/repository/apis mergeRenovateJson(config) returns error in config if renovate.json cannot be parsed 1`] = `
diff --git a/test/workers/repository/apis.spec.js b/test/workers/repository/apis.spec.js
index 5d9ae3fdbe..5006d051c0 100644
--- a/test/workers/repository/apis.spec.js
+++ b/test/workers/repository/apis.spec.js
@@ -7,8 +7,6 @@ const githubApi = require('../../../lib/platform/github');
 const gitlabApi = require('../../../lib/platform/gitlab');
 const npmApi = require('../../../lib/manager/npm/registry');
 
-const defaultConfig = require('../../../lib/config/defaults').getConfig();
-
 jest.mock('../../../lib/platform/github');
 jest.mock('../../../lib/platform/gitlab');
 jest.mock('../../../lib/manager/npm/registry');
@@ -52,84 +50,6 @@ describe('workers/repository/apis', () => {
       expect(await apis.getNpmrc(config)).toMatchObject(config);
     });
   });
-  describe('checkMonorepos', () => {
-    let config;
-    beforeEach(() => {
-      config = {
-        ...defaultConfig,
-        api: {
-          getFileJson: jest.fn(),
-        },
-        logger,
-      };
-    });
-    it('adds yarn workspaces', async () => {
-      config.packageFiles = [
-        {
-          packageFile: 'package.json',
-          content: { workspaces: ['packages/*'] },
-        },
-        {
-          packageFile: 'packages/something/package.json',
-          content: { name: '@a/b' },
-        },
-        {
-          packageFile: 'packages/something-else/package.json',
-          content: { name: '@a/c' },
-        },
-      ];
-      const res = await apis.checkMonorepos(config);
-      expect(res.monorepoPackages).toMatchSnapshot();
-    });
-    it('adds nested yarn workspaces', async () => {
-      config.packageFiles = [
-        {
-          packageFile: 'frontend/package.json',
-          content: { workspaces: ['packages/*'] },
-        },
-        {
-          packageFile: 'frontend/packages/something/package.json',
-          content: { name: '@a/b' },
-        },
-        {
-          packageFile: 'frontend/packages/something-else/package.json',
-          content: { name: '@a/c' },
-        },
-      ];
-      const res = await apis.checkMonorepos(config);
-      expect(res.monorepoPackages).toMatchSnapshot();
-    });
-    it('adds lerna packages', async () => {
-      config.packageFiles = [
-        {
-          packageFile: 'package.json',
-          content: {},
-        },
-        {
-          packageFile: 'packages/something/package.json',
-          content: { name: '@a/b' },
-        },
-        {
-          packageFile: 'packages/something-else/package.json',
-          content: { name: '@a/c' },
-        },
-      ];
-      config.api.getFileJson.mockReturnValue({ packages: ['packages/*'] });
-      const res = await apis.checkMonorepos(config);
-      expect(res.monorepoPackages).toMatchSnapshot();
-    });
-    it('skips if no lerna packages', async () => {
-      config.packageFiles = [
-        {
-          packageFile: 'package.json',
-          content: {},
-        },
-      ];
-      config.api.getFileJson.mockReturnValue({});
-      const res = await apis.checkMonorepos(config);
-      expect(res.monorepoPackages).toMatchSnapshot();
-    });
-  });
   describe('detectSemanticCommits', () => {
     it('disables semantic commits', async () => {
       const config = {
-- 
GitLab