From c564296d91e523ebbd9c12de78f88525b9ef7944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Tue, 7 Mar 2023 17:20:27 +0100 Subject: [PATCH] fix(core): catch all lock file parsing/pruning errors and provide fallback (#15158) Co-authored-by: Jason Jean --- packages/nx/src/lock-file/lock-file.ts | 87 ++++++++++++++++++------- packages/nx/src/lock-file/npm-parser.ts | 7 +- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/packages/nx/src/lock-file/lock-file.ts b/packages/nx/src/lock-file/lock-file.ts index 0e12e93aa38fa1..5b70364cd61a46 100644 --- a/packages/nx/src/lock-file/lock-file.ts +++ b/packages/nx/src/lock-file/lock-file.ts @@ -17,6 +17,7 @@ import { parsePnpmLockfile, stringifyPnpmLockfile } from './pnpm-parser'; import { parseYarnLockfile, stringifyYarnLockfile } from './yarn-parser'; import { pruneProjectGraph } from './project-graph-pruning'; import { normalizePackageJson } from './utils/package-json'; +import { output } from '../utils/output'; const YARN_LOCK_FILE = 'yarn.lock'; const NPM_LOCK_FILE = 'package-lock.json'; @@ -77,17 +78,25 @@ export function lockFileHash( export function parseLockFile( packageManager: PackageManager = detectPackageManager(workspaceRoot) ): ProjectGraph { - if (packageManager === 'yarn') { - const content = readFileSync(YARN_LOCK_PATH, 'utf8'); - return parseYarnLockfile(content); - } - if (packageManager === 'pnpm') { - const content = readFileSync(PNPM_LOCK_PATH, 'utf8'); - return parsePnpmLockfile(content); - } - if (packageManager === 'npm') { - const content = readFileSync(NPM_LOCK_PATH, 'utf8'); - return parseNpmLockfile(content); + try { + if (packageManager === 'yarn') { + const content = readFileSync(YARN_LOCK_PATH, 'utf8'); + return parseYarnLockfile(content); + } + if (packageManager === 'pnpm') { + const content = readFileSync(PNPM_LOCK_PATH, 'utf8'); + return parsePnpmLockfile(content); + } + if (packageManager === 'npm') { + const content = readFileSync(NPM_LOCK_PATH, 'utf8'); + return parseNpmLockfile(content); + } + } catch (e) { + output.error({ + title: `Failed to parse ${packageManager} lockfile`, + bodyLines: errorBodyLines(e), + }); + return; } throw new Error(`Unknown package manager: ${packageManager}`); } @@ -127,19 +136,47 @@ export function createLockFile( const normalizedPackageJson = normalizePackageJson(packageJson); const content = readFileSync(getLockFileName(packageManager), 'utf8'); - if (packageManager === 'yarn') { - const graph = parseYarnLockfile(content); - const prunedGraph = pruneProjectGraph(graph, packageJson); - return stringifyYarnLockfile(prunedGraph, content, normalizedPackageJson); - } - if (packageManager === 'pnpm') { - const graph = parsePnpmLockfile(content); - const prunedGraph = pruneProjectGraph(graph, packageJson); - return stringifyPnpmLockfile(prunedGraph, content, normalizedPackageJson); - } - if (packageManager === 'npm') { - const graph = parseNpmLockfile(content); - const prunedGraph = pruneProjectGraph(graph, packageJson); - return stringifyNpmLockfile(prunedGraph, content, normalizedPackageJson); + try { + if (packageManager === 'yarn') { + const graph = parseYarnLockfile(content); + const prunedGraph = pruneProjectGraph(graph, packageJson); + return stringifyYarnLockfile(prunedGraph, content, normalizedPackageJson); + } + if (packageManager === 'pnpm') { + const graph = parsePnpmLockfile(content); + const prunedGraph = pruneProjectGraph(graph, packageJson); + return stringifyPnpmLockfile(prunedGraph, content, normalizedPackageJson); + } + if (packageManager === 'npm') { + const graph = parseNpmLockfile(content); + const prunedGraph = pruneProjectGraph(graph, packageJson); + return stringifyNpmLockfile(prunedGraph, content, normalizedPackageJson); + } + } catch (e) { + const additionalInfo = [ + 'To prevent the build from breaking we are returning the root lock file.', + ]; + if (packageManager === 'npm') { + additionalInfo.push( + 'If you run `npm install --package-lock-only` in your output folder it will regenerate the correct pruned lockfile.' + ); + } + output.error({ + title: 'An error occured while creating pruned lockfile', + bodyLines: errorBodyLines(e, additionalInfo), + }); + return content; } } + +// generate body lines for error message +function errorBodyLines(originalError: Error, additionalInfo: string[] = []) { + return [ + 'Please open an issue at `https://github.com/nrwl/nx/issues/new?template=1-bug.yml` and provide a reproduction.', + + ...additionalInfo, + + `\nOriginal error: ${originalError.message}\n\n`, + originalError.stack, + ]; +} diff --git a/packages/nx/src/lock-file/npm-parser.ts b/packages/nx/src/lock-file/npm-parser.ts index e9d5de234a3785..070594be4b12e1 100644 --- a/packages/nx/src/lock-file/npm-parser.ts +++ b/packages/nx/src/lock-file/npm-parser.ts @@ -527,7 +527,12 @@ function nestMappedPackages( }); if (initialSize === nestedNodes.size) { - throw Error('Loop detected while pruning. Please report this issue.'); + throw new Error( + [ + 'Following packages could not be mapped to the NPM lockfile:', + ...Array.from(nestedNodes).map((n) => `- ${n.name}`), + ].join('\n') + ); } else { nestMappedPackages( invertedGraph,