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