Skip to content

Commit

Permalink
feat(install): --from-lockfile flag
Browse files Browse the repository at this point in the history
Basic implementation of a flag to use `package-lock.json` contents during normal
installs.

- `from-lockfile` reads from `package-lock.json` or another shrinkwrap for exact
  package numbers.

- `from-lockfile` does **not** remove `node_modules`.

- `from-lockfile` is ignored when installing new packages.

- `from-lockfile` will error out if a `package-lock.json` or other shrinkwrap is
  not present.

- `npm install --from-lockfile` is **not** a clean install. If an existing
  installed module is corrupted or edited beyond the point where a normal `npm
  install` would catch it, it will not be cleared out and reinstalled. It does
  not offer the same deterministic guarantees that `npm ci` offers.

Documentation still needs to happen, and there's a little bit more testing to
do. This is an implementation of a feature that's still in-review, so things
might change.
  • Loading branch information
Daniel Shumway committed Jul 15, 2021
1 parent 665a7bd commit bc8ef21
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 0 deletions.
15 changes: 15 additions & 0 deletions lib/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,22 @@ class Install extends ArboristWorkspaceCmd {
add: args,
workspaces: this.workspaceNames,
}

const arb = new Arborist(opts)

// `npm i --from-lockfile` => "Install using only lockfile"
// Ignore if installing a new package.
if (this.npm.config.get('from-lockfile') && !args.length) {
await arb.loadVirtual().catch(er => {
log.verbose('loadVirtual', er.stack)
const msg =
'The `--from-lockfile` flag can only install with an existing package-lock.json or\n' +
'npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or\n' +
'later to generate a package-lock.json file, then try again.'
throw new Error(msg)
})
}

await arb.reify(opts)

if (!args.length && !isGlobalInstall && !ignoreScripts) {
Expand Down
37 changes: 37 additions & 0 deletions test/lib/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,43 @@ t.test('should install globally using Arborist', (t) => {
})
})

t.test('should throw if package-lock.json or npm-shrinkwrap missing and --from-lockfile specified', (t) => {
const testDir = t.testdir({
'package.json': JSON.stringify({
name: 'foo',
version: '1.2.3',
}),
})

const Install = t.mock('../../lib/install.js', {
'@npmcli/run-script': opts => {},
'../../lib/utils/reify-finish.js': async () => {},
npmlog: {
verbose: () => {
t.ok(true, 'log fn called')
},
},
})
const npm = mockNpm({
prefix: testDir,
globalDir: 'path/to/node_modules/',
config: {
'from-lockfile': true,
global: false,
},
flatOptions: {
'from-lockfile': true,
global: false,
},
})
const install = new Install(npm)
install.exec([], (err, res) => {
t.match(err, /package-lock.json/, 'throws error when there is no package-lock')
t.notOk(res)
t.end()
})
})

t.test('completion to folder', async t => {
const Install = t.mock('../../lib/install.js', {
'../../lib/utils/reify-finish.js': async () => {},
Expand Down

0 comments on commit bc8ef21

Please sign in to comment.