From b184d1ea9c4f8b24a358920855a8fb1a980ebb7e Mon Sep 17 00:00:00 2001
From: ylemkimon <mail@ylem.kim>
Date: Fri, 13 Nov 2020 18:11:08 +0900
Subject: [PATCH] refactor(npm): lockfile extraction code (#7703)

---
 .../extract/__snapshots__/npm.spec.ts.snap    | 16 +++++++------
 lib/manager/npm/extract/common.ts             |  6 +++++
 .../npm/extract/locked-versions.spec.ts       | 10 ++++----
 lib/manager/npm/extract/locked-versions.ts    | 15 ++++--------
 lib/manager/npm/extract/npm.spec.ts           |  6 ++---
 lib/manager/npm/extract/npm.ts                | 14 +++++------
 lib/manager/npm/extract/yarn.spec.ts          |  6 ++---
 lib/manager/npm/extract/yarn.ts               | 23 ++++++++-----------
 8 files changed, 47 insertions(+), 49 deletions(-)

diff --git a/lib/manager/npm/extract/__snapshots__/npm.spec.ts.snap b/lib/manager/npm/extract/__snapshots__/npm.spec.ts.snap
index 3858128514..e8e7f21cc4 100644
--- a/lib/manager/npm/extract/__snapshots__/npm.spec.ts.snap
+++ b/lib/manager/npm/extract/__snapshots__/npm.spec.ts.snap
@@ -2,12 +2,14 @@
 
 exports[`manager/npm/extract/npm .getNpmLock() extracts 1`] = `
 Object {
-  "ansi-styles": "3.2.1",
-  "chalk": "2.4.1",
-  "color-convert": "1.9.1",
-  "color-name": "1.1.3",
-  "escape-string-regexp": "1.0.5",
-  "has-flag": "3.0.0",
-  "supports-color": "5.4.0",
+  "lockedVersions": Object {
+    "ansi-styles": "3.2.1",
+    "chalk": "2.4.1",
+    "color-convert": "1.9.1",
+    "color-name": "1.1.3",
+    "escape-string-regexp": "1.0.5",
+    "has-flag": "3.0.0",
+    "supports-color": "5.4.0",
+  },
 }
 `;
diff --git a/lib/manager/npm/extract/common.ts b/lib/manager/npm/extract/common.ts
index ed2b7a959e..c463f997e5 100644
--- a/lib/manager/npm/extract/common.ts
+++ b/lib/manager/npm/extract/common.ts
@@ -13,3 +13,9 @@ export type LockFileEntry = Record<
   string,
   { version: string; integrity?: boolean }
 >;
+
+export interface LockFile {
+  lockedVersions: Record<string, string>;
+  lockfileVersion?: number; // cache version for Yarn
+  isYarn1?: boolean;
+}
diff --git a/lib/manager/npm/extract/locked-versions.spec.ts b/lib/manager/npm/extract/locked-versions.spec.ts
index 90348d08b1..897dfd18aa 100644
--- a/lib/manager/npm/extract/locked-versions.spec.ts
+++ b/lib/manager/npm/extract/locked-versions.spec.ts
@@ -15,7 +15,7 @@ describe('manager/npm/extract/locked-versions', () => {
       async (yarnVersion) => {
         yarn.getYarnLock.mockReturnValue({
           isYarn1: yarnVersion === '1.22.0',
-          cacheVersion: yarnVersion === '2.2.0' ? 6 : NaN,
+          lockfileVersion: yarnVersion === '2.2.0' ? 6 : undefined,
           lockedVersions: {
             'a@1.0.0': '1.0.0',
             'b@2.0.0': '2.0.0',
@@ -46,9 +46,11 @@ describe('manager/npm/extract/locked-versions', () => {
 
     it('uses package-lock.json', async () => {
       npm.getNpmLock.mockReturnValue({
-        a: '1.0.0',
-        b: '2.0.0',
-        c: '3.0.0',
+        lockedVersions: {
+          a: '1.0.0',
+          b: '2.0.0',
+          c: '3.0.0',
+        },
       });
       const packageFiles = [
         {
diff --git a/lib/manager/npm/extract/locked-versions.ts b/lib/manager/npm/extract/locked-versions.ts
index a54dcb7de3..593968bc06 100644
--- a/lib/manager/npm/extract/locked-versions.ts
+++ b/lib/manager/npm/extract/locked-versions.ts
@@ -1,13 +1,14 @@
 import { valid } from 'semver';
 import { logger } from '../../../logger';
 import { PackageFile } from '../../common';
+import { LockFile } from './common';
 import { getNpmLock } from './npm';
 import { getYarnLock } from './yarn';
 
 export async function getLockedVersions(
   packageFiles: PackageFile[]
 ): Promise<void> {
-  const lockFileCache: Record<string, YarnLockCache> = {};
+  const lockFileCache: Record<string, LockFile> = {};
   logger.debug('Finding locked versions');
   for (const packageFile of packageFiles) {
     const { yarnLock, npmLock, pnpmShrinkwrap } = packageFile;
@@ -17,9 +18,9 @@ export async function getLockedVersions(
         logger.trace('Retrieving/parsing ' + yarnLock);
         lockFileCache[yarnLock] = await getYarnLock(yarnLock);
       }
-      const { cacheVersion, isYarn1 } = lockFileCache[yarnLock];
+      const { lockfileVersion, isYarn1 } = lockFileCache[yarnLock];
       if (!isYarn1) {
-        if (cacheVersion >= 6) {
+        if (lockfileVersion >= 6) {
           // https://github.com/yarnpkg/berry/commit/f753790380cbda5b55d028ea84b199445129f9ba
           packageFile.constraints.yarn = '>= 2.2.0';
         } else {
@@ -36,7 +37,7 @@ export async function getLockedVersions(
       logger.debug('Found ' + npmLock + ' for ' + packageFile.packageFile);
       if (!lockFileCache[npmLock]) {
         logger.trace('Retrieving/parsing ' + npmLock);
-        lockFileCache[npmLock] = { lockedVersions: await getNpmLock(npmLock) };
+        lockFileCache[npmLock] = await getNpmLock(npmLock);
       }
       for (const dep of packageFile.deps) {
         dep.lockedVersion = valid(
@@ -48,9 +49,3 @@ export async function getLockedVersions(
     }
   }
 }
-
-interface YarnLockCache {
-  lockedVersions: Record<string, string>;
-  cacheVersion?: number;
-  isYarn1?: boolean;
-}
diff --git a/lib/manager/npm/extract/npm.spec.ts b/lib/manager/npm/extract/npm.spec.ts
index 3156240410..077259fcde 100644
--- a/lib/manager/npm/extract/npm.spec.ts
+++ b/lib/manager/npm/extract/npm.spec.ts
@@ -9,7 +9,7 @@ describe('manager/npm/extract/npm', () => {
     it('returns empty if failed to parse', async () => {
       fs.readLocalFile.mockResolvedValueOnce('abcd');
       const res = await getNpmLock('package.json');
-      expect(Object.keys(res)).toHaveLength(0);
+      expect(Object.keys(res.lockedVersions)).toHaveLength(0);
     });
     it('extracts', async () => {
       const plocktest1Lock = readFileSync(
@@ -18,12 +18,12 @@ describe('manager/npm/extract/npm', () => {
       fs.readLocalFile.mockResolvedValueOnce(plocktest1Lock as never);
       const res = await getNpmLock('package.json');
       expect(res).toMatchSnapshot();
-      expect(Object.keys(res)).toHaveLength(7);
+      expect(Object.keys(res.lockedVersions)).toHaveLength(7);
     });
     it('returns empty if no deps', async () => {
       fs.readLocalFile.mockResolvedValueOnce('{}');
       const res = await getNpmLock('package.json');
-      expect(Object.keys(res)).toHaveLength(0);
+      expect(Object.keys(res.lockedVersions)).toHaveLength(0);
     });
   });
 });
diff --git a/lib/manager/npm/extract/npm.ts b/lib/manager/npm/extract/npm.ts
index bfabceffa2..6982352412 100644
--- a/lib/manager/npm/extract/npm.ts
+++ b/lib/manager/npm/extract/npm.ts
@@ -1,23 +1,21 @@
 import { logger } from '../../../logger';
 import { readLocalFile } from '../../../util/fs';
-import { LockFileEntry } from './common';
+import { LockFile, LockFileEntry } from './common';
 
-export async function getNpmLock(
-  filePath: string
-): Promise<Record<string, string>> {
+export async function getNpmLock(filePath: string): Promise<LockFile> {
   const lockRaw = await readLocalFile(filePath, 'utf8');
   try {
     const lockParsed = JSON.parse(lockRaw);
-    const lockFile: Record<string, string> = {};
+    const lockedVersions: Record<string, string> = {};
     for (const [entry, val] of Object.entries(
       (lockParsed.dependencies || {}) as LockFileEntry
     )) {
       logger.trace({ entry, version: val.version });
-      lockFile[entry] = val.version;
+      lockedVersions[entry] = val.version;
     }
-    return lockFile;
+    return { lockedVersions };
   } catch (err) {
     logger.debug({ filePath, err }, 'Warning: Exception parsing npm lock file');
-    return {};
+    return { lockedVersions: {} };
   }
 }
diff --git a/lib/manager/npm/extract/yarn.spec.ts b/lib/manager/npm/extract/yarn.spec.ts
index 18cff3c671..a106e7ecd4 100644
--- a/lib/manager/npm/extract/yarn.spec.ts
+++ b/lib/manager/npm/extract/yarn.spec.ts
@@ -21,7 +21,7 @@ describe('manager/npm/extract/yarn', () => {
       fs.readLocalFile.mockResolvedValueOnce(plocktest1Lock);
       const res = await getYarnLock('package.json');
       expect(res.isYarn1).toBe(true);
-      expect(res.cacheVersion).toBe(NaN);
+      expect(res.lockfileVersion).toBeUndefined();
       expect(res.lockedVersions).toMatchSnapshot();
       expect(Object.keys(res.lockedVersions)).toHaveLength(7);
     });
@@ -34,7 +34,7 @@ describe('manager/npm/extract/yarn', () => {
       fs.readLocalFile.mockResolvedValueOnce(plocktest1Lock);
       const res = await getYarnLock('package.json');
       expect(res.isYarn1).toBe(false);
-      expect(res.cacheVersion).toBe(NaN);
+      expect(res.lockfileVersion).toBe(NaN);
       expect(res.lockedVersions).toMatchSnapshot();
       expect(Object.keys(res.lockedVersions)).toHaveLength(8);
     });
@@ -47,7 +47,7 @@ describe('manager/npm/extract/yarn', () => {
       fs.readLocalFile.mockResolvedValueOnce(plocktest1Lock);
       const res = await getYarnLock('package.json');
       expect(res.isYarn1).toBe(false);
-      expect(res.cacheVersion).toBe(6);
+      expect(res.lockfileVersion).toBe(6);
       expect(res.lockedVersions).toMatchSnapshot();
       expect(Object.keys(res.lockedVersions)).toHaveLength(10);
     });
diff --git a/lib/manager/npm/extract/yarn.ts b/lib/manager/npm/extract/yarn.ts
index 731f17dcd0..fa7d17b9bf 100644
--- a/lib/manager/npm/extract/yarn.ts
+++ b/lib/manager/npm/extract/yarn.ts
@@ -2,24 +2,19 @@ import { structUtils } from '@yarnpkg/core';
 import { parseSyml } from '@yarnpkg/parsers';
 import { logger } from '../../../logger';
 import { readLocalFile } from '../../../util/fs';
+import { LockFile } from './common';
 
-export async function getYarnLock(
-  filePath: string
-): Promise<{
-  isYarn1: boolean;
-  cacheVersion: number;
-  lockedVersions: Record<string, string>;
-}> {
+export async function getYarnLock(filePath: string): Promise<LockFile> {
   const yarnLockRaw = await readLocalFile(filePath, 'utf8');
   try {
     const parsed = parseSyml(yarnLockRaw);
-    const lockFile: Record<string, string> = {};
-    let cacheVersion = NaN;
+    const lockedVersions: Record<string, string> = {};
+    let lockfileVersion: number;
 
     for (const [key, val] of Object.entries(parsed)) {
       if (key === '__metadata') {
         // yarn 2
-        cacheVersion = parseInt(val.cacheKey, 10);
+        lockfileVersion = parseInt(val.cacheKey, 10);
       } else {
         for (const entry of key.split(', ')) {
           const { scope, name, range } = structUtils.parseDescriptor(entry);
@@ -27,17 +22,17 @@ export async function getYarnLock(
           const { selector } = structUtils.parseRange(range);
 
           logger.trace({ entry, version: val.version });
-          lockFile[packageName + '@' + selector] = parsed[key].version;
+          lockedVersions[packageName + '@' + selector] = parsed[key].version;
         }
       }
     }
     return {
       isYarn1: !('__metadata' in parsed),
-      cacheVersion,
-      lockedVersions: lockFile,
+      lockfileVersion,
+      lockedVersions,
     };
   } catch (err) {
     logger.debug({ filePath, err }, 'Warning: Exception parsing yarn.lock');
-    return { isYarn1: true, cacheVersion: NaN, lockedVersions: {} };
+    return { isYarn1: true, lockedVersions: {} };
   }
 }
-- 
GitLab