1const { resolve, basename } = require('path') 2const { unlink } = require('fs').promises 3const log = require('../utils/log-shim') 4const BaseCommand = require('../base-command.js') 5class Shrinkwrap extends BaseCommand { 6 static description = 'Lock down dependency versions for publication' 7 static name = 'shrinkwrap' 8 static ignoreImplicitWorkspace = false 9 10 async exec () { 11 // if has a npm-shrinkwrap.json, nothing to do 12 // if has a package-lock.json, rename to npm-shrinkwrap.json 13 // if has neither, load the actual tree and save that as npm-shrinkwrap.json 14 // 15 // loadVirtual, fall back to loadActual 16 // rename shrinkwrap file type, and tree.meta.save() 17 if (this.npm.global) { 18 const er = new Error('`npm shrinkwrap` does not work for global packages') 19 er.code = 'ESHRINKWRAPGLOBAL' 20 throw er 21 } 22 23 const Arborist = require('@npmcli/arborist') 24 const path = this.npm.prefix 25 const sw = resolve(path, 'npm-shrinkwrap.json') 26 const arb = new Arborist({ ...this.npm.flatOptions, path }) 27 const tree = await arb.loadVirtual().catch(() => arb.loadActual()) 28 const { meta } = tree 29 const newFile = meta.hiddenLockfile || !meta.loadedFromDisk 30 const oldFilename = meta.filename 31 const notSW = !newFile && basename(oldFilename) !== 'npm-shrinkwrap.json' 32 33 // The computed lockfile version of a hidden lockfile is always 3 34 // even if the actual value of the property is a different. 35 // When shrinkwrap is run with only a hidden lockfile we want to 36 // set the shrinkwrap lockfile version as whatever was explicitly 37 // requested with a fallback to the actual value from the hidden 38 // lockfile. 39 if (meta.hiddenLockfile) { 40 meta.lockfileVersion = arb.options.lockfileVersion || 41 meta.originalLockfileVersion 42 } 43 meta.hiddenLockfile = false 44 meta.filename = sw 45 await meta.save() 46 47 const updatedVersion = meta.originalLockfileVersion !== meta.lockfileVersion 48 ? meta.lockfileVersion 49 : null 50 51 if (newFile) { 52 let message = 'created a lockfile as npm-shrinkwrap.json' 53 if (updatedVersion) { 54 message += ` with version ${updatedVersion}` 55 } 56 log.notice('', message) 57 } else if (notSW) { 58 await unlink(oldFilename) 59 let message = 'package-lock.json has been renamed to npm-shrinkwrap.json' 60 if (updatedVersion) { 61 message += ` and updated to version ${updatedVersion}` 62 } 63 log.notice('', message) 64 } else if (updatedVersion) { 65 log.notice('', `npm-shrinkwrap.json updated to version ${updatedVersion}`) 66 } else { 67 log.notice('', 'npm-shrinkwrap.json up to date') 68 } 69 } 70} 71module.exports = Shrinkwrap 72