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