/* @flow */
import type { Logger } from '../../types.js';

const semver = require('semver');
const path = require('path');
const chalk = require('chalk');
const { createArrowList } = require('../../cli/helpers');
const { highlight } = require('../../cli/colorScheme');

const packageName = '/package.json';

/**
 * Opens the project's package.json file and compares version requirements to
 * the actual version of each dependency and devDependency. It lists the
 * node_modules that don't satisfy the requirement or are missing and suggests
 * running `npm install` to amend the issue.
 *
 * Corner cases: there are no dependencies and/or devDependencies, the version
 * is a path to a file, the input file to the package.json or node_modules dir
 * is either undefined or incorrect.
 *
 * Return - returns array of dependencies that are missing or out of date.
 */

type Discrepancy = {
  name: string,
  required: string,
  found: ?string,
};

const checkVersions = (
  dependencies: Object,
  modulePath: string,
  discrepancies: Discrepancy[],
  requireCall: Function,
) => {
  Object.keys(dependencies).forEach((depName: string) => {
    const depVersion: string = dependencies[depName];
    const tempPath: string = path.join(modulePath, depName, packageName);
    let tempPackage: Object;

    try {
      tempPackage = requireCall(tempPath);
    } catch (e) {
      discrepancies.push({
        name: depName,
        required: depVersion,
        found: 'N/A - missing',
      });
      return;
    }

    if (depVersion && !semver.satisfies(tempPackage.version, depVersion)) {
      const versionMatch = depVersion.match(/\d+\.\d+\.\d+.*/);
      if (
        versionMatch &&
        !semver.satisfies(tempPackage.version, versionMatch[0])
      ) {
        discrepancies.push({
          name: depName,
          required: versionMatch ? versionMatch[0] : depVersion,
          found: tempPackage.version,
        });
      }
    }
  });
};

module.exports = (
  projectPackage: Object,
  modulePath: string,
  logger: Logger,
  requireCall?: Function = require,
) => {
  const discrepancies: Discrepancy[] = [];

  if (projectPackage.dependencies) {
    checkVersions(
      projectPackage.dependencies,
      modulePath,
      discrepancies,
      requireCall,
    );
  }
  if (projectPackage.devDependencies) {
    checkVersions(
      projectPackage.devDependencies,
      modulePath,
      discrepancies,
      requireCall,
    );
  }

  if (discrepancies.length === 0) {
    logger.success('No problems found: all dependencies are valid.');
  } else {
    logger.error(
      `Found problems with the following dependencies:\n${createArrowList(
        discrepancies.map(
          ({ name, required, found }) =>
            `${highlight(name)}: required version ${highlight(
              required,
            )}, found ${highlight(found)}`,
        ),
        10,
      )}`,
    );
    logger.error(`Run ${chalk.yellow('npm install')} to resolve this issue`);
  }
  return discrepancies;
};

@@ -940,16 +940,70 @@
 ng,%0A%7D;%0A%0A
+const versionRegex = /%5Cd+%5C.%5Cd+%5C.%5Cd+(-%5Ba-z%5D+%5C.%5Cd+)?/;%0A%0A
 const ch
@@ -1197,19 +1197,24 @@
   const 
-dep
+required
 Version:
@@ -1256,20 +1256,27 @@
   const 
-temp
+packageJson
 Path: st
@@ -1335,20 +1335,25 @@
     let 
-temp
+installed
 Package:
@@ -1378,20 +1378,25 @@
 %7B%0A      
-temp
+installed
 Package 
@@ -1409,20 +1409,27 @@
 ireCall(
-temp
+packageJson
 Path);%0A 
@@ -1513,19 +1513,24 @@
 quired: 
-dep
+required
 Version,
@@ -1601,19 +1601,31 @@
     if (
-dep
+%0A      required
 Version 
@@ -1626,16 +1626,22 @@
 rsion &&
+%0A     
  !semver
@@ -1643,36 +1643,41 @@
 emver.satisfies(
-temp
+installed
 Package.version,
@@ -1681,19 +1681,24 @@
 on, 
-dep
+required
 Version)
 ) %7B%0A
@@ -1693,16 +1693,21 @@
 Version)
+%0A    
 ) %7B%0A    
@@ -1710,25 +1710,33 @@
       const 
-v
+requiredV
 ersionMatch 
@@ -1737,19 +1737,24 @@
 Match = 
-dep
+required
 Version.
@@ -1763,48 +1763,183 @@
 tch(
-/%5Cd+%5C.%5Cd+%5C.%5Cd+.*/);%0A      if (%0A        v
+versionRegex);%0A      const installedVersionMatch = installedPackage.version.match(%0A        versionRegex,%0A      );%0A      if (%0A        requiredVersionMatch &&%0A        installedV
 ersi
@@ -1979,30 +1979,43 @@
 ies(
-tempPackage.version, v
+installedVersionMatch%5B0%5D, requiredV
 ersi
@@ -2114,24 +2114,17 @@
 ed: 
-versionMatch ? v
+requiredV
 ersi
@@ -2137,21 +2137,8 @@
 h%5B0%5D
- : depVersion
 ,%0A  
@@ -2156,27 +2156,32 @@
 nd: 
-tempPackage.version
+installedVersionMatch%5B0%5D
 ,%0A  

fix compareModuleVersions logic (#1094) (#1095)