• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2module.exports = lockVerify
3
4const fs = require('fs')
5const path = require('path')
6const npa = require('npm-package-arg')
7const semver = require('semver')
8
9function lockVerify(check) {
10  if (!check) check = '.'
11
12  const pjson = readJson(`${check}/package.json`)
13  let plock = readJson(`${check}/npm-shrinkwrap.json`)
14    .catch(() => readJson(`${check}/package-lock.json`))
15
16  return Promise.all([pjson, plock]).then(result => {
17    const pjson = result[0]
18    const plock = result[1]
19    let warnings = []
20    let errors = []
21    for (let type of [['dependencies'], ['devDependencies'], ['optionalDependencies', true]]) {
22      const deps = pjson[type[0]]
23      if (!deps) continue
24      const isOptional = type[1]
25      Object.keys(deps).forEach(name => {
26        const spec = npa.resolve(name, deps[name])
27        const lock = plock.dependencies[name]
28        if (!lock) {
29          if (isOptional) {
30            warnings.push('Optional missing: ' + name + '@' + deps[name])
31          } else {
32            errors.push('Missing: ' + name + '@' + deps[name])
33          }
34          return
35        }
36        if (spec.registry) {
37          // Can't match tags to package-lock w/o network
38          if (spec.type === 'tag') return
39          if (spec.type === 'alias') {
40            const lockSpec = npa.resolve(name, lock.version)
41            if (!semver.satisfies(lockSpec.subSpec.fetchSpec, spec.subSpec.fetchSpec)) {
42              errors.push("Invalid: lock file's " + name + '@' + lock.version + ' does not satisfy ' + name + '@' + spec.rawSpec)
43              return
44            }
45          } else {
46            if (!semver.satisfies(lock.version, spec.fetchSpec)) {
47              errors.push("Invalid: lock file's " + name + '@' + lock.version + ' does not satisfy ' + name + '@' + spec.fetchSpec)
48              return
49            }
50          }
51        } else if (spec.type === 'git') {
52          // can't verify git w/o network
53          return
54        } else if (spec.type === 'remote') {
55          if (lock.version !== spec.fetchSpec) {
56            errors.push("Invalid: lock file's " + name + '@' + lock.version + ' does not satisfy ' + name + '@' + spec.fetchSpec)
57            return
58          }
59        } else if (spec.type === 'file' || spec.type === 'directory') {
60          const lockSpec = npa.resolve(name, lock.version)
61          if (spec.fetchSpec !== lockSpec.fetchSpec) {
62            errors.push("Invalid: lock file's " + name + '@' + lock.version + ' does not satisfy ' + name + '@' + deps[name])
63            return
64          }
65        } else {
66          console.log(spec)
67        }
68      })
69    }
70    return Promise.resolve({status: errors.length === 0, warnings: warnings, errors: errors})
71  })
72}
73
74function readJson (file) {
75  return new Promise((resolve, reject) => {
76    fs.readFile(file, (err, content) => {
77      if (err) return reject(err)
78      return resolve(JSON.parse(content))
79    })
80  })
81}
82