From f18dba3500c40ff4f8bc47e214a22089913847e9 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 29 Nov 2023 14:49:57 +0100 Subject: [PATCH 1/2] improve findInstallations function for npm --- .../cli/src/js-package-manager/NPMProxy.ts | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/code/lib/cli/src/js-package-manager/NPMProxy.ts b/code/lib/cli/src/js-package-manager/NPMProxy.ts index da6a83acec8b..a8db9a836047 100644 --- a/code/lib/cli/src/js-package-manager/NPMProxy.ts +++ b/code/lib/cli/src/js-package-manager/NPMProxy.ts @@ -5,6 +5,7 @@ import { sync as findUpSync } from 'find-up'; import { existsSync, readFileSync } from 'fs'; import path from 'path'; import semver from 'semver'; +import { logger } from '@storybook/node-logger'; import { JsPackageManager } from './JsPackageManager'; import type { PackageJson } from './PackageJson'; import type { InstallationMetadata, PackageMetadata } from './types'; @@ -136,22 +137,34 @@ export class NPMProxy extends JsPackageManager { } public async findInstallations() { - const pipeToNull = platform() === 'win32' ? '2>NUL' : '2>/dev/null'; - const commandResult = await this.executeCommand({ - command: 'npm', - args: ['ls', '--json', '--depth=99', pipeToNull], - // ignore errors, because npm ls will exit with code 1 if there are e.g. unmet peer dependencies - ignoreError: true, - env: { - FORCE_COLOR: 'false', - }, - }); + const exec = async ({ depth }: { depth: number }) => { + const pipeToNull = platform() === 'win32' ? '2>NUL' : '2>/dev/null'; + return this.executeCommand({ + command: 'npm', + args: ['ls', '--json', `--depth=${depth}`, pipeToNull], + env: { + FORCE_COLOR: 'false', + }, + }); + }; try { + const commandResult = await exec({ depth: 99 }); const parsedOutput = JSON.parse(commandResult); + return this.mapDependencies(parsedOutput); } catch (e) { - return undefined; + // when --depth is higher than 0, npm can return a non-zero exit code + // in case the user's project has peer dependency issues. So we try again with no depth + try { + const commandResult = await exec({ depth: 0 }); + const parsedOutput = JSON.parse(commandResult); + + return this.mapDependencies(parsedOutput); + } catch (err) { + logger.warn(`An issue occurred while trying to find dependencies metadata using npm.`); + return undefined; + } } } From 19e54ce6e3b28a294d296cab544c962763c42c46 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 29 Nov 2023 15:31:43 +0100 Subject: [PATCH 2/2] remove special characters when finding installation metadata --- .../cli/src/doctor/getDuplicatedDepsWarnings.ts | 1 + .../src/doctor/getMismatchingVersionsWarning.ts | 17 ++++++++++++++--- code/lib/cli/src/js-package-manager/util.ts | 5 ++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts b/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts index 0015798988de..3c72d6c21bc0 100644 --- a/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts +++ b/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts @@ -44,6 +44,7 @@ export function getDuplicatedDepsWarnings( ): string[] | undefined { try { if ( + !installationMetadata || !installationMetadata?.duplicatedDependencies || Object.keys(installationMetadata.duplicatedDependencies).length === 0 ) { diff --git a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts index 665bfa92a93c..68e93f491075 100644 --- a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts +++ b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts @@ -20,6 +20,10 @@ export function getMismatchingVersionsWarnings( installationMetadata?: InstallationMetadata, allDependencies?: Record ): string | undefined { + if (!installationMetadata) { + return undefined; + } + const messages: string[] = []; try { const frameworkPackageName = Object.keys(installationMetadata?.dependencies || []).find( @@ -27,7 +31,9 @@ export function getMismatchingVersionsWarnings( return Object.keys(frameworkPackages).includes(packageName); } ); - const cliVersion = getPrimaryVersion('@storybook/cli', installationMetadata); + const cliVersion = + getPrimaryVersion('@storybook/cli', installationMetadata) || + getPrimaryVersion('storybook', installationMetadata); const frameworkVersion = getPrimaryVersion(frameworkPackageName, installationMetadata); if (!cliVersion || !frameworkVersion || semver.eq(cliVersion, frameworkVersion)) { @@ -68,15 +74,20 @@ export function getMismatchingVersionsWarnings( ); if (filteredDependencies.length > 0) { + const packageJsonSuffix = '(in your package.json)'; messages.push( `Based on your lockfile, these dependencies should be upgraded:`, filteredDependencies .map( ([name, dep]) => `${chalk.hex('#ff9800')(name)}: ${dep[0].version} ${ - allDependencies?.[name] ? '(in your package.json)' : '' + allDependencies?.[name] ? packageJsonSuffix : '' }` ) + .sort( + (a, b) => + (b.includes(packageJsonSuffix) ? 1 : 0) - (a.includes(packageJsonSuffix) ? 1 : 0) + ) .join('\n') ); } @@ -85,7 +96,7 @@ export function getMismatchingVersionsWarnings( `You can run ${chalk.cyan( 'npx storybook@latest upgrade' )} to upgrade all of your Storybook packages to the latest version. - + Alternatively you can try manually changing the versions to match in your package.json. We also recommend regenerating your lockfile, or running the following command to possibly deduplicate your Storybook package versions: ${chalk.cyan( installationMetadata?.dedupeCommand )}` diff --git a/code/lib/cli/src/js-package-manager/util.ts b/code/lib/cli/src/js-package-manager/util.ts index 26d4fe4a8979..ccf07c3f56a2 100644 --- a/code/lib/cli/src/js-package-manager/util.ts +++ b/code/lib/cli/src/js-package-manager/util.ts @@ -1,7 +1,10 @@ // input: @storybook/addon-essentials@npm:7.0.0 // output: { name: '@storybook/addon-essentials', value: { version : '7.0.0', location: '' } } export const parsePackageData = (packageName = '') => { - const [first, second, third] = packageName.trim().split('@'); + const [first, second, third] = packageName + .replace(/[└─├]+/g, '') + .trim() + .split('@'); const version = (third || second).replace('npm:', ''); const name = third ? `@${second}` : first;