├── .gitignore ├── cli.js ├── package.json ├── LICENSE ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | require('@iarna/cli')(main) 4 | .usage('lock-verify [projectPath]') 5 | .help() 6 | 7 | const lockVerify = require('./index.js') 8 | 9 | function main (opts, check) { 10 | return lockVerify(check).then(result => { 11 | result.warnings.forEach(w => console.error('Warning:', w)) 12 | if (!result.status) { 13 | result.errors.forEach(e => console.error(e)) 14 | throw 1 15 | } 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lock-verify", 3 | "version": "2.2.2", 4 | "description": "Report if your package.json is out of sync with your package-lock.json.", 5 | "main": "index.js", 6 | "bin": "cli.js", 7 | "author": "Rebecca Turner (http://re-becca.org/)", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@iarna/cli": "^2.1.0", 11 | "npm-package-arg": "^6.1.0", 12 | "semver": "^5.4.1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/npm/lock-verify.git" 17 | }, 18 | "bugs": { 19 | "url": "https://github.com/npm/lock-verify/issues" 20 | }, 21 | "homepage": "https://github.com/npm/lock-verify#readme", 22 | "files": [ 23 | "index.js", 24 | "cli.js" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Rebecca Turner 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note: pending imminent deprecation 2 | 3 | **This module will be deprecated once npm v7 is released. Please do not rely 4 | on it more than absolutely necessary (ie, only if you are depending on 5 | it for use with npm v6 internal dependencies).** 6 | 7 | ---- 8 | 9 | # lock-verify 10 | 11 | Report if your package.json is out of sync with your package-lock.json. 12 | 13 | ## USAGE 14 | 15 | ``` 16 | const lockVerify = require('lock-verify') 17 | lockVerify(moduleDir).then(result => { 18 | result.warnings.forEach(w => console.error('Warning:', w)) 19 | if (!result.status) { 20 | result.errors.forEach(e => console.error(e)) 21 | process.exit(1) 22 | } 23 | }) 24 | ``` 25 | 26 | As a library it's a function that takes the path to a module and returns a 27 | promise that resolves to an object with `.status`, `.warnings` and `.errors` 28 | properties. The first will be true if everything was ok (though warnings 29 | may exist). If there's no `package.json` or no lockfile in `moduleDir` or they're 30 | unreadable then the promise will be rejected. 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = lockVerify 3 | 4 | const fs = require('fs') 5 | const path = require('path') 6 | const npa = require('npm-package-arg') 7 | const semver = require('semver') 8 | 9 | function 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 | 74 | function 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 | --------------------------------------------------------------------------------