All files / lib ncm-analyze-tree.js

97.61% Statements 123/126
95.83% Branches 23/24
100% Functions 9/9
97.61% Lines 123/126

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 12727x 27x 27x 27x 27x 27x 27x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 596x 596x 17x 17x 17x 17x 27x 27x 17x 17x 596x 596x 596x 596x 596x 17x 17x 17x 17x 17x 27x 27x 27x 27x 17x 17x 17x 17x 612x 612x 16x 16x 612x 596x 596x 596x 596x 596x 596x 596x 530x 530x 596x 612x 17x 17x 82x 82x 17x 17x 17x 17x 17x 27x 27x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 596x 596x 596x 17x 17x 27x 27x 34x 34x 34x 613x 613x 613x       613x 34x 34x 34x 27x 27x  
'use strict'
 
const { graphql } = require('./util')
const semver = require('semver')
const universalModuleTree = require('universal-module-tree')
 
const analyze = async ({
  dir,
  token,
  pageSize = 50,
  concurrency = 5,
  onPkgs = () => {},
  filter = () => true,
  url
}) => {
  const pkgs = filterPkgs(await readUniversalTree(dir), filter)
  onPkgs(pkgs)
  const data = new Set()
  const pages = splitSet(pkgs, pageSize)
  const batches = splitSet(pages, concurrency)
  for (const batch of batches) {
    await Promise.all([...batch].map(async page => {
      for (const datum of await fetchData({ pkgs: page, token, url })) {
        data.add(datum)
      }
    }))
  }
  return data
}
 
const filterPkgs = (pkgs, fn) => {
  const map = new Map()
  for (const pkg of pkgs) {
    const id = `${pkg.name}${pkg.version}`
    if (semver.valid(pkg.version) && !map.get(id) && fn(pkg)) {
      map.set(id, pkg)
    }
  }
 
  const clean = new Set()
  for (const [, pkg] of map) clean.add(pkg)
  return clean
}
 
const id = node => `${node.data.name}@${node.data.version}`
 
const readUniversalTree = async dir => {
  const tree = await universalModuleTree(dir)
  const pkgs = new Map()
 
  const walk = (node, path) => {
    let pkgObj
    if (pkgs.has(id(node))) {
      pkgObj = pkgs.get(id(node))
      pkgObj.paths.push(path)
    } else {
      pkgObj = {
        name: node.data.name,
        version: node.data.version,
        paths: [path]
      }
      pkgs.set(id(node), pkgObj)
      for (const child of node.children) {
        walk(child, [...path, node])
      }
    }
  }
 
  for (const child of tree.children) {
    walk(child, [])
  }
 
  const set = new Set()
  for (const [, pkg] of pkgs) set.add(pkg)
  return set
}
 
const fetchData = async ({ pkgs, token, url }) => {
  const query = `
    query getPackageVersions($packageVersions: [PackageVersionInput!]!) {
      packageVersions(packageVersions: $packageVersions) {
        name
        version
        published
        publishedAt
        scores {
          group
          name
          pass
          severity
          title
          data
        }
      }
    }
  `
 
  const variables = {
    packageVersions: [...pkgs].map(({ name, version }) => ({ name, version }))
  }
 
  const res = await graphql(url, query, variables)
  const data = new Set()
  for (const datum of res.packageVersions) {
    // datum.paths = [...pkgs][i].paths
    data.add(datum)
  }
  return data
}
 
const splitSet = (set, n) => {
  const buckets = new Set()
  let bucket
  for (const member of set) {
    if (!bucket) bucket = new Set()
    bucket.add(member)
    if (bucket.size === n) {
      buckets.add(bucket)
      bucket = null
    }
  }
  if (bucket) buckets.add(bucket)
  return buckets
}
 
module.exports = analyze