<commit_before>const semver = require('semver');

class Formatter {

  constructor (format, excludes, changelogs) {
    this.format = format;
    this.excludes = excludes;
    this.changelogs = changelogs;
  }

  run (json) {
    const packages = this.sortPackages(this.extractOutdatedPackages(json));
    const format   = this.format;

    let result;

    if (format === 'markdown') {
      result = this.toMarkdown(packages);
    } else if (format === 'json') {
      result = this.toJSON(packages);
    } else if (format === 'mackerel') {
      result = this.toMackerel(packages);
    }

    return result;
  }

  extractOutdatedPackages (json) {
    return json.data.body
      .filter((p) => ! p.slice(1, 3).includes('exotic'))
      .filter((p) => ! this.excludes.includes(p[0]))
      .map((p) => {
        p.push(this.changelogs[p[0]] || '');
        return p;
      });
  }

  sortPackages (packages) {
    const sortedPackages = {
      major : [],
      minor : [],
      patch : [],
    };

    packages.forEach((p) => {
      const current = p[1];
      const latest  = p[3];

      if (semver.major(current) !== semver.major(latest)) {
        sortedPackages.major.push(p);
      } else if (semver.minor(current) !== semver.minor(latest)) {
        sortedPackages.minor.push(p);
      } else if (semver.patch(current) !== semver.patch(latest)) {
        sortedPackages.patch.push(p);
      }
    });

    return sortedPackages;
  }
<commit_msg>Formatter can parse npm outdated --json<commit_after>const semver = require('semver');

class Formatter {

  constructor (format, excludes, changelogs) {
    this.format = format;
    this.excludes = excludes;
    this.changelogs = changelogs;
  }

  run (json) {
    const packages = this.sortPackages(this.extractOutdatedPackages(json));
    const format   = this.format;

    let result;

    if (format === 'markdown') {
      result = this.toMarkdown(packages);
    } else if (format === 'json') {
      result = this.toJSON(packages);
    } else if (format === 'mackerel') {
      result = this.toMackerel(packages);
    }

    return result;
  }

  extractOutdatedPackages(json) {
    const packages = (json.data && json.data.body) ? json.data.body : this.extractPackagesFromNpmOutdatedJSON(json);
      return packages
        .filter((p) => !p.slice(1, 3).includes('exotic'))
        .filter((p) => !this.excludes.includes(p[0]))
        .map((p) => {
          p.push(this.changelogs[p[0]] || '');
          return p;
        });
  }

  /* convert { packageName: { current, wanted, latest, type }} to [ packageName, current, wanted, latest, type, url(null) ] */
  extractPackagesFromNpmOutdatedJSON(json) {
    return Object.keys(json).map(packageName => {
      const info = json[packageName];
      return [
        packageName,
        info.current,
        info.wanted,
        info.latest,
        info.type,
        null
      ];
    });
  }

  sortPackages (packages) {
    const sortedPackages = {
      major : [],
      minor : [],
      patch : [],
    };

    packages.forEach((p) => {
      const current = p[1];
      const latest  = p[3];

      if (semver.major(current) !== semver.major(latest)) {
        sortedPackages.major.push(p);
      } else if (semver.minor(current) !== semver.minor(latest)) {
        sortedPackages.minor.push(p);
      } else if (semver.patch(current) !== semver.patch(latest)) {
        sortedPackages.patch.push(p);
      }
    });

    return sortedPackages;
  }

{'fraction_non_alphanumeric': 0.15632183908045977, 'fraction_numerical': 0.0034482758620689655, 'mean_word_length': 2.5055387713997987, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3550786', 'n_tokens_mistral': 1153, 'n_tokens_neox': 1116, 'n_words': 210}