diff --git a/CHANGELOG.md b/CHANGELOG.md index f1861a3686b1..4b1dc7ca1734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 7.6.2 + +- CLI: Improve dependency metadata detection in storybook doctor - [#25037](https://github.com/storybookjs/storybook/pull/25037), thanks [@yannbf](https://github.com/yannbf)! +- React-Docgen: Make error-handling more gentle - [#25055](https://github.com/storybookjs/storybook/pull/25055), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! + ## 7.6.1 - Next.js: Fix AppRouterProvider usage - [#25032](https://github.com/storybookjs/storybook/pull/25032), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)! 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 9aa0d424e01d..68e93f491075 100644 --- a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts +++ b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts @@ -4,7 +4,10 @@ import { frameworkPackages } from '@storybook/core-common'; import type { InstallationMetadata } from '../js-package-manager/types'; import storybookCorePackages from '../versions'; -function getPrimaryVersion(name: string, installationMetadata?: InstallationMetadata) { +function getPrimaryVersion(name: string | undefined, installationMetadata?: InstallationMetadata) { + if (!name) { + return undefined; + } const packageMetadata = installationMetadata?.dependencies[name]; if (!packageMetadata) { return undefined; @@ -17,14 +20,20 @@ export function getMismatchingVersionsWarnings( installationMetadata?: InstallationMetadata, allDependencies?: Record ): string | undefined { + if (!installationMetadata) { + return undefined; + } + const messages: string[] = []; try { - const frameworkPackageName = Object.keys(installationMetadata?.dependencies).find( + const frameworkPackageName = Object.keys(installationMetadata?.dependencies || []).find( (packageName) => { 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)) { @@ -41,7 +50,7 @@ export function getMismatchingVersionsWarnings( let packageToDisplay: string; if (semver.lt(cliVersion, frameworkVersion)) { versionToCompare = frameworkVersion; - packageToDisplay = frameworkPackageName; + packageToDisplay = frameworkPackageName as string; } else { versionToCompare = cliVersion; packageToDisplay = 'storybook'; @@ -53,7 +62,7 @@ export function getMismatchingVersionsWarnings( )} (from the ${chalk.cyan(packageToDisplay)} package) or higher.` ); - const filteredDependencies = Object.entries(installationMetadata?.dependencies).filter( + const filteredDependencies = Object.entries(installationMetadata?.dependencies || []).filter( ([name, packages]) => { if (Object.keys(storybookCorePackages).includes(name)) { const packageVersion = packages[0].version; @@ -65,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') ); } @@ -82,9 +96,9 @@ 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 + 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; diff --git a/code/package.json b/code/package.json index 67c9fd931ebc..35513681840e 100644 --- a/code/package.json +++ b/code/package.json @@ -328,5 +328,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "7.6.2" } diff --git a/code/presets/react-webpack/src/framework-preset-react-docs.test.ts b/code/presets/react-webpack/src/framework-preset-react-docs.test.ts index bfdcd7851e07..b773481c76ac 100644 --- a/code/presets/react-webpack/src/framework-preset-react-docs.test.ts +++ b/code/presets/react-webpack/src/framework-preset-react-docs.test.ts @@ -43,9 +43,9 @@ describe('framework-preset-react-docgen', () => { module: { rules: [ { - exclude: /node_modules/, + exclude: /(\.(stories|story)\.(js|jsx|ts|tsx))|(node_modules)/, loader: '@storybook/preset-react-webpack/dist/loaders/react-docgen-loader', - options: { babelOptions: { plugins: [], presets: [] } }, + options: { babelOptions: { plugins: [], presets: [] }, debug: false }, test: /\.(cjs|mjs|tsx?|jsx?)$/, }, ], @@ -88,9 +88,9 @@ describe('framework-preset-react-docgen', () => { module: { rules: [ { - exclude: /node_modules/, + exclude: /(\.(stories|story)\.(js|jsx|ts|tsx))|(node_modules)/, loader: '@storybook/preset-react-webpack/dist/loaders/react-docgen-loader', - options: { babelOptions: { plugins: [], presets: [] } }, + options: { babelOptions: { plugins: [], presets: [] }, debug: false }, test: /\.(cjs|mjs|jsx?)$/, }, ], diff --git a/code/presets/react-webpack/src/framework-preset-react-docs.ts b/code/presets/react-webpack/src/framework-preset-react-docs.ts index 4825312b90d8..273dec8dd3af 100644 --- a/code/presets/react-webpack/src/framework-preset-react-docs.ts +++ b/code/presets/react-webpack/src/framework-preset-react-docs.ts @@ -14,6 +14,7 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async ( 'typescript', {} as any ); + const debug = options.loglevel === 'debug'; const { reactDocgen, reactDocgenTypescriptOptions } = typescriptOptions || {}; @@ -37,8 +38,9 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async ( ), options: { babelOptions, + debug, }, - exclude: /node_modules/, + exclude: /(\.(stories|story)\.(js|jsx|ts|tsx))|(node_modules)/, }, ], }, @@ -63,8 +65,9 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async ( ), options: { babelOptions, + debug, }, - exclude: /node_modules/, + exclude: /(\.(stories|story)\.(js|jsx|ts|tsx))|(node_modules)/, }, ], }, diff --git a/code/presets/react-webpack/src/loaders/react-docgen-loader.ts b/code/presets/react-webpack/src/loaders/react-docgen-loader.ts index aa1724fd880f..9f3ff5278b9e 100644 --- a/code/presets/react-webpack/src/loaders/react-docgen-loader.ts +++ b/code/presets/react-webpack/src/loaders/react-docgen-loader.ts @@ -9,6 +9,8 @@ import { import MagicString from 'magic-string'; import type { LoaderContext } from 'webpack'; import type { Handler, NodePath, babelTypes as t, Documentation } from 'react-docgen'; +import { logger } from '@storybook/node-logger'; +import type { TransformOptions } from '@babel/core'; const { getNameOrValue, isReactForwardRefCall } = utils; @@ -56,11 +58,14 @@ const defaultResolver = new docgenResolver.FindExportedDefinitionsResolver(); const defaultImporter = docgenImporters.fsImporter; const handlers = [...defaultHandlers, actualNameHandler]; -export default async function reactDocgenLoader(this: LoaderContext, source: string) { +export default async function reactDocgenLoader( + this: LoaderContext<{ babelOptions: TransformOptions; debug: boolean }>, + source: string +) { const callback = this.async(); // get options const options = this.getOptions() || {}; - const { babelOptions = {} } = options; + const { babelOptions = {}, debug = false } = options; const { plugins, presets } = babelOptions; @@ -94,7 +99,18 @@ export default async function reactDocgenLoader(this: LoaderContext, source if (error.code === ERROR_CODES.MISSING_DEFINITION) { callback(null, source); } else { - callback(error); + if (!debug) { + logger.warn( + `Failed to parse ${this.resourcePath} with react-docgen. Rerun Storybook with --loglevel=debug to get more info.` + ); + } else { + logger.warn( + `Failed to parse ${this.resourcePath} with react-docgen. Please use the below error message and the content of the file which causes the error to report the issue to the maintainers of react-docgen. https://github.com/reactjs/react-docgen` + ); + logger.error(error); + } + + callback(null, source); } } } diff --git a/code/yarn.lock b/code/yarn.lock index ad90083b8b36..823381eaadc0 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6281,15 +6281,6 @@ __metadata: languageName: unknown linkType: soft -"@storybook/client-logger@npm:7.5.0": - version: 7.5.0 - resolution: "@storybook/client-logger@npm:7.5.0" - dependencies: - "@storybook/global": "npm:^5.0.0" - checksum: 90326c49a224bf21680c04ffee94725bf75658086093ccb839a8aae39476929c4719eafb18e498a148cf0dd956d4e9a5d3b2a34d09ca4fd25e2af553458558ac - languageName: node - linkType: hard - "@storybook/client-logger@workspace:*, @storybook/client-logger@workspace:lib/client-logger": version: 0.0.0-use.local resolution: "@storybook/client-logger@workspace:lib/client-logger" @@ -6693,12 +6684,12 @@ __metadata: linkType: soft "@storybook/icons@npm:^1.1.6": - version: 1.1.7 - resolution: "@storybook/icons@npm:1.1.7" + version: 1.2.3 + resolution: "@storybook/icons@npm:1.2.3" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 5bb97f948f2a1cfc067a120f8e17004cdf9cdef0d08558ec51659e4e4d4b1620c76ced6d15a57d84aed888c664a0f9daa7a6e7b4ef22302d95f3228aa627bc83 + checksum: 510878cc80a844eb5b83460be8e71890743fb9fd70afb3cc46658d3324574d6389542a5dd078c5f613741193d0ac1ff7d2d83b33e447ee0fc1c3699d255ba225 languageName: node linkType: hard @@ -7720,22 +7711,7 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:^7.0.2": - version: 7.5.0 - resolution: "@storybook/theming@npm:7.5.0" - dependencies: - "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.0.0" - "@storybook/client-logger": "npm:7.5.0" - "@storybook/global": "npm:^5.0.0" - memoizerific: "npm:^1.11.3" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 57da8e27c748cbec4dc1661cdd2d449949d97476d8e97933696b31d07c7361cbbcca8d7225cc00ca078daa160023b8965ddec7c23519ce0a4ef2658246b062e7 - languageName: node - linkType: hard - -"@storybook/theming@workspace:*, @storybook/theming@workspace:lib/theming": +"@storybook/theming@npm:^7.0.2, @storybook/theming@workspace:*, @storybook/theming@workspace:lib/theming": version: 0.0.0-use.local resolution: "@storybook/theming@workspace:lib/theming" dependencies: diff --git a/docs/versions/latest.json b/docs/versions/latest.json index c75de132ca72..46c8495a61a7 100644 --- a/docs/versions/latest.json +++ b/docs/versions/latest.json @@ -1 +1 @@ -{"version":"7.6.1","info":{"plain":"- Next.js: Fix AppRouterProvider usage - [#25032](https://github.com/storybookjs/storybook/pull/25032), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- SvelteKit: Fix HMR not working - [#25031](https://github.com/storybookjs/storybook/pull/25031), thanks [@JReinhold](https://github.com/JReinhold)!\n- Test: Downgrade @testing-library/user-event to 14.3.0 - [#25004](https://github.com/storybookjs/storybook/pull/25004), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Webpack: Fix exclude regex in react-docgen-loader - [#25030](https://github.com/storybookjs/storybook/pull/25030), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!"}} +{"version":"7.6.2","info":{"plain":"- CLI: Improve dependency metadata detection in storybook doctor - [#25037](https://github.com/storybookjs/storybook/pull/25037), thanks [@yannbf](https://github.com/yannbf)!"}}