diff --git a/lib/manager/npm/post-update/__snapshots__/npm.spec.ts.snap b/lib/manager/npm/post-update/__snapshots__/npm.spec.ts.snap index b21eac557d17a87f99551935a03214b389337916..0abab7674b2c197d47eae457d7582f0990bf0590 100644 --- a/lib/manager/npm/post-update/__snapshots__/npm.spec.ts.snap +++ b/lib/manager/npm/post-update/__snapshots__/npm.spec.ts.snap @@ -45,29 +45,6 @@ Array [ ] `; -exports[`manager/npm/post-update/npm massages lock files 1`] = ` -Array [ - Object { - "cmd": "npm install --package-lock-only --no-audit --ignore-scripts", - "options": Object { - "cwd": "some-dir", - "encoding": "utf-8", - "env": Object { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - exports[`manager/npm/post-update/npm performs full install 1`] = `Array []`; exports[`manager/npm/post-update/npm performs lock file maintenance 1`] = ` diff --git a/lib/manager/npm/post-update/index.ts b/lib/manager/npm/post-update/index.ts index dc83a1ca9495515cc7d37f922c8a76b24f53b5d3..636ca8b5cb355a61839b1f716240f2eb94e12775 100644 --- a/lib/manager/npm/post-update/index.ts +++ b/lib/manager/npm/post-update/index.ts @@ -167,7 +167,10 @@ export async function writeExistingFiles( } else { logger.debug(`Writing ${npmLock}`); let existingNpmLock = await getFile(npmLock); + const npmLockParsed = JSON.parse(existingNpmLock); + const packageNames = Object.keys(npmLockParsed?.packages || {}); // lockfileVersion=2 const widens = []; + let lockFileChanged = false; for (const upgrade of config.upgrades) { if ( upgrade.rangeStrategy === 'widen' && @@ -175,19 +178,29 @@ export async function writeExistingFiles( ) { widens.push(upgrade.depName); } + const { depName } = upgrade; + for (const packageName of packageNames) { + if ( + packageName === `node_modules/${depName}` || + packageName.startsWith(`node_modules/${depName}/`) + ) { + logger.trace({ packageName }, 'Massaging out package name'); + lockFileChanged = true; + delete npmLockParsed.packages[packageName]; + } + } } if (widens.length) { logger.debug( `Removing ${String(widens)} from ${npmLock} to force an update` ); + lockFileChanged = true; try { - const npmLockParsed = JSON.parse(existingNpmLock); if (npmLockParsed.dependencies) { widens.forEach((depName) => { delete npmLockParsed.dependencies[depName]; }); } - existingNpmLock = JSON.stringify(npmLockParsed, null, 2); } catch (err) { logger.warn( { npmLock }, @@ -195,6 +208,10 @@ export async function writeExistingFiles( ); } } + if (lockFileChanged) { + logger.debug('Massaging npm lock file before writing to disk'); + existingNpmLock = JSON.stringify(npmLockParsed, null, 2); + } await outputFile(npmLockPath, existingNpmLock); } } diff --git a/lib/manager/npm/post-update/npm.spec.ts b/lib/manager/npm/post-update/npm.spec.ts index 4fd11cf91c3e64b7804d36f0878eb5be4c33f285..e4f9341151427be2b7b907f2b7f5a208fc1b8552 100644 --- a/lib/manager/npm/post-update/npm.spec.ts +++ b/lib/manager/npm/post-update/npm.spec.ts @@ -1,5 +1,4 @@ import { exec as _exec } from 'child_process'; -import * as fsExtra from 'fs-extra'; import upath from 'upath'; import { envMock, mockExecAll } from '../../../../test/exec-util'; @@ -38,38 +37,11 @@ describe('manager/npm/post-update/npm', () => { { skipInstalls, postUpdateOptions }, updates ); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(res.error).toBeUndefined(); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchSnapshot(); }); - it('massages lock files', async () => { - const execSnapshots = mockExecAll(exec); - const lockContents = await fsExtra.readFile( - upath.join(__dirname, '/__fixtures__/massage1/package-lock.json'), - 'utf8' - ); - fs.readFile = jest.fn(() => lockContents) as never; - const skipInstalls = true; - const updates = [ - { - depName: '@storybook/vue', - newVersion: '6.3.12', - isLockfileUpdate: false, - }, - ]; - const res = await npmHelper.generateLockFile( - 'some-dir', - {}, - 'package-lock.json', - { skipInstalls }, - updates - ); - expect(fs.readFile).toHaveBeenCalledTimes(2); - expect(fs.outputFile).toHaveBeenCalledTimes(1); - expect(res.error).toBeUndefined(); - expect(execSnapshots).toMatchSnapshot(); - }); it('performs lock file updates', async () => { const execSnapshots = mockExecAll(exec); fs.readFile = jest.fn(() => 'package-lock-contents') as never; @@ -84,7 +56,7 @@ describe('manager/npm/post-update/npm', () => { { skipInstalls }, updates ); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(res.error).toBeUndefined(); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchSnapshot(); @@ -109,7 +81,7 @@ describe('manager/npm/post-update/npm', () => { upath.join('some-dir', 'package-lock.json'), upath.join('some-dir', 'npm-shrinkwrap.json') ); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(fs.readFile).toHaveBeenCalledWith( upath.join('some-dir', 'npm-shrinkwrap.json'), 'utf8' @@ -134,7 +106,7 @@ describe('manager/npm/post-update/npm', () => { upath.join('some-dir', 'package-lock.json') ); expect(fs.move).toHaveBeenCalledTimes(0); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(fs.readFile).toHaveBeenCalledWith( upath.join('some-dir', 'npm-shrinkwrap.json'), 'utf8' @@ -154,7 +126,7 @@ describe('manager/npm/post-update/npm', () => { 'package-lock.json', { skipInstalls, binarySource } ); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(res.error).toBeUndefined(); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchSnapshot(); @@ -170,7 +142,7 @@ describe('manager/npm/post-update/npm', () => { { binarySource }, [{ isRemediation: true }] ); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(res.error).toBeUndefined(); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toHaveLength(2); @@ -185,7 +157,7 @@ describe('manager/npm/post-update/npm', () => { {}, 'package-lock.json' ); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(res.error).toBeTrue(); expect(res.lockFile).toBeUndefined(); expect(execSnapshots).toMatchSnapshot(); @@ -198,7 +170,7 @@ describe('manager/npm/post-update/npm', () => { {}, 'package-lock.json' ); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchSnapshot(); }); @@ -211,7 +183,7 @@ describe('manager/npm/post-update/npm', () => { 'package-lock.json', { binarySource: 'docker', constraints: { npm: '^6.0.0' } } ); - expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(fs.readFile).toHaveBeenCalledTimes(1); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchSnapshot(); }); diff --git a/lib/manager/npm/post-update/npm.ts b/lib/manager/npm/post-update/npm.ts index 9dd5414dcdd478ebb3f19efd6fb5541f2ba0359f..f84023593c5ceccf9a2483ef6ba5964b4e7b5552 100644 --- a/lib/manager/npm/post-update/npm.ts +++ b/lib/manager/npm/post-update/npm.ts @@ -1,4 +1,3 @@ -import is from '@sindresorhus/is'; import { validRange } from 'semver'; import { quote } from 'shlex'; import { join } from 'upath'; @@ -9,13 +8,7 @@ import { } from '../../../constants/error-messages'; import { logger } from '../../../logger'; import { ExecOptions, exec } from '../../../util/exec'; -import { - move, - outputFile, - pathExists, - readFile, - remove, -} from '../../../util/fs'; +import { move, pathExists, readFile, remove } from '../../../util/fs'; import type { PostUpdateConfig, Upgrade } from '../../types'; import { getNodeConstraint } from './node-version'; import type { GenerateLockFileResult } from './types'; @@ -111,6 +104,7 @@ export async function generateLockFile( commands.push('npm dedupe'); } + // TODO: don't assume package-lock.json is in the same directory const lockFileName = join(cwd, filename); if (upgrades.find((upgrade) => upgrade.isLockFileMaintenance)) { @@ -125,36 +119,6 @@ export async function generateLockFile( 'Error removing yarn.lock for lock file maintenance' ); } - } else { - // massage lock file for npm 7+ - try { - const lockFileParsed = JSON.parse(await readFile(lockFileName, 'utf8')); - if (is.plainObject(lockFileParsed.packages)) { - const packageNames = Object.keys(lockFileParsed.packages); - let lockFileMassaged = false; - for (const { depName } of upgrades) { - for (const packageName of packageNames) { - if ( - packageName === `node_modules/${depName}` || - packageName.startsWith(`node_modules/${depName}/`) - ) { - logger.trace({ packageName }, 'Massaging out package name'); - lockFileMassaged = true; - delete lockFileParsed.packages[packageName]; - } - } - } - if (lockFileMassaged) { - logger.debug('Writing massaged package-lock.json'); - await outputFile( - lockFileName, - JSON.stringify(lockFileParsed, null, 2) - ); - } - } - } catch (err) { - logger.warn({ err }, 'Error massaging package-lock.json'); - } } // Run the commands