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/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; + } } } 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;