From 22fcb10e9253e0af4c30a7c1e614a94a9a094ac3 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 15 Feb 2024 12:54:05 +0100 Subject: [PATCH 01/39] let 'findInstallations' method of the npm package manager accept a pattern like for the other package managers --- code/lib/cli/src/upgrade.ts | 7 ++----- .../src/js-package-manager/NPMProxy.test.ts | 2 +- .../src/js-package-manager/NPMProxy.ts | 16 +++++++++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index e9f4a8151b1c..3dfd0a2cc707 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -45,11 +45,8 @@ const getInstalledStorybookVersion = async (packageManager: JsPackageManager) => if (!installations) { return; } - const cliVersion = installations.dependencies['@storybook/cli']?.[0].version; - if (cliVersion) { - return cliVersion; - } - return installations.dependencies['storybook']?.[0].version; + + return Object.entries(installations.dependencies)[0]?.[1]?.[0].version; }; const deprecatedPackages = [ diff --git a/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts b/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts index bd5a4372bccf..1445b1be2b87 100644 --- a/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts +++ b/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts @@ -375,7 +375,7 @@ describe('NPM Proxy', () => { } `); - const installations = await npmProxy.findInstallations(); + const installations = await npmProxy.findInstallations(['@storybook/*']); expect(installations).toMatchInlineSnapshot(` { diff --git a/code/lib/core-common/src/js-package-manager/NPMProxy.ts b/code/lib/core-common/src/js-package-manager/NPMProxy.ts index 62d8be2fec5c..b54c0d00e434 100644 --- a/code/lib/core-common/src/js-package-manager/NPMProxy.ts +++ b/code/lib/core-common/src/js-package-manager/NPMProxy.ts @@ -130,7 +130,7 @@ export class NPMProxy extends JsPackageManager { }); } - public async findInstallations() { + public async findInstallations(pattern: string[]) { const exec = async ({ depth }: { depth: number }) => { const pipeToNull = platform() === 'win32' ? '2>NUL' : '2>/dev/null'; return this.executeCommand({ @@ -146,7 +146,7 @@ export class NPMProxy extends JsPackageManager { const commandResult = await exec({ depth: 99 }); const parsedOutput = JSON.parse(commandResult); - return this.mapDependencies(parsedOutput); + return this.mapDependencies(parsedOutput, pattern); } catch (e) { // 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 @@ -154,7 +154,7 @@ export class NPMProxy extends JsPackageManager { const commandResult = await exec({ depth: 0 }); const parsedOutput = JSON.parse(commandResult); - return this.mapDependencies(parsedOutput); + return this.mapDependencies(parsedOutput, pattern); } catch (err) { logger.warn(`An issue occurred while trying to find dependencies metadata using npm.`); return undefined; @@ -245,13 +245,19 @@ export class NPMProxy extends JsPackageManager { } } - protected mapDependencies(input: NpmListOutput): InstallationMetadata { + /** + * + * @param input The output of `npm ls --json` + * @param pattern A list of package names to filter the result. * can be used as a placeholder + */ + protected mapDependencies(input: NpmListOutput, pattern: string[]): InstallationMetadata { const acc: Record = {}; const existingVersions: Record = {}; const duplicatedDependencies: Record = {}; const recurse = ([name, packageInfo]: [string, NpmDependency]): void => { - if (!name || !name.includes('storybook')) return; + // transform pattern into regex where `*` is replaced with `.*` + if (!name || !pattern.some((p) => new RegExp(p.replace(/\*/g, '.*')).test(name))) return; const value = { version: packageInfo.version, From 34a324a490d31db24b13520b2061bf1cb9ec10aa Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 15 Feb 2024 13:50:54 +0100 Subject: [PATCH 02/39] Use the correct cli version of Storybook --- .../lib/cli/src/automigrate/fixes/prompt-remove-react.ts | 2 +- code/lib/cli/src/upgrade.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts index eb93ce015b08..a5bc29074cfa 100644 --- a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts +++ b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts @@ -25,7 +25,7 @@ export const removeReactDependency: Fix<{}> = { } // do not prompt to remove react for older versions of storybook - if (!semver.gte(storybookVersion, '8.0.0')) { + if (!semver.gte(storybookVersion, '8.0.0-alpha.4')) { return null; } diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index 3dfd0a2cc707..a208847ccc03 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -142,11 +142,10 @@ export const doUpgrade = async ({ throw new UpgradeStorybookToSameVersionError({ beforeVersion }); } - const [latestVersion, packageJson, storybookVersion] = await Promise.all([ + const [latestVersion, packageJson] = await Promise.all([ // packageManager.latestVersion('@storybook/cli'), packageManager.retrievePackageJson(), - getCoercedStorybookVersion(packageManager), ]); const isOutdated = lt(currentVersion, latestVersion); @@ -194,7 +193,7 @@ export const doUpgrade = async ({ }); // GUARDS - if (!storybookVersion) { + if (!beforeVersion) { logger.info(missingStorybookVersionMessage()); results = { preCheckFailure: PreCheckFailure.UNDETECTED_SB_VERSION }; } else if ( @@ -262,7 +261,7 @@ export const doUpgrade = async ({ } // AUTOMIGRATIONS - if (!skipCheck && !results && mainConfigPath && storybookVersion) { + if (!skipCheck && !results && mainConfigPath) { checkVersionConsistency(); results = await automigrate({ dryRun, @@ -270,7 +269,7 @@ export const doUpgrade = async ({ packageManager, configDir, mainConfigPath, - storybookVersion, + storybookVersion: currentVersion, }); } From 3514a770a2e8a221c77949f1b915569d829c025b Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 15 Feb 2024 14:11:09 +0100 Subject: [PATCH 03/39] Improve findInstallations method in pnpm yarn1 and yarn2 envs --- code/lib/core-common/src/js-package-manager/PNPMProxy.ts | 7 ++++--- .../lib/core-common/src/js-package-manager/Yarn1Proxy.ts | 6 +++--- .../lib/core-common/src/js-package-manager/Yarn2Proxy.ts | 9 ++++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/code/lib/core-common/src/js-package-manager/PNPMProxy.ts b/code/lib/core-common/src/js-package-manager/PNPMProxy.ts index 7e7f0279e226..2eeda6d12dce 100644 --- a/code/lib/core-common/src/js-package-manager/PNPMProxy.ts +++ b/code/lib/core-common/src/js-package-manager/PNPMProxy.ts @@ -107,7 +107,7 @@ export class PNPMProxy extends JsPackageManager { try { const parsedOutput = JSON.parse(commandResult); - return this.mapDependencies(parsedOutput); + return this.mapDependencies(parsedOutput, pattern); } catch (e) { return undefined; } @@ -241,7 +241,7 @@ export class PNPMProxy extends JsPackageManager { } } - protected mapDependencies(input: PnpmListOutput): InstallationMetadata { + protected mapDependencies(input: PnpmListOutput, pattern: string[]): InstallationMetadata { const acc: Record = {}; const existingVersions: Record = {}; const duplicatedDependencies: Record = {}; @@ -252,7 +252,8 @@ export class PNPMProxy extends JsPackageManager { }, {} as PnpmDependencies); const recurse = ([name, packageInfo]: [string, PnpmDependency]): void => { - if (!name || !name.includes('storybook')) return; + // transform pattern into regex where `*` is replaced with `.*` + if (!name || !pattern.some((p) => new RegExp(p.replace(/\*/g, '.*')).test(name))) return; const value = { version: packageInfo.version, diff --git a/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts index 2018e8f8ddf4..c43fe9103c92 100644 --- a/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts @@ -92,7 +92,7 @@ export class Yarn1Proxy extends JsPackageManager { try { const parsedOutput = JSON.parse(commandResult); - return this.mapDependencies(parsedOutput); + return this.mapDependencies(parsedOutput, pattern); } catch (e) { return undefined; } @@ -179,7 +179,7 @@ export class Yarn1Proxy extends JsPackageManager { } } - protected mapDependencies(input: Yarn1ListOutput): InstallationMetadata { + protected mapDependencies(input: Yarn1ListOutput, pattern: string[]): InstallationMetadata { if (input.type === 'tree') { const { trees } = input.data; const acc: Record = {}; @@ -189,7 +189,7 @@ export class Yarn1Proxy extends JsPackageManager { const recurse = (tree: (typeof trees)[0]) => { const { children } = tree; const { name, value } = parsePackageData(tree.name); - if (!name || !name.includes('storybook')) return; + if (!name || !pattern.some((p) => new RegExp(p.replace(/\*/g, '.*')).test(name))) return; if (!existingVersions[name]?.includes(value.version)) { if (acc[name]) { acc[name].push(value); diff --git a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts index 4f76868d395c..ce5c5c1335ca 100644 --- a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts @@ -119,7 +119,7 @@ export class Yarn2Proxy extends JsPackageManager { }); try { - return this.mapDependencies(commandResult); + return this.mapDependencies(commandResult, pattern); } catch (e) { return undefined; } @@ -252,14 +252,17 @@ export class Yarn2Proxy extends JsPackageManager { } } - protected mapDependencies(input: string): InstallationMetadata { + protected mapDependencies(input: string, pattern: string[]): InstallationMetadata { const lines = input.split('\n'); const acc: Record = {}; const existingVersions: Record = {}; const duplicatedDependencies: Record = {}; lines.forEach((packageName) => { - if (!packageName || !packageName.includes('storybook')) { + if ( + !packageName || + !pattern.some((p) => new RegExp(p.replace(/\*/g, '.*')).test(packageName)) + ) { return; } From bcb041865e986a21ef582d44ac43844da0b45962 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 15 Feb 2024 14:19:57 +0100 Subject: [PATCH 04/39] Improve findInstallations method in pnpm, yarn1 and yarn2 envs --- code/lib/core-common/src/js-package-manager/NPMProxy.ts | 4 +++- code/lib/core-common/src/js-package-manager/PNPMProxy.ts | 4 +++- code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts | 5 ++++- code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/code/lib/core-common/src/js-package-manager/NPMProxy.ts b/code/lib/core-common/src/js-package-manager/NPMProxy.ts index b54c0d00e434..8e996861f32a 100644 --- a/code/lib/core-common/src/js-package-manager/NPMProxy.ts +++ b/code/lib/core-common/src/js-package-manager/NPMProxy.ts @@ -257,7 +257,9 @@ export class NPMProxy extends JsPackageManager { const recurse = ([name, packageInfo]: [string, NpmDependency]): void => { // transform pattern into regex where `*` is replaced with `.*` - if (!name || !pattern.some((p) => new RegExp(p.replace(/\*/g, '.*')).test(name))) return; + if (!name || !pattern.some((p) => new RegExp(`^${p.replace(/\*/g, '.*')}$`).test(name))) { + return; + } const value = { version: packageInfo.version, diff --git a/code/lib/core-common/src/js-package-manager/PNPMProxy.ts b/code/lib/core-common/src/js-package-manager/PNPMProxy.ts index 2eeda6d12dce..a2f8baf6206b 100644 --- a/code/lib/core-common/src/js-package-manager/PNPMProxy.ts +++ b/code/lib/core-common/src/js-package-manager/PNPMProxy.ts @@ -253,7 +253,9 @@ export class PNPMProxy extends JsPackageManager { const recurse = ([name, packageInfo]: [string, PnpmDependency]): void => { // transform pattern into regex where `*` is replaced with `.*` - if (!name || !pattern.some((p) => new RegExp(p.replace(/\*/g, '.*')).test(name))) return; + if (!name || !pattern.some((p) => new RegExp(`^${p.replace(/\*/g, '.*')}$`).test(name))) { + return; + } const value = { version: packageInfo.version, diff --git a/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts index c43fe9103c92..5980b26accc4 100644 --- a/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts @@ -189,7 +189,10 @@ export class Yarn1Proxy extends JsPackageManager { const recurse = (tree: (typeof trees)[0]) => { const { children } = tree; const { name, value } = parsePackageData(tree.name); - if (!name || !pattern.some((p) => new RegExp(p.replace(/\*/g, '.*')).test(name))) return; + if (!name || !pattern.some((p) => new RegExp(`^${p.replace(/\*/g, '.*')}$`).test(name))) { + return; + } + if (!existingVersions[name]?.includes(value.version)) { if (acc[name]) { acc[name].push(value); diff --git a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts index ce5c5c1335ca..4b69af2c40ff 100644 --- a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts @@ -261,7 +261,7 @@ export class Yarn2Proxy extends JsPackageManager { lines.forEach((packageName) => { if ( !packageName || - !pattern.some((p) => new RegExp(p.replace(/\*/g, '.*')).test(packageName)) + !pattern.some((p) => new RegExp(`"${p.replace(/\*/g, '.*')}`).test(packageName)) ) { return; } From 8c9fc3925fa931da802939d2465329ed0cc4caa2 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 15 Feb 2024 14:20:37 +0100 Subject: [PATCH 05/39] Adjust findInstallations to cover all kind of Storybook 6 installations --- code/lib/cli/src/upgrade.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index a208847ccc03..1b26654a9bf8 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -41,7 +41,14 @@ export const getStorybookVersion = (line: string) => { }; const getInstalledStorybookVersion = async (packageManager: JsPackageManager) => { - const installations = await packageManager.findInstallations(['storybook', '@storybook/cli']); + const installations = await packageManager.findInstallations([ + // Storybook 3.0.0+ | the installation wasn't required though in Storybook < 7.0.0 + '@storybook/cli', + // Storybook 3.3.3 - 6.5.16 + '@storybook/core', + // Storybook 6.2+ + '@storybook/core-common', + ]); if (!installations) { return; } From e1ba87cacab9109080b792b96d02249e05215a5c Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 16 Feb 2024 14:43:33 +0100 Subject: [PATCH 06/39] Implement version checks to support automigrations from 6 to 8 --- .../autoblock/block-dependencies-versions.ts | 3 +- .../fixes/angular-builders-multiproject.ts | 2 + .../fixes/angular-builders.test.ts | 18 -- .../src/automigrate/fixes/angular-builders.ts | 10 +- .../src/automigrate/fixes/autodocs-true.ts | 2 + .../fixes/bare-mdx-stories-glob.test.ts | 13 +- ...ries-glob.ts => bare-mdx-stories-glob.tsx} | 14 +- .../cli/src/automigrate/fixes/builder-vite.ts | 2 + code/lib/cli/src/automigrate/fixes/cra5.ts | 2 + .../src/automigrate/fixes/eslint-plugin.ts | 2 + code/lib/cli/src/automigrate/fixes/index.ts | 6 - .../src/automigrate/fixes/mdx-1-to-2.test.ts | 120 ------------- .../cli/src/automigrate/fixes/mdx-1-to-2.ts | 83 --------- code/lib/cli/src/automigrate/fixes/mdx-gfm.ts | 11 +- .../automigrate/fixes/new-frameworks.test.ts | 13 -- .../src/automigrate/fixes/new-frameworks.ts | 17 +- .../automigrate/fixes/prompt-remove-react.ts | 9 +- .../cli/src/automigrate/fixes/react-docgen.ts | 4 +- .../fixes/remove-global-client-apis.ts | 2 + .../src/automigrate/fixes/sb-binary.test.ts | 25 --- .../cli/src/automigrate/fixes/sb-binary.ts | 5 +- .../cli/src/automigrate/fixes/sb-scripts.ts | 2 + .../src/automigrate/fixes/vite-config-file.ts | 6 +- code/lib/cli/src/automigrate/fixes/vite4.ts | 43 ----- code/lib/cli/src/automigrate/fixes/vue3.ts | 2 + .../lib/cli/src/automigrate/fixes/webpack5.ts | 4 +- .../cli/src/automigrate/fixes/wrap-require.ts | 2 + code/lib/cli/src/automigrate/index.test.ts | 157 ++++++++++++++++++ code/lib/cli/src/automigrate/index.ts | 43 +++-- code/lib/cli/src/automigrate/types.ts | 14 ++ code/lib/cli/src/upgrade.ts | 3 +- 31 files changed, 261 insertions(+), 378 deletions(-) rename code/lib/cli/src/automigrate/fixes/{bare-mdx-stories-glob.ts => bare-mdx-stories-glob.tsx} (95%) delete mode 100644 code/lib/cli/src/automigrate/fixes/mdx-1-to-2.test.ts delete mode 100644 code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts delete mode 100644 code/lib/cli/src/automigrate/fixes/vite4.ts create mode 100644 code/lib/cli/src/automigrate/index.test.ts diff --git a/code/lib/cli/src/autoblock/block-dependencies-versions.ts b/code/lib/cli/src/autoblock/block-dependencies-versions.ts index 284562aa9f6d..d2a0a31d456f 100644 --- a/code/lib/cli/src/autoblock/block-dependencies-versions.ts +++ b/code/lib/cli/src/autoblock/block-dependencies-versions.ts @@ -9,6 +9,7 @@ const minimalVersionsMap = { preact: '10.0.0', svelte: '4.0.0', vue: '3.0.0', + vite: '4.0.0', }; type Result = { @@ -83,7 +84,7 @@ export const blocker = createBlocker({ default: return dedent` Support for ${data.packageName} version < ${data.minimumVersion} has been removed. - Storybook 8 needs minimum version of ${data.minimumVersion}, but you had version ${data.installedVersion}. + Since version 8, Storybook needs minimum version of ${data.minimumVersion}, but you had version ${data.installedVersion}. Please update this dependency. `; diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts index 9bf74a288416..41b253084b0f 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts @@ -12,6 +12,8 @@ export const angularBuildersMultiproject: Fix=7'], + async check({ packageManager, mainConfig }) { // Skip in case of NX const angularVersion = await packageManager.getPackageVersion('@angular/core'); diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts b/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts index 2b9623e26d93..e3825b0dae65 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts @@ -71,24 +71,6 @@ describe('is not Nx project', () => { }); }); - describe('Angular < 15.0.0', () => { - const packageManager = { - getPackageVersion: (packageName: string) => { - if (packageName === '@angular/core') { - return Promise.resolve('14.0.0'); - } - - return null; - }, - } as Partial; - - it('should throw an Error', async () => { - await expect( - checkAngularBuilders({ packageManager, mainConfig: { framework: '@storybook/angular' } }) - ).rejects.toThrowErrorMatchingSnapshot(); - }); - }); - describe('Angular >= 16.0.0', () => { const packageManager = { getPackageVersion: (packageName) => { diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders.ts b/code/lib/cli/src/automigrate/fixes/angular-builders.ts index b0c40a0f6991..ac2f2af99d2e 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders.ts @@ -1,5 +1,4 @@ import { dedent } from 'ts-dedent'; -import semver from 'semver'; import type { StorybookConfig } from '@storybook/types'; import chalk from 'chalk'; import prompts from 'prompts'; @@ -17,6 +16,8 @@ interface AngularBuildersRunOptions { export const angularBuilders: Fix = { id: 'angular-builders', + versionRange: ['<7', '>=7'], + async check({ packageManager, mainConfig }) { const angularVersion = await packageManager.getPackageVersion('@angular/core'); @@ -31,13 +32,6 @@ export const angularBuilders: Fix = { return null; } - if (semver.lt(angularVersion, '15.0.0')) { - throw new Error(dedent` - ❌ Your project uses Angular < 15.0.0. Storybook 8.0 for Angular requires Angular 15.0.0 or higher. - Please upgrade your Angular version to at least version 15.0.0 to use Storybook 8.0 in your project. - `); - } - const angularJSON = new AngularJSON(); const { hasStorybookBuilder } = angularJSON; diff --git a/code/lib/cli/src/automigrate/fixes/autodocs-true.ts b/code/lib/cli/src/automigrate/fixes/autodocs-true.ts index f84c3f7ee778..e5c2aceda7c2 100644 --- a/code/lib/cli/src/automigrate/fixes/autodocs-true.ts +++ b/code/lib/cli/src/automigrate/fixes/autodocs-true.ts @@ -16,6 +16,8 @@ interface AutodocsTrueFrameworkRunOptions { export const autodocsTrue: Fix = { id: 'autodocsTrue', + versionRange: ['<7', '>=7'], + async check({ mainConfig }) { const { docs } = mainConfig; diff --git a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts index 27154a2a63a2..c2574fa2d1e3 100644 --- a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts +++ b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts @@ -29,16 +29,6 @@ describe('bare-mdx fix', () => { }); describe('should no-op', () => { - it('in SB < v7.0.0', async () => { - const packageJson = { - dependencies: { '@storybook/react': '^6.2.0' }, - }; - const main = { stories: ['../**/*.stories.mdx'] }; - await expect( - checkBareMdxStoriesGlob({ packageJson, main, storybookVersion: '6.5.0' }) - ).resolves.toBeFalsy(); - }); - describe('in SB >= v7.0.0', () => { it('without main', async () => { const packageJson = { @@ -162,7 +152,7 @@ describe('bare-mdx fix', () => { } In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx. - + Now, since Storybook 8.0, we have removed support for .stories.mdx files. We can automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx. That would result in the following 'stories' config: "../src/**/*.mdx" @@ -171,7 +161,6 @@ describe('bare-mdx fix', () => { "directory": "../src/**", "files": "*.mdx" } - To learn more about this change, see: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mdx-docs-files" `); }); diff --git a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.tsx similarity index 95% rename from code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts rename to code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.tsx index 175e69c23b14..28fb62c6b636 100644 --- a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts +++ b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.tsx @@ -1,6 +1,5 @@ import chalk from 'chalk'; import dedent from 'ts-dedent'; -import semver from 'semver'; import type { StoriesEntry } from '@storybook/types'; import { updateMainConfig } from '../helpers/mainConfigFile'; import type { Fix } from '../types'; @@ -31,11 +30,8 @@ const getNextGlob = (glob: string) => { export const bareMdxStoriesGlob: Fix = { id: 'bare-mdx-stories-glob', - async check({ storybookVersion, mainConfig }) { - if (!semver.gte(storybookVersion, '7.0.0')) { - return null; - } - + versionRange: ['<7', '>=7'], + async check({ mainConfig }) { const existingStoriesEntries = mainConfig.stories as StoriesEntry[]; if (!existingStoriesEntries) { @@ -45,10 +41,9 @@ export const bareMdxStoriesGlob: Fix = { )}, skipping ${chalk.cyan(this.id)} fix. In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx. - + Now, since Storybook 8.0, we have removed support for .stories.mdx files. We were unable to automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx. We suggest you make this change manually. - To learn more about this change, see: ${chalk.yellow( 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mdx-docs-files' )} @@ -100,11 +95,10 @@ export const bareMdxStoriesGlob: Fix = { ${chalk.cyan(prettyExistingStoriesEntries)} In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx. - + Now, since Storybook 8.0, we have removed support for .stories.mdx files. We can automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx. That would result in the following 'stories' config: ${chalk.cyan(prettyNextStoriesEntries)} - To learn more about this change, see: ${chalk.yellow( 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mdx-docs-files' )} diff --git a/code/lib/cli/src/automigrate/fixes/builder-vite.ts b/code/lib/cli/src/automigrate/fixes/builder-vite.ts index 8662bb6cc21b..b38cf9ccb677 100644 --- a/code/lib/cli/src/automigrate/fixes/builder-vite.ts +++ b/code/lib/cli/src/automigrate/fixes/builder-vite.ts @@ -27,6 +27,8 @@ interface BuilderViteOptions { export const builderVite: Fix = { id: 'builder-vite', + versionRange: ['<7', '>=7'], + async check({ packageManager, mainConfig }) { const packageJson = await packageManager.retrievePackageJson(); const builder = mainConfig.core?.builder; diff --git a/code/lib/cli/src/automigrate/fixes/cra5.ts b/code/lib/cli/src/automigrate/fixes/cra5.ts index d3786cd2d00f..468fbe90947b 100644 --- a/code/lib/cli/src/automigrate/fixes/cra5.ts +++ b/code/lib/cli/src/automigrate/fixes/cra5.ts @@ -20,6 +20,8 @@ interface CRA5RunOptions { export const cra5: Fix = { id: 'cra5', + versionRange: ['<7', '>=7'], + async check({ packageManager, mainConfig, storybookVersion }) { const craVersion = await packageManager.getPackageVersion('react-scripts'); diff --git a/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts b/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts index 25e5dfd03204..de81e08008d8 100644 --- a/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts +++ b/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts @@ -26,6 +26,8 @@ interface EslintPluginRunOptions { export const eslintPlugin: Fix = { id: 'eslintPlugin', + versionRange: ['<8', '>=7'], + async check({ packageManager }) { const { hasEslint, isStorybookPluginInstalled } = await extractEslintInfo(packageManager); diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts index 030e31baa6dc..6b1b4bc90124 100644 --- a/code/lib/cli/src/automigrate/fixes/index.ts +++ b/code/lib/cli/src/automigrate/fixes/index.ts @@ -2,7 +2,6 @@ import type { Fix } from '../types'; import { cra5 } from './cra5'; import { webpack5 } from './webpack5'; -import { vite4 } from './vite4'; import { vue3 } from './vue3'; import { mdxgfm } from './mdx-gfm'; import { eslintPlugin } from './eslint-plugin'; @@ -12,10 +11,8 @@ import { sbScripts } from './sb-scripts'; import { sbBinary } from './sb-binary'; import { newFrameworks } from './new-frameworks'; import { removedGlobalClientAPIs } from './remove-global-client-apis'; -import { mdx1to2 } from './mdx-1-to-2'; import { autodocsTrue } from './autodocs-true'; import { angularBuilders } from './angular-builders'; -import { incompatibleAddons } from './incompatible-addons'; import { angularBuildersMultiproject } from './angular-builders-multiproject'; import { wrapRequire } from './wrap-require'; import { reactDocgen } from './react-docgen'; @@ -30,16 +27,13 @@ export const allFixes: Fix[] = [ cra5, webpack5, vue3, - vite4, viteConfigFile, eslintPlugin, builderVite, sbBinary, sbScripts, - incompatibleAddons, removeJestTestingLibrary, removedGlobalClientAPIs, - mdx1to2, mdxgfm, autodocsTrue, angularBuildersMultiproject, diff --git a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.test.ts b/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.test.ts deleted file mode 100644 index c471d478fdc6..000000000000 --- a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.test.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { it, expect } from 'vitest'; - -import { dedent } from 'ts-dedent'; -import { fixMdxStyleTags, fixMdxComments } from './mdx-1-to-2'; - -it('fixMdxStyleTags fixes badly-formatted style blocks', () => { - expect( - fixMdxStyleTags(dedent` - - `) - ).toEqual(dedent` - - `); -}); - -it('fixMdxStyleTags fixes multiple style blocks', () => { - expect( - fixMdxStyleTags(dedent` - - - `) - ).toMatchInlineSnapshot(` - " - " - `); -}); - -it('fixMdxComments fixes all comments', () => { - expect( - fixMdxComments(dedent` - # Hello - - - - and this is not - - - `) - ).toMatchInlineSnapshot(` - "# Hello - - {/* This is a comment */} - - and this is not - - {/* This is another comment */}" - `); -}); - -it('fixMdxComments keeps html comments in codeblocks', () => { - expect( - fixMdxComments(dedent` - # Hello - - ~~~html - - ~~~ - - ~~~html - - ~~~ - - \`\`\`html - - \`\`\` - - \`\`\`html - - \`\`\` - - and this is not - - - `) - ).toMatchInlineSnapshot(` - "# Hello - - ~~~html - - ~~~ - - ~~~html - - ~~~ - - \`\`\`html - - \`\`\` - - \`\`\`html - - \`\`\` - - and this is not - - {/* This is another comment */}" - `); -}); diff --git a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts b/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts deleted file mode 100644 index 108dde3c5aa3..000000000000 --- a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts +++ /dev/null @@ -1,83 +0,0 @@ -import chalk from 'chalk'; -import { dedent } from 'ts-dedent'; -import { basename } from 'path'; -import fse from 'fs-extra'; -import globby from 'globby'; -import type { Fix } from '../types'; - -const MDX1_STYLE_START = /'); -}; - -export const fixMdxComments = (mdx: string) => { - const codeblocks = mdx.matchAll(MDX1_CODEBLOCK); - - // separate the mdx into sections without codeblocks & replace html comments NOT in codeblocks - const sections = mdx - .split(MDX1_CODEBLOCK) - .map((v) => v.replace(MDX1_COMMENT, (original, group) => `{/*${group}*/}`)); - - // interleave the original codeblocks with the replaced sections - return sections.reduce((acc, item, i) => { - const next = codeblocks.next(); - return next.done ? acc + item : acc + item + next.value[0]; - }, ''); -}; - -const logger = console; - -interface Mdx1to2Options { - storiesMdxFiles: string[]; -} - -/** - * Does the user have `.stories.mdx` files? - * - * If so: - * - Assume they might be MDX1 - * - Offer to help migrate to MDX2 - */ -export const mdx1to2: Fix = { - id: 'mdx1to2', - - async check() { - const storiesMdxFiles = await globby('./!(node_modules)**/*.(story|stories).mdx'); - return storiesMdxFiles.length ? { storiesMdxFiles } : null; - }, - - prompt({ storiesMdxFiles }) { - return dedent` - We've found ${chalk.yellow(storiesMdxFiles.length)} '.stories.mdx' files in your project. - - Storybook has upgraded to MDX2 (https://mdxjs.com/blog/v2/), which contains breaking changes from MDX1. - We can try to automatically upgrade your MDX files to MDX2 format using some common patterns. - - After this install completes, and before you start Storybook, we strongly recommend reading the MDX2 section - of the 7.0 migration guide. It contains useful tools for detecting and fixing any remaining issues. - - ${chalk.cyan('https://storybook.js.org/migration-guides/7.0')} - `; - }, - - async run({ result: { storiesMdxFiles }, dryRun }) { - await Promise.all([ - ...storiesMdxFiles.map(async (fname) => { - const contents = await fse.readFile(fname, 'utf-8'); - const updated = fixMdxComments(fixMdxStyleTags(contents)); - if (updated === contents) { - logger.info(`🆗 Unmodified ${basename(fname)}`); - } else { - logger.info(`✅ Modified ${basename(fname)}`); - if (!dryRun) { - await fse.writeFile(fname, updated); - } - } - }), - ]); - }, -}; diff --git a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts index f844decf1443..651b18dbc228 100644 --- a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts +++ b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts @@ -1,5 +1,4 @@ import { dedent } from 'ts-dedent'; -import semver from 'semver'; import { join } from 'path'; import slash from 'slash'; import glob from 'globby'; @@ -19,11 +18,9 @@ interface Options { export const mdxgfm: Fix = { id: 'github-flavored-markdown-mdx', - async check({ configDir, mainConfig, storybookVersion }) { - if (!semver.gte(storybookVersion, '7.0.0')) { - return null; - } + versionRange: ['<7', '>=7'], + async check({ configDir, mainConfig }) { const hasMDXFiles = await mainConfig?.stories?.reduce(async (acc, item) => { const val = await acc; @@ -82,11 +79,11 @@ export const mdxgfm: Fix = { return dedent` In MDX1 you had the option of using GitHub flavored markdown. - Storybook 8.0 uses MDX3 for compiling MDX, and thus no longer supports GFM out of the box. + Storybook >= 8.0 uses MDX3 for compiling MDX, and thus no longer supports GFM out of the box. Because of this you need to explicitly add the GFM plugin in the addon-docs options: https://storybook.js.org/docs/react/writing-docs/mdx#lack-of-github-flavored-markdown-gfm - We recommend you follow the guide on the link above, however we can add a temporary storybook addon that helps make this migration easier. + We recommend you follow the guide on the link above, however we can add a temporary Storybook addon that helps make this migration easier. We'll install the addon and add it to your storybook config. `; }, diff --git a/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts b/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts index 4cc2669b91ae..96099855df73 100644 --- a/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts +++ b/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts @@ -57,19 +57,6 @@ const getPackageManager = (packages: Record) => { describe('new-frameworks fix', () => { describe('should no-op', () => { - it('in sb < 7', async () => { - const packageManager = getPackageManager({ - '@storybook/vue': '6.2.0', - }); - - await expect( - checkNewFrameworks({ - packageManager, - storybookVersion: '6.2.0', - }) - ).resolves.toBeFalsy(); - }); - it('in sb 7 with correct structure already', async () => { const packageManager = getPackageManager({ '@storybook/angular': '7.0.0', diff --git a/code/lib/cli/src/automigrate/fixes/new-frameworks.ts b/code/lib/cli/src/automigrate/fixes/new-frameworks.ts index f335d654050b..97ace48c8ee9 100644 --- a/code/lib/cli/src/automigrate/fixes/new-frameworks.ts +++ b/code/lib/cli/src/automigrate/fixes/new-frameworks.ts @@ -58,18 +58,9 @@ interface NewFrameworkRunOptions { export const newFrameworks: Fix = { id: 'new-frameworks', - async check({ - configDir, - packageManager, - storybookVersion, - mainConfig, - mainConfigPath, - rendererPackage, - }) { - if (!semver.gte(storybookVersion, '7.0.0')) { - return null; - } + versionRange: ['<7', '>=7'], + async check({ configDir, packageManager, mainConfig, mainConfigPath, rendererPackage }) { if (typeof configDir === 'undefined') { return null; } @@ -219,7 +210,7 @@ export const newFrameworks: Fix = { newFrameworkPackage )}, but we detected that you are using Vite ${chalk.bold( viteVersion - )}, which is unsupported in ${chalk.bold( + )}, which is unsupported since ${chalk.bold( 'Storybook 7.0' )}. Please upgrade Vite to ${chalk.bold('3.0.0 or higher')} and rerun this migration. `); @@ -406,7 +397,7 @@ export const newFrameworks: Fix = { } return dedent` - We've detected your project is not fully setup with Storybook's 7 new framework format. + We've detected your project is not fully setup with the new framework format, which was introduced in Storybook 7. Storybook 7 introduced the concept of frameworks, which abstracts configuration for renderers (e.g. React, Vue), builders (e.g. Webpack, Vite) and defaults to make integrations easier. diff --git a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts index a5bc29074cfa..096bf83a030d 100644 --- a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts +++ b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts @@ -3,10 +3,15 @@ import semver from 'semver'; import { getFrameworkPackageName } from '../helpers/mainConfigFile'; import type { Fix } from '../types'; +// This fix is only relevant for projects using Storybook 8.0.0-alpha.4 or later +const minimumStorybookVersion = '8.0.0-alpha.4'; + export const removeReactDependency: Fix<{}> = { id: 'remove-react-dependency', promptOnly: true, + versionRange: ['>=7 <8.0.0-alpha.4', `>=${minimumStorybookVersion}`], + async check({ packageManager, mainConfig, storybookVersion }) { // when the user is using the react renderer, we should not prompt them to remove react const frameworkPackageName = getFrameworkPackageName(mainConfig); @@ -25,7 +30,7 @@ export const removeReactDependency: Fix<{}> = { } // do not prompt to remove react for older versions of storybook - if (!semver.gte(storybookVersion, '8.0.0-alpha.4')) { + if (!semver.gte(storybookVersion, minimumStorybookVersion)) { return null; } @@ -36,7 +41,7 @@ export const removeReactDependency: Fix<{}> = { We detected that your project has a dependency for "react" that it might not need. Nothing breaks by having it, you can safely ignore this message, if you wish. - Storybook asked you to add "react" as a direct dependency in the past. + Storybook asked you to add "react" as a direct dependency in the past when upgrading from Storybook 6 to 7. However, since version 8.0, Storybook no longer requires you to provide "react" as a dependency. Some community addons might still wrongfully list "react" and "react-dom" as required peer dependencies, but since Storybook 7.6 it should not be needed in the majority of cases. diff --git a/code/lib/cli/src/automigrate/fixes/react-docgen.ts b/code/lib/cli/src/automigrate/fixes/react-docgen.ts index e21c73db3577..ef89a24915ff 100644 --- a/code/lib/cli/src/automigrate/fixes/react-docgen.ts +++ b/code/lib/cli/src/automigrate/fixes/react-docgen.ts @@ -13,6 +13,8 @@ interface Options { export const reactDocgen: Fix = { id: 'react-docgen', + versionRange: ['<8.0.0-alpha.1', '>=8.0.0-alpha.1'], + async check({ mainConfig }) { // @ts-expect-error assume react const { reactDocgenTypescriptOptions } = mainConfig.typescript || {}; @@ -25,7 +27,7 @@ export const reactDocgen: Fix = { You have "typescript.reactDocgenTypescriptOptions" configured in your main.js, but "typescript.reactDocgen" is unset. - In Storybook 8.0, we changed the default React docgen analysis from + Since Storybook 8.0, we changed the default React docgen analysis from "react-docgen-typescript" to "react-docgen". We recommend "react-docgen" for most projects, since it is dramatically faster. However, it doesn't handle all TypeScript constructs, and may generate different results diff --git a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts index 4a1304dae1a6..8c02f62a9101 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts @@ -21,6 +21,8 @@ export const removedGlobalClientAPIs: Fix = { id: 'removedglobalclientapis', promptOnly: true, + versionRange: ['<7', '>=7'], + async check({ previewConfigPath }) { if (previewConfigPath) { const contents = await readFile(previewConfigPath, 'utf8'); diff --git a/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts b/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts index b1f06e162c30..bb5250179a89 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts @@ -17,31 +17,6 @@ const checkStorybookBinary = async ({ }; describe('storybook-binary fix', () => { - describe('sb < 7.0', () => { - describe('does nothing', () => { - const packageManager = { - getPackageVersion: (packageName) => { - switch (packageName) { - case '@storybook/react': - return Promise.resolve('6.2.0'); - default: - return null; - } - }, - retrievePackageJson: () => Promise.resolve({}), - } as Partial; - - it('should no-op', async () => { - await expect( - checkStorybookBinary({ - packageManager, - storybookVersion: '6.2.0', - }) - ).resolves.toBeFalsy(); - }); - }); - }); - describe('sb >= 7.0', () => { it('should no-op in NX projects', async () => { const packageManager = { diff --git a/code/lib/cli/src/automigrate/fixes/sb-binary.ts b/code/lib/cli/src/automigrate/fixes/sb-binary.ts index 4c84cf987d04..53b583b8a58f 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-binary.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-binary.ts @@ -1,6 +1,5 @@ import chalk from 'chalk'; import { dedent } from 'ts-dedent'; -import semver from 'semver'; import type { Fix } from '../types'; import { getStorybookVersionSpecifier } from '../../helpers'; import type { PackageJsonWithDepsAndDevDeps } from '@storybook/core-common'; @@ -24,6 +23,8 @@ const logger = console; export const sbBinary: Fix = { id: 'storybook-binary', + versionRange: ['<7', '>=7'], + async check({ packageManager, storybookVersion }) { const packageJson = await packageManager.retrievePackageJson(); @@ -32,7 +33,7 @@ export const sbBinary: Fix = { const storybookBinaryVersion = await packageManager.getPackageVersion('storybook'); // Nx provides their own binary, so we don't need to do anything - if (nrwlStorybookVersion || semver.lt(storybookVersion, '7.0.0')) { + if (nrwlStorybookVersion) { return null; } diff --git a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts index 7ff111920e27..aad1796b9a38 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts @@ -78,6 +78,8 @@ export const getStorybookScripts = (allScripts: NonNullable = { id: 'sb-scripts', + versionRange: ['<7', '>=7'], + async check({ packageManager, storybookVersion }) { const packageJson = await packageManager.retrievePackageJson(); const { scripts = {} } = packageJson; diff --git a/code/lib/cli/src/automigrate/fixes/vite-config-file.ts b/code/lib/cli/src/automigrate/fixes/vite-config-file.ts index f8047a839af9..6b2e105f1120 100644 --- a/code/lib/cli/src/automigrate/fixes/vite-config-file.ts +++ b/code/lib/cli/src/automigrate/fixes/vite-config-file.ts @@ -13,6 +13,8 @@ interface Webpack5RunOptions { export const viteConfigFile = { id: 'viteConfigFile', + versionRange: ['<8.0.0-beta.3', '>=8.0.0-beta.3'], + async check({ mainConfig, packageManager }) { const viteConfigPath = await findUp([ 'vite.config.js', @@ -85,7 +87,7 @@ export const viteConfigFile = { prompt({ existed, plugins }) { if (existed) { return dedent` - Storybook 8.0.0 no longer ships with a Vite config build-in. + Since version 8.0.0, Storybook no longer ships with a Vite config build-in. We've detected you do have a Vite config, but you may be missing the following plugins in it. ${plugins.map((plugin) => ` - ${plugin}`).join('\n')} @@ -99,7 +101,7 @@ export const viteConfigFile = { `; } return dedent` - Storybook 8.0.0 no longer ships with a Vite config build-in. + Since version 8.0.0, Storybook no longer ships with a Vite config build-in. Please add a vite.config.js file to your project root. You can find more information on how to do this here: diff --git a/code/lib/cli/src/automigrate/fixes/vite4.ts b/code/lib/cli/src/automigrate/fixes/vite4.ts deleted file mode 100644 index d04c4abd10d7..000000000000 --- a/code/lib/cli/src/automigrate/fixes/vite4.ts +++ /dev/null @@ -1,43 +0,0 @@ -import chalk from 'chalk'; -import { dedent } from 'ts-dedent'; -import semver from 'semver'; -import type { Fix } from '../types'; - -const logger = console; - -interface Webpack5RunOptions { - viteVersion: string | null; -} - -export const vite4 = { - id: 'vite4', - - async check({ packageManager }) { - const viteVersion = await packageManager.getPackageVersion('vite'); - - if (!viteVersion || semver.gt(viteVersion, '4.0.0')) { - return null; - } - - return { viteVersion }; - }, - - prompt({ viteVersion: viteVersion }) { - const viteFormatted = chalk.cyan(`${viteVersion}`); - - return dedent` - We've detected your version of Vite is outdated (${viteFormatted}). - - Storybook 8.0.0 will require Vite 4.0.0 or later. - Do you want us to upgrade Vite for you? - `; - }, - - async run({ packageManager, dryRun }) { - const deps = [`vite`]; - logger.info(`✅ Adding dependencies: ${deps}`); - if (!dryRun) { - await packageManager.addDependencies({ installAsDevDependencies: true }, deps); - } - }, -} satisfies Fix; diff --git a/code/lib/cli/src/automigrate/fixes/vue3.ts b/code/lib/cli/src/automigrate/fixes/vue3.ts index 84bde42d6196..f641b30373de 100644 --- a/code/lib/cli/src/automigrate/fixes/vue3.ts +++ b/code/lib/cli/src/automigrate/fixes/vue3.ts @@ -19,6 +19,8 @@ interface Vue3RunOptions { export const vue3: Fix = { id: 'vue3', + versionRange: ['<7', '>=7'], + async check({ packageManager, mainConfig, storybookVersion }) { const vueVersion = await packageManager.getPackageVersion('vue'); diff --git a/code/lib/cli/src/automigrate/fixes/webpack5.ts b/code/lib/cli/src/automigrate/fixes/webpack5.ts index 8b54ee2d5a8c..a2072be41eb9 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5.ts @@ -25,7 +25,9 @@ interface Webpack5RunOptions { export const webpack5 = { id: 'webpack5', - async check({ configDir, packageManager, mainConfig, storybookVersion }) { + versionRange: ['<7', '>=7'], + + async check({ packageManager, mainConfig, storybookVersion }) { const webpackVersion = await packageManager.getPackageVersion('webpack'); if ( diff --git a/code/lib/cli/src/automigrate/fixes/wrap-require.ts b/code/lib/cli/src/automigrate/fixes/wrap-require.ts index 3651eb2c57a8..e241f05858ab 100644 --- a/code/lib/cli/src/automigrate/fixes/wrap-require.ts +++ b/code/lib/cli/src/automigrate/fixes/wrap-require.ts @@ -22,6 +22,8 @@ interface WrapRequireRunOptions { export const wrapRequire: Fix = { id: 'wrap-require', + versionRange: ['<7.2.0-rc.0', '>=7.2.0-rc.0'], + async check({ packageManager, storybookVersion, mainConfigPath }) { const isStorybookInMonorepo = await packageManager.isStorybookInMonorepo(); const isPnp = await detectPnp(); diff --git a/code/lib/cli/src/automigrate/index.test.ts b/code/lib/cli/src/automigrate/index.test.ts new file mode 100644 index 000000000000..78a098e9c8d7 --- /dev/null +++ b/code/lib/cli/src/automigrate/index.test.ts @@ -0,0 +1,157 @@ +import { vi, it, expect, describe, beforeEach } from 'vitest'; +import { runFixes } from './index'; +import type { Fix } from './types'; +import type { JsPackageManager, PackageJsonWithDepsAndDevDeps } from '@storybook/core-common'; +import { afterEach } from 'node:test'; + +const check1 = vi.fn(); +const run1 = vi.fn(); +const retrievePackageJson = vi.fn(); +const getPackageVersion = vi.fn(); +const prompt1Message = 'prompt1Message'; + +vi.spyOn(console, 'error').mockImplementation(console.log); + +const fixes: Fix[] = [ + { + id: 'fix-1', + + versionRange: ['<7', '>=7'], + + async check(config) { + return check1(config); + }, + + prompt() { + return prompt1Message; + }, + + async run(result) { + run1(result); + }, + }, +]; + +const coreCommonMock = vi.hoisted(() => { + return { + loadMainConfig: vi.fn(), + }; +}); + +vi.mock('@storybook/core-common', async (importOriginal) => ({ + ...(await importOriginal()), + loadMainConfig: coreCommonMock.loadMainConfig, +})); + +const promptMocks = vi.hoisted(() => { + return { + default: vi.fn(), + }; +}); + +vi.mock('prompts', () => { + return { + default: promptMocks.default, + }; +}); + +class PackageManager implements Partial { + public async retrievePackageJson(): Promise { + return retrievePackageJson(); + } + + getPackageVersion(packageName: string, basePath?: string | undefined): Promise { + return getPackageVersion(packageName, basePath); + } +} + +const packageManager = new PackageManager() as any as JsPackageManager; + +const dryRun = false; +const yes = true; +const rendererPackage = 'storybook'; +const skipInstall = false; +const configDir = '/path/to/config'; +const mainConfigPath = '/path/to/mainConfig'; +const beforeVersion = '6.5.15'; +const isUpgrade = true; + +const runFixWrapper = async ({ + // eslint-disable-next-line @typescript-eslint/no-shadow + beforeVersion, + storybookVersion, +}: { + beforeVersion: string; + storybookVersion: string; +}) => { + return runFixes({ + fixes, + dryRun, + yes, + rendererPackage, + skipInstall, + configDir, + packageManager: packageManager, + mainConfigPath, + storybookVersion, + beforeVersion, + isUpgrade, + }); +}; + +describe('runFixes', () => { + beforeEach(() => { + retrievePackageJson.mockResolvedValue({ + depedencies: [], + devDepedencies: [], + }); + getPackageVersion.mockImplementation((packageName) => { + return beforeVersion; + }); + check1.mockResolvedValue({ some: 'result' }); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should be unnecessary to run fix-1 from SB 6.5.15 to 6.5.16', async () => { + const { fixResults } = await runFixWrapper({ beforeVersion, storybookVersion: '6.5.16' }); + + // Assertions + expect(fixResults).toEqual({ + 'fix-1': 'unnecessary', + }); + expect(run1).not.toHaveBeenCalled(); + }); + + it('should be necessary to run fix-1 from SB 6.5.15 to 7.0.0', async () => { + promptMocks.default.mockResolvedValue({ shouldContinue: true }); + + const { fixResults } = await runFixWrapper({ beforeVersion, storybookVersion: '7.0.0' }); + + expect(fixResults).toEqual({ + 'fix-1': 'succeeded', + }); + expect(run1).toHaveBeenCalledWith({ + dryRun, + mainConfigPath, + packageManager, + result: { + some: 'result', + }, + skipInstall, + }); + }); + + it('should fail if an error is thrown', async () => { + check1.mockRejectedValue(new Error('check1 error')); + + const { fixResults } = await runFixWrapper({ beforeVersion, storybookVersion: '7.0.0' }); + + expect(fixResults).toEqual({ + 'fix-1': 'check_failed', + }); + expect(run1).not.toHaveBeenCalled(); + }); +}); diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index 19d4ee8922e9..fbb62578b937 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -5,6 +5,7 @@ import { createWriteStream, move, remove } from 'fs-extra'; import tempy from 'tempy'; import { join } from 'path'; import invariant from 'tiny-invariant'; +import semver from 'semver'; import { JsPackageManagerFactory, @@ -81,7 +82,15 @@ export const doAutomigrate = async (options: AutofixOptionsFromCLI) => { throw new Error('Could not determine main config path'); } - return automigrate({ ...options, packageManager, storybookVersion, mainConfigPath, configDir }); + return automigrate({ + ...options, + packageManager, + storybookVersion, + beforeVersion: storybookVersion, + mainConfigPath, + configDir, + isUpgrade: false, + }); }; export const automigrate = async ({ @@ -94,9 +103,11 @@ export const automigrate = async ({ configDir, mainConfigPath, storybookVersion, + beforeVersion, renderer: rendererPackage, skipInstall, hideMigrationSummary = false, + isUpgrade, }: AutofixOptions): Promise<{ fixResults: Record; preCheckFailure?: PreCheckFailure; @@ -127,6 +138,7 @@ export const automigrate = async ({ configDir, mainConfigPath, storybookVersion, + beforeVersion, dryRun, yes, }); @@ -170,6 +182,8 @@ export async function runFixes({ packageManager, mainConfigPath, storybookVersion, + beforeVersion, + isUpgrade, }: { fixes: Fix[]; yes?: boolean; @@ -180,6 +194,8 @@ export async function runFixes({ packageManager: JsPackageManager; mainConfigPath: string; storybookVersion: string; + beforeVersion: string; + isUpgrade?: boolean; }): Promise<{ preCheckFailure?: PreCheckFailure; fixResults: Record; @@ -198,15 +214,22 @@ export async function runFixes({ packageManager, }); - result = await f.check({ - packageManager, - configDir, - rendererPackage, - mainConfig, - storybookVersion, - previewConfigPath, - mainConfigPath, - }); + if ( + (isUpgrade && + semver.satisfies(beforeVersion, f.versionRange[0], { includePrerelease: true }) && + semver.satisfies(storybookVersion, f.versionRange[1], { includePrerelease: true })) || + !isUpgrade + ) { + result = await f.check({ + packageManager, + configDir, + rendererPackage, + mainConfig, + storybookVersion, + previewConfigPath, + mainConfigPath, + }); + } } catch (error) { logger.info(`⚠️ failed to check fix ${chalk.bold(f.id)}`); if (error instanceof Error) { diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index 97d20c09dc45..3c73db118a9f 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -22,6 +22,12 @@ export interface RunOptions { export interface Fix { id: string; promptOnly?: boolean; + /** + * The from/to version range of Storybook that this fix applies to. The strings are semver ranges. + * The versionRange will only be checked if the automigration is part of an upgrade. + * If the automigration is not part of an upgrade but rather called via `automigrate` CLI, the check function should handle the version check. + */ + versionRange: [from: string, to: string]; check: (options: CheckOptions) => Promise; prompt: (result: ResultType) => string; run?: (options: RunOptions) => Promise; @@ -38,7 +44,15 @@ export enum PreCheckFailure { export interface AutofixOptions extends Omit { packageManager: JsPackageManager; mainConfigPath: string; + /** + * The version of Storybook before the migration. + */ + beforeVersion: string; storybookVersion: string; + /** + * Whether the migration is part of an upgrade. + */ + isUpgrade: boolean; } export interface AutofixOptionsFromCLI { fixId?: FixId; diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index 1b26654a9bf8..7958eacbb9b5 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -16,7 +16,6 @@ import { isCorePackage, versions, getStorybookInfo, - getCoercedStorybookVersion, loadMainConfig, JsPackageManagerFactory, } from '@storybook/core-common'; @@ -276,7 +275,9 @@ export const doUpgrade = async ({ packageManager, configDir, mainConfigPath, + beforeVersion, storybookVersion: currentVersion, + isUpgrade: true, }); } From bfe8c25752fcfa4d81f38d38d64aaf4595978602 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 16 Feb 2024 15:39:38 +0100 Subject: [PATCH 07/39] Fix types and add versionRanges for missed automigrations --- code/lib/cli/src/automigrate/fixes/incompatible-addons.ts | 7 ++++--- .../src/automigrate/fixes/remove-jest-testing-library.ts | 5 +++-- code/lib/cli/src/automigrate/fixes/storyshots-migration.ts | 1 + code/lib/cli/src/migrate.ts | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts b/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts index ce9d01ccd96a..db5879506781 100644 --- a/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts +++ b/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts @@ -10,6 +10,7 @@ interface IncompatibleAddonsOptions { export const incompatibleAddons: Fix = { id: 'incompatible-addons', promptOnly: true, + versionRange: ['*', '*'], async check({ mainConfig, packageManager }) { const incompatibleAddonList = await getIncompatibleAddons(mainConfig, packageManager); @@ -20,14 +21,14 @@ export const incompatibleAddons: Fix = { return dedent` ${chalk.bold( 'Attention' - )}: We've detected that you're using the following addons in versions which are known to be incompatible with Storybook 7: + )}: We've detected that you're using the following addons in versions which are known to be incompatible with Storybook 8: ${incompatibleAddonList .map(({ name, version }) => `- ${chalk.cyan(`${name}@${version}`)}`) .join('\n')} - Please be aware they might not work in Storybook 7. Reach out to their maintainers for updates and check the following Github issue for more information: - ${chalk.yellow('https://github.com/storybookjs/storybook/issues/20529')} + Please be aware they might not work in Storybook 8. Reach out to their maintainers for updates and check the following Github issue for more information: + ${chalk.yellow('https://github.com/storybookjs/storybook/issues/26031')} `; }, }; diff --git a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts index 87cf964468b3..40b2f5455913 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts @@ -4,8 +4,9 @@ import type { Fix } from '../types'; export const removeJestTestingLibrary: Fix<{ incompatiblePackages: string[] }> = { id: 'remove-jest-testing-library', + versionRange: ['<8.0.0-alpha.0', '>=8.0.0-alpha.0'], promptOnly: true, - async check({ mainConfig, packageManager }) { + async check({ packageManager }) { const deps = await packageManager.getAllDependencies(); const incompatiblePackages = Object.keys(deps).filter( @@ -17,7 +18,7 @@ export const removeJestTestingLibrary: Fix<{ incompatiblePackages: string[] }> = return dedent` ${chalk.bold( 'Attention' - )}: We've detected that you're using the following packages which are known to be incompatible with Storybook 8: + )}: We've detected that you're using the following packages which are known to be incompatible since Storybook 8: ${incompatiblePackages.map((name) => `- ${chalk.cyan(`${name}`)}`).join('\n')} diff --git a/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts b/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts index 6d047f88449f..b192400c0c99 100644 --- a/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts +++ b/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts @@ -4,6 +4,7 @@ import type { Fix } from '../types'; export const storyshotsMigration: Fix = { id: 'storyshots-migration', + versionRange: ['<8.0.0-alpha.0', '>=8.0.0-alpha.0'], promptOnly: true, async check({ mainConfig, packageManager }) { diff --git a/code/lib/cli/src/migrate.ts b/code/lib/cli/src/migrate.ts index 5e38507afd61..f3041ed46362 100644 --- a/code/lib/cli/src/migrate.ts +++ b/code/lib/cli/src/migrate.ts @@ -41,6 +41,8 @@ export async function migrate(migration: any, { glob, dryRun, list, rename, pars mainConfigPath, packageManager, storybookVersion, + beforeVersion: storybookVersion, + isUpgrade: false, }); await addStorybookBlocksPackage(); } From 3770642629cf34695202cf1fa88cd3112fcc1643 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 08:10:38 +0100 Subject: [PATCH 08/39] Update tests --- .../fixes/__snapshots__/angular-builders.test.ts.snap | 6 ------ .../automigrate/fixes/remove-jest-testing-library.test.ts | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 code/lib/cli/src/automigrate/fixes/__snapshots__/angular-builders.test.ts.snap diff --git a/code/lib/cli/src/automigrate/fixes/__snapshots__/angular-builders.test.ts.snap b/code/lib/cli/src/automigrate/fixes/__snapshots__/angular-builders.test.ts.snap deleted file mode 100644 index 0c9ea612b977..000000000000 --- a/code/lib/cli/src/automigrate/fixes/__snapshots__/angular-builders.test.ts.snap +++ /dev/null @@ -1,6 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`is not Nx project > angular builders > Angular < 15.0.0 > should throw an Error 1`] = ` -[Error: ❌ Your project uses Angular < 15.0.0. Storybook 8.0 for Angular requires Angular 15.0.0 or higher. -Please upgrade your Angular version to at least version 15.0.0 to use Storybook 8.0 in your project.] -`; diff --git a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts index 60a2c2a97a35..29f6b391566f 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts @@ -51,7 +51,7 @@ it('should prompt to install the test package and run the codemod', async () => }); expect(await removeJestTestingLibrary.prompt(options!)).toMatchInlineSnapshot(` - Attention: We've detected that you're using the following packages which are known to be incompatible with Storybook 8: + Attention: We've detected that you're using the following packages which are known to be incompatible since Storybook 8: - @storybook/jest - @storybook/testing-library From b3b7360cf54f9563dd63d7a2478d2ae6306ef707 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 08:34:24 +0100 Subject: [PATCH 09/39] Fix tests --- code/lib/cli/src/upgrade.test.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/code/lib/cli/src/upgrade.test.ts b/code/lib/cli/src/upgrade.test.ts index 149951460724..5a72e3130628 100644 --- a/code/lib/cli/src/upgrade.test.ts +++ b/code/lib/cli/src/upgrade.test.ts @@ -66,7 +66,11 @@ describe('Upgrade errors', () => { }); await expect(doUpgrade({} as any)).rejects.toThrowError(UpgradeStorybookToLowerVersionError); - expect(findInstallationsMock).toHaveBeenCalledWith(['storybook', '@storybook/cli']); + expect(findInstallationsMock).toHaveBeenCalledWith([ + '@storybook/cli', + '@storybook/core', + '@storybook/core-common', + ]); }); it('should throw an error when upgrading to the same version number', async () => { findInstallationsMock.mockResolvedValue({ @@ -83,6 +87,10 @@ describe('Upgrade errors', () => { }); await expect(doUpgrade({} as any)).rejects.toThrowError(UpgradeStorybookToSameVersionError); - expect(findInstallationsMock).toHaveBeenCalledWith(['storybook', '@storybook/cli']); + expect(findInstallationsMock).toHaveBeenCalledWith([ + '@storybook/cli', + '@storybook/core', + '@storybook/core-common', + ]); }); }); From b4630e49c19bcce0afecf32800e23953ee886186 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 12:38:23 +0100 Subject: [PATCH 10/39] Update webpack5-compiler-setup.ts with versionRange --- code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index a22f8a55fdd0..d899ee5df3dd 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -28,7 +28,7 @@ type Options = { export const webpack5CompilerSetup = { id: 'webpack5-compiler-setup', - + versionRange: ['<8.0.0-alpha.9', '>=8.0.0-alpha.9'], promptType(result) { return result.isNextJs && !result.shouldRemoveSWCFlag ? 'notification' : 'auto'; }, From dcb5d3306c44125ce20fd00aec24877fd21fb7c3 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 12:38:35 +0100 Subject: [PATCH 11/39] Remove unused prop --- code/lib/cli/src/automigrate/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index 85502b617c74..92d21e2ec6c2 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -108,7 +108,6 @@ export const automigrate = async ({ renderer: rendererPackage, skipInstall, hideMigrationSummary = false, - isUpgrade, }: AutofixOptions): Promise<{ fixResults: Record; preCheckFailure?: PreCheckFailure; From e5cfe1e4894c5bd6a2db5f72eb48f44c29870cb1 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 20 Feb 2024 09:27:24 +0100 Subject: [PATCH 12/39] don't re-attach the same csf file again --- .../src/modules/preview-web/docs-context/DocsContext.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts index da41240756d5..bd95c0e81253 100644 --- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts +++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts @@ -71,6 +71,10 @@ export class DocsContext implements DocsContextProps if (!this.exportsToCSFFile.has(csfFile.moduleExports)) { throw new Error('Cannot attach a CSF file that has not been referenced'); } + if (this.attachedCSFFile === csfFile) { + // this CSF file is already attached, don't do anything + return; + } this.attachedCSFFile = csfFile; From 54286fceb884039c125b2ce7dff50c69db27dc25 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 20 Feb 2024 10:58:49 +0100 Subject: [PATCH 13/39] improve support for multiple attached CSF files in DocsContext --- .../docs-context/DocsContext.test.ts | 46 +++++++++++++++++++ .../preview-web/docs-context/DocsContext.ts | 27 ++++++----- .../preview-web/docs-context/test-utils.ts | 10 ++-- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.test.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.test.ts index 22a3aacc8a41..5321f4bc697f 100644 --- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.test.ts +++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.test.ts @@ -33,6 +33,52 @@ describe('referenceCSFFile', () => { }); }); +describe('attachCSFFile', () => { + const firstCsfParts = csfFileParts('first-meta--first-story', 'first-meta'); + const secondCsfParts = csfFileParts('second-meta--second-story', 'second-meta'); + const store = { + componentStoriesFromCSFFile: ({ csfFile }: { csfFile: CSFFile }) => + csfFile === firstCsfParts.csfFile ? [firstCsfParts.story] : [secondCsfParts.story], + } as unknown as StoryStore; + + it('attaches multiple CSF files', () => { + // Arrange - create a context with both CSF files + const context = new DocsContext(channel, store, renderStoryToElement, [ + firstCsfParts.csfFile, + secondCsfParts.csfFile, + ]); + + // Act - attach the first CSF file + context.attachCSFFile(firstCsfParts.csfFile); + + // Assert - the first story is now the primary story and the only component story + expect(context.storyById()).toEqual(firstCsfParts.story); + expect(context.componentStories()).toEqual([firstCsfParts.story]); + + // Assert - stories from both CSF files are available + expect(context.componentStoriesFromCSFFile(firstCsfParts.csfFile)).toEqual([ + firstCsfParts.story, + ]); + expect(context.componentStoriesFromCSFFile(secondCsfParts.csfFile)).toEqual([ + secondCsfParts.story, + ]); + + // Act - attach the second CSF file + context.attachCSFFile(secondCsfParts.csfFile); + + // Assert - the first story is still the primary story but both stories are available + expect(context.storyById()).toEqual(firstCsfParts.story); + expect(context.componentStories()).toEqual([firstCsfParts.story, secondCsfParts.story]); + + // Act - attach the second CSF file again + context.attachCSFFile(secondCsfParts.csfFile); + + // Assert - still only two stories are available + expect(context.storyById()).toEqual(firstCsfParts.story); + expect(context.componentStories()).toEqual([firstCsfParts.story, secondCsfParts.story]); + }); +}); + describe('resolveOf', () => { const { story, csfFile, storyExport, metaExport, moduleExports, component } = csfFileParts(); diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts index bd95c0e81253..5c83e764ec02 100644 --- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts +++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts @@ -17,7 +17,7 @@ import type { StoryStore } from '../../store'; import type { DocsContextProps } from './DocsContextProps'; export class DocsContext implements DocsContextProps { - private componentStoriesValue: PreparedStory[]; + private componentStoriesValue: Set>; private storyIdToCSFFile: Map>; @@ -27,7 +27,7 @@ export class DocsContext implements DocsContextProps private nameToStoryId: Map; - private attachedCSFFile?: CSFFile; + private attachedCSFFiles: Set>; private primaryStory?: PreparedStory; @@ -38,11 +38,12 @@ export class DocsContext implements DocsContextProps /** The CSF files known (via the index) to be refererenced by this docs file */ csfFiles: CSFFile[] ) { + this.componentStoriesValue = new Set(); this.storyIdToCSFFile = new Map(); this.exportToStory = new Map(); this.exportsToCSFFile = new Map(); this.nameToStoryId = new Map(); - this.componentStoriesValue = []; + this.attachedCSFFiles = new Set(); csfFiles.forEach((csfFile, index) => { this.referenceCSFFile(csfFile); @@ -71,17 +72,18 @@ export class DocsContext implements DocsContextProps if (!this.exportsToCSFFile.has(csfFile.moduleExports)) { throw new Error('Cannot attach a CSF file that has not been referenced'); } - if (this.attachedCSFFile === csfFile) { + if (this.attachedCSFFiles.has(csfFile)) { // this CSF file is already attached, don't do anything return; } - this.attachedCSFFile = csfFile; + this.attachedCSFFiles.add(csfFile); const stories = this.store.componentStoriesFromCSFFile({ csfFile }); + stories.forEach((story) => { this.nameToStoryId.set(story.name, story.id); - this.componentStoriesValue.push(story); + this.componentStoriesValue.add(story); if (!this.primaryStory) this.primaryStory = story; }); } @@ -119,15 +121,18 @@ export class DocsContext implements DocsContextProps return { type: 'story', story: this.primaryStory } as TResolvedExport; } - if (!this.attachedCSFFile) + if (this.attachedCSFFiles.size === 0) throw new Error( `No CSF file attached to this docs file, did you forget to use ?` ); - if (moduleExportType === 'meta') - return { type: 'meta', csfFile: this.attachedCSFFile } as TResolvedExport; + const firstAttachedCSFFile = Array.from(this.attachedCSFFiles)[0]; + + if (moduleExportType === 'meta') { + return { type: 'meta', csfFile: firstAttachedCSFFile } as TResolvedExport; + } - const { component } = this.attachedCSFFile.meta; + const { component } = firstAttachedCSFFile.meta; if (!component) throw new Error( `Attached CSF file does not defined a component, did you forget to export one?` @@ -202,7 +207,7 @@ export class DocsContext implements DocsContextProps }; componentStories = () => { - return this.componentStoriesValue; + return Array.from(this.componentStoriesValue); }; componentStoriesFromCSFFile = (csfFile: CSFFile) => { diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/test-utils.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/test-utils.ts index 4be2710c8f10..5de026e902b8 100644 --- a/code/lib/preview-api/src/modules/preview-web/docs-context/test-utils.ts +++ b/code/lib/preview-api/src/modules/preview-web/docs-context/test-utils.ts @@ -1,6 +1,6 @@ import type { CSFFile, PreparedStory } from '@storybook/types'; -export function csfFileParts() { +export function csfFileParts(storyId = 'meta--story', metaId = 'meta') { // These compose the raw exports of the CSF file const component = {}; const metaExport = { component }; @@ -9,13 +9,13 @@ export function csfFileParts() { // This is the prepared story + CSF file after SB has processed them const storyAnnotations = { - id: 'meta--story', + id: storyId, moduleExport: storyExport, } as CSFFile['stories'][string]; - const story = { id: 'meta--story', moduleExport: storyExport } as PreparedStory; - const meta = { id: 'meta', title: 'Meta', component, moduleExports } as CSFFile['meta']; + const story = { id: storyId, moduleExport: storyExport } as PreparedStory; + const meta = { id: metaId, title: 'Meta', component, moduleExports } as CSFFile['meta']; const csfFile = { - stories: { 'meta--story': storyAnnotations }, + stories: { [storyId]: storyAnnotations }, meta, moduleExports, } as CSFFile; From 3faf37296dd027ca756fa1490f4e0d5ddb7369ad Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 20 Feb 2024 12:06:36 +0100 Subject: [PATCH 14/39] Cleanup prompt-remove-react automigration --- .../fixes/prompt-remove-react.test.ts | 50 ++++--------------- .../automigrate/fixes/prompt-remove-react.ts | 16 ++---- 2 files changed, 13 insertions(+), 53 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts index 7d33c0fe90aa..baf9922b5f0f 100644 --- a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts +++ b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts @@ -9,15 +9,12 @@ const check = async ({ main: mainConfig, storybookVersion = '8.0.0', }: { - packageManagerContent: Pick< - Partial>>, - 'dependencies' | 'devDependencies' | 'peerDependencies' - >; + packageManagerContent: Partial>>; main: Partial & Record; storybookVersion?: string; }) => { const packageManager = { - retrievePackageJson: async () => packageManagerContent, + getAllDependencies: async () => packageManagerContent, } as JsPackageManager; return removeReactDependency.check({ @@ -31,21 +28,6 @@ const check = async ({ vi.mock('glob', () => ({ glob: vi.fn(() => []) })); describe('early exits', () => { - it('cancel if storybookVersion < 8', async () => { - await expect( - check({ - packageManagerContent: { - dependencies: { react: '16.0.0' }, - }, - main: { - stories: [], - framework: '@storybook/vue-vite', - }, - storybookVersion: '7.0.0', - }) - ).resolves.toBeFalsy(); - }); - it('cancel if no react deps', async () => { await expect( check({ @@ -61,9 +43,7 @@ describe('early exits', () => { it('cancel if react renderer', async () => { await expect( check({ - packageManagerContent: { - dependencies: { react: '16.0.0' }, - }, + packageManagerContent: { react: '16.0.0' }, main: { stories: [], framework: '@storybook/react-vite', @@ -73,9 +53,7 @@ describe('early exits', () => { await expect( check({ - packageManagerContent: { - dependencies: { react: '16.0.0' }, - }, + packageManagerContent: { react: '16.0.0' }, main: { stories: [], framework: '@storybook/nextjs', @@ -85,9 +63,7 @@ describe('early exits', () => { await expect( check({ - packageManagerContent: { - dependencies: { react: '16.0.0' }, - }, + packageManagerContent: { react: '16.0.0' }, main: { stories: [], framework: { name: '@storybook/react-webpack5' }, @@ -101,9 +77,7 @@ describe('prompts', () => { it('simple', async () => { await expect( check({ - packageManagerContent: { - dependencies: { react: '16.0.0' }, - }, + packageManagerContent: { react: '16.0.0' }, main: { stories: ['*.stories.ts'], addons: [], @@ -115,9 +89,7 @@ describe('prompts', () => { it('detects addon docs', async () => { await expect( check({ - packageManagerContent: { - dependencies: { react: '16.0.0' }, - }, + packageManagerContent: { react: '16.0.0' }, main: { stories: ['*.stories.ts'], addons: ['@storybook/addon-docs'], @@ -129,9 +101,7 @@ describe('prompts', () => { it('detects addon essentials', async () => { await expect( check({ - packageManagerContent: { - dependencies: { react: '16.0.0' }, - }, + packageManagerContent: { react: '16.0.0' }, main: { stories: ['*.stories.ts'], addons: ['@storybook/addon-docs', '@storybook/addon-essentials'], @@ -145,9 +115,7 @@ describe('prompts', () => { glob.mockImplementationOnce(() => ['*.stories.mdx']); await expect( check({ - packageManagerContent: { - dependencies: { react: '16.0.0' }, - }, + packageManagerContent: { react: '16.0.0' }, main: { stories: ['*.stories.ts'], addons: ['@storybook/addon-docs', '@storybook/addon-essentials'], diff --git a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts index c95822da64dc..65463ec015ea 100644 --- a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts +++ b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts @@ -1,5 +1,4 @@ import dedent from 'ts-dedent'; -import semver from 'semver'; import { getFrameworkPackageName } from '../helpers/mainConfigFile'; import type { Fix } from '../types'; @@ -10,27 +9,20 @@ export const removeReactDependency: Fix<{}> = { id: 'remove-react-dependency', promptType: 'manual', - versionRange: ['>=7 <8.0.0-alpha.4', `>=${minimumStorybookVersion}`], + versionRange: [`^7 || <${minimumStorybookVersion}`, `>=${minimumStorybookVersion}`], async check({ packageManager, mainConfig, storybookVersion }) { // when the user is using the react renderer, we should not prompt them to remove react const frameworkPackageName = getFrameworkPackageName(mainConfig); + if (frameworkPackageName?.includes('react') || frameworkPackageName?.includes('nextjs')) { return null; } // if the user has no dependency on react, we can skip this fix - const packageJson = await packageManager.retrievePackageJson(); - if ( - !packageJson?.dependencies?.['react'] && - !packageJson?.peerDependencies?.['react'] && - !packageJson?.devDependencies?.['react'] - ) { - return null; - } + const { react } = await packageManager.getAllDependencies(); - // do not prompt to remove react for older versions of storybook - if (!semver.gte(storybookVersion, minimumStorybookVersion)) { + if (!react) { return null; } From 6b4f27d0ddd868a6f04aad43a86529700b843aa9 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 20 Feb 2024 12:08:05 +0100 Subject: [PATCH 15/39] Fix block-stories-mdx autoblock detection --- code/lib/cli/src/autoblock/block-stories-mdx.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/lib/cli/src/autoblock/block-stories-mdx.ts b/code/lib/cli/src/autoblock/block-stories-mdx.ts index b868d913ecd0..99ab3e643827 100644 --- a/code/lib/cli/src/autoblock/block-stories-mdx.ts +++ b/code/lib/cli/src/autoblock/block-stories-mdx.ts @@ -5,7 +5,10 @@ import { glob } from 'glob'; export const blocker = createBlocker({ id: 'storiesMdxUsage', async check() { - const files = await glob('**/*.stories.mdx', { cwd: process.cwd() }); + const files = await glob('**/*.stories.mdx', { + cwd: process.cwd(), + ignore: ['node_modules/**'], + }); if (files.length === 0) { return false; } From 609482b994763641dc3e7dafcca1219c39517f0c Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 20 Feb 2024 12:11:36 +0100 Subject: [PATCH 16/39] Fix storybook-version detection --- code/lib/cli/src/upgrade.test.ts | 15 +++------------ code/lib/cli/src/upgrade.ts | 9 +-------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/code/lib/cli/src/upgrade.test.ts b/code/lib/cli/src/upgrade.test.ts index 5a72e3130628..14bafd7cf5b9 100644 --- a/code/lib/cli/src/upgrade.test.ts +++ b/code/lib/cli/src/upgrade.test.ts @@ -1,12 +1,11 @@ import { describe, it, expect, vi } from 'vitest'; +import * as sbcc from '@storybook/core-common'; import { UpgradeStorybookToLowerVersionError, UpgradeStorybookToSameVersionError, } from '@storybook/core-events/server-errors'; import { doUpgrade, getStorybookVersion } from './upgrade'; -import type * as sbcc from '@storybook/core-common'; - const findInstallationsMock = vi.fn>(); vi.mock('@storybook/telemetry'); @@ -66,11 +65,7 @@ describe('Upgrade errors', () => { }); await expect(doUpgrade({} as any)).rejects.toThrowError(UpgradeStorybookToLowerVersionError); - expect(findInstallationsMock).toHaveBeenCalledWith([ - '@storybook/cli', - '@storybook/core', - '@storybook/core-common', - ]); + expect(findInstallationsMock).toHaveBeenCalledWith(Object.keys(sbcc.versions)); }); it('should throw an error when upgrading to the same version number', async () => { findInstallationsMock.mockResolvedValue({ @@ -87,10 +82,6 @@ describe('Upgrade errors', () => { }); await expect(doUpgrade({} as any)).rejects.toThrowError(UpgradeStorybookToSameVersionError); - expect(findInstallationsMock).toHaveBeenCalledWith([ - '@storybook/cli', - '@storybook/core', - '@storybook/core-common', - ]); + expect(findInstallationsMock).toHaveBeenCalledWith(Object.keys(sbcc.versions)); }); }); diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index 7958eacbb9b5..53c939f56c36 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -40,14 +40,7 @@ export const getStorybookVersion = (line: string) => { }; const getInstalledStorybookVersion = async (packageManager: JsPackageManager) => { - const installations = await packageManager.findInstallations([ - // Storybook 3.0.0+ | the installation wasn't required though in Storybook < 7.0.0 - '@storybook/cli', - // Storybook 3.3.3 - 6.5.16 - '@storybook/core', - // Storybook 6.2+ - '@storybook/core-common', - ]); + const installations = await packageManager.findInstallations(Object.keys(versions)); if (!installations) { return; } From 1291b0ee1593aa99189eae684f244cb351a882e8 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 22 Feb 2024 15:37:12 +0100 Subject: [PATCH 17/39] Remove block-stories-mdx autoblock --- .../cli/src/autoblock/block-stories-mdx.ts | 36 ------------------- code/lib/cli/src/autoblock/index.ts | 1 - 2 files changed, 37 deletions(-) delete mode 100644 code/lib/cli/src/autoblock/block-stories-mdx.ts diff --git a/code/lib/cli/src/autoblock/block-stories-mdx.ts b/code/lib/cli/src/autoblock/block-stories-mdx.ts deleted file mode 100644 index 99ab3e643827..000000000000 --- a/code/lib/cli/src/autoblock/block-stories-mdx.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createBlocker } from './types'; -import { dedent } from 'ts-dedent'; -import { glob } from 'glob'; - -export const blocker = createBlocker({ - id: 'storiesMdxUsage', - async check() { - const files = await glob('**/*.stories.mdx', { - cwd: process.cwd(), - ignore: ['node_modules/**'], - }); - if (files.length === 0) { - return false; - } - return { files }; - }, - message(options, data) { - return `Found ${data.files.length} stories.mdx ${ - data.files.length === 1 ? 'file' : 'files' - }, these must be migrated.`; - }, - log() { - return dedent` - Support for *.stories.mdx files has been removed. - Please see the migration guide for more information: - https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropping-support-for-storiesmdx-csf-in-mdx-format-and-mdx1-support - - Storybook will also require you to use MDX 3.0.0 or later. - Check the migration guide for more information: - https://mdxjs.com/blog/v3/ - - Manually run the migration script to convert your stories.mdx files to CSF format documented here: - https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf - `; - }, -}); diff --git a/code/lib/cli/src/autoblock/index.ts b/code/lib/cli/src/autoblock/index.ts index ca8116d890cb..1ba9ff8c85e8 100644 --- a/code/lib/cli/src/autoblock/index.ts +++ b/code/lib/cli/src/autoblock/index.ts @@ -9,7 +9,6 @@ const excludesFalse = (x: T | false): x is T => x !== false; const blockers: () => BlockerModule[] = () => [ // add/remove blockers here import('./block-storystorev6'), - import('./block-stories-mdx'), import('./block-dependencies-versions'), import('./block-node-version'), ]; From b481b3fff09b60337fecf338a0de42bee93ec586 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 22 Feb 2024 15:38:06 +0100 Subject: [PATCH 18/39] Readd mdx-1-to-2 automigration and rename it to mdx-1-to-3 --- code/lib/cli/src/automigrate/fixes/index.ts | 2 + .../src/automigrate/fixes/mdx-1-to-3.test.ts | 120 ++++++++++++++++++ .../cli/src/automigrate/fixes/mdx-1-to-3.ts | 85 +++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 code/lib/cli/src/automigrate/fixes/mdx-1-to-3.test.ts create mode 100644 code/lib/cli/src/automigrate/fixes/mdx-1-to-3.ts diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts index 559bac391ea8..b1e7fc9643c9 100644 --- a/code/lib/cli/src/automigrate/fixes/index.ts +++ b/code/lib/cli/src/automigrate/fixes/index.ts @@ -20,6 +20,7 @@ import { removeReactDependency } from './prompt-remove-react'; import { storyshotsMigration } from './storyshots-migration'; import { webpack5CompilerSetup } from './webpack5-compiler-setup'; import { removeJestTestingLibrary } from './remove-jest-testing-library'; +import { mdx1to3 } from './mdx-1-to-3'; export * from '../types'; @@ -44,6 +45,7 @@ export const allFixes: Fix[] = [ storyshotsMigration, removeReactDependency, webpack5CompilerSetup, + mdx1to3, ]; export const initFixes: Fix[] = [eslintPlugin]; diff --git a/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.test.ts b/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.test.ts new file mode 100644 index 000000000000..320e4447de7d --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.test.ts @@ -0,0 +1,120 @@ +import { it, expect } from 'vitest'; + +import { dedent } from 'ts-dedent'; +import { fixMdxStyleTags, fixMdxComments } from './mdx-1-to-3'; + +it('fixMdxStyleTags fixes badly-formatted style blocks', () => { + expect( + fixMdxStyleTags(dedent` + + `) + ).toEqual(dedent` + + `); +}); + +it('fixMdxStyleTags fixes multiple style blocks', () => { + expect( + fixMdxStyleTags(dedent` + + + `) + ).toMatchInlineSnapshot(` + " + " + `); +}); + +it('fixMdxComments fixes all comments', () => { + expect( + fixMdxComments(dedent` + # Hello + + + + and this is not + + + `) + ).toMatchInlineSnapshot(` + "# Hello + + {/* This is a comment */} + + and this is not + + {/* This is another comment */}" + `); +}); + +it('fixMdxComments keeps html comments in codeblocks', () => { + expect( + fixMdxComments(dedent` + # Hello + + ~~~html + + ~~~ + + ~~~html + + ~~~ + + \`\`\`html + + \`\`\` + + \`\`\`html + + \`\`\` + + and this is not + + + `) + ).toMatchInlineSnapshot(` + "# Hello + + ~~~html + + ~~~ + + ~~~html + + ~~~ + + \`\`\`html + + \`\`\` + + \`\`\`html + + \`\`\` + + and this is not + + {/* This is another comment */}" + `); +}); diff --git a/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.ts b/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.ts new file mode 100644 index 000000000000..3d81d0934447 --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.ts @@ -0,0 +1,85 @@ +import chalk from 'chalk'; +import { dedent } from 'ts-dedent'; +import { basename } from 'path'; +import fse from 'fs-extra'; +import globby from 'globby'; +import type { Fix } from '../types'; + +const MDX1_STYLE_START = /'); +}; + +export const fixMdxComments = (mdx: string) => { + const codeblocks = mdx.matchAll(MDX1_CODEBLOCK); + + // separate the mdx into sections without codeblocks & replace html comments NOT in codeblocks + const sections = mdx + .split(MDX1_CODEBLOCK) + .map((v) => v.replace(MDX1_COMMENT, (original, group) => `{/*${group}*/}`)); + + // interleave the original codeblocks with the replaced sections + return sections.reduce((acc, item, i) => { + const next = codeblocks.next(); + return next.done ? acc + item : acc + item + next.value[0]; + }, ''); +}; + +const logger = console; + +interface Mdx1to3Options { + storiesMdxFiles: string[]; +} + +/** + * Does the user have `.stories.mdx` files? + * + * If so: + * - Assume they might be MDX1 + * - Offer to help migrate to MDX3 + */ +export const mdx1to3: Fix = { + id: 'mdx1to3', + + versionRange: ['<7.0.0', '>=8.0.0-alpha.0'], + + async check() { + const storiesMdxFiles = await globby('./!(node_modules)**/*.(story|stories).mdx'); + return storiesMdxFiles.length ? { storiesMdxFiles } : null; + }, + + prompt({ storiesMdxFiles }) { + return dedent` + We've found ${chalk.yellow(storiesMdxFiles.length)} '.stories.mdx' files in your project. + + Storybook has upgraded to MDX3 (https://mdxjs.com/blog/v3/). MDX3 itself doesn't contain disruptive breaking changes, whereas the transition from MDX1 to MDX2 was a significant change. + We can try to automatically upgrade your MDX files to MDX3 format using some common patterns. + + After this install completes, and before you start Storybook, we strongly recommend reading the MDX2 section + of the 7.0 migration guide. It contains useful tools for detecting and fixing any remaining issues. + + ${chalk.cyan('https://storybook.js.org/migration-guides/7.0')} + `; + }, + + async run({ result: { storiesMdxFiles }, dryRun }) { + await Promise.all([ + ...storiesMdxFiles.map(async (fname) => { + const contents = await fse.readFile(fname, 'utf-8'); + const updated = fixMdxComments(fixMdxStyleTags(contents)); + if (updated === contents) { + logger.info(`🆗 Unmodified ${basename(fname)}`); + } else { + logger.info(`✅ Modified ${basename(fname)}`); + if (!dryRun) { + await fse.writeFile(fname, updated); + } + } + }), + ]); + }, +}; From 6a9f123d1b4f6ac314c33f1f0a5ea2e41dd768ba Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 23 Feb 2024 11:40:14 +0100 Subject: [PATCH 19/39] Add version range for removeArgtypesRegex --- code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts index 6a517fc0be0c..f393ee2f6588 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts @@ -9,6 +9,7 @@ import chalk from 'chalk'; export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPath: string }> = { id: 'remove-argtypes-regex', promptType: 'manual', + versionRange: ['<8.0.0-alpha.0', '>=8.0.0-alpha.0'], async check({ previewConfigPath }) { if (!previewConfigPath) return null; @@ -59,7 +60,7 @@ export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPa ${argTypesRegex.buildCodeFrameError(`${previewConfigPath}`).message} - In Storybook 8, we recommend removing this regex. + Since Storybook 8, we recommend removing this regex. Assign explicit spies with the ${chalk.cyan('fn')} function instead: ${formattedSnippet} From 44b1493f3c8ff1cd1a863eeb5835ef5d416cf23a Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 23 Feb 2024 11:41:08 +0100 Subject: [PATCH 20/39] Fix typo in automigrate/types.ts --- code/lib/cli/src/automigrate/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index 791f7574467f..80cc4e74defc 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -23,7 +23,7 @@ export interface RunOptions { * promptType defines how the user will be prompted to apply an automigration fix * - auto: the fix will be applied automatically * - manual: the user will be prompted to apply the fix - * - notification: the user will be notified about the some changes. A fix isn't required + * - notification: the user will be notified about some changes. A fix isn't required, thought */ export type Prompt = 'auto' | 'manual' | 'notification'; From f7e680215b9ccca0a9d424c1fb1749b5d0b5eee8 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 23 Feb 2024 11:48:04 +0100 Subject: [PATCH 21/39] Add version range for removeLegacyMDX1 fix --- code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts b/code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts index 57f90e8403af..4045fc800cba 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts @@ -18,6 +18,7 @@ interface RemoveLegacyMDX1Options { */ export const removeLegacyMDX1: Fix = { id: 'builder-vite', + versionRange: ['<8.0.0-alpha.0', '>=8.0.0-alpha.0'], async check({ mainConfig }) { if (mainConfig.features) { From f2064fbdea162c73b680927c03277a180c2a026d Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 22 Feb 2024 15:44:55 +0100 Subject: [PATCH 22/39] Rename transformed mdx files --- code/lib/codemod/src/index.ts | 5 ----- code/lib/codemod/src/transforms/mdx-to-csf.ts | 17 ++++++++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/code/lib/codemod/src/index.ts b/code/lib/codemod/src/index.ts index 0bc3f4d1b3db..e458e8ed9906 100644 --- a/code/lib/codemod/src/index.ts +++ b/code/lib/codemod/src/index.ts @@ -93,11 +93,6 @@ export async function runCodemod(codemod: any, { glob, logger, dryRun, rename, p } } - if (!renameParts && codemod === 'mdx-to-csf') { - renameParts = ['.stories.mdx', '.mdx']; - rename = '.stories.mdx:.mdx;'; - } - if (renameParts) { const [from, to] = renameParts; logger.log(`=> Renaming ${rename}: ${files.length} files`); diff --git a/code/lib/codemod/src/transforms/mdx-to-csf.ts b/code/lib/codemod/src/transforms/mdx-to-csf.ts index 9c657c822e04..d7751965f673 100644 --- a/code/lib/codemod/src/transforms/mdx-to-csf.ts +++ b/code/lib/codemod/src/transforms/mdx-to-csf.ts @@ -24,6 +24,8 @@ import type { MdxFlowExpression } from 'mdast-util-mdx-expression'; const mdxProcessor = remark().use(remarkMdx) as ReturnType; +const renameList: { original: string; baseName: string }[] = []; + export default async function jscodeshift(info: FileInfo) { const parsed = path.parse(info.path); @@ -39,15 +41,20 @@ export default async function jscodeshift(info: FileInfo) { const result = await transform(info, path.basename(baseName)); - const [mdx, csf] = result; - - if (csf != null) { - fs.writeFileSync(`${baseName}.stories.js`, csf); + if (result[1] != null) { + fs.writeFileSync(`${baseName}.stories.js`, result[1]); + renameList.push({ original: info.path, baseName }); } - return mdx; + return result[0]; } +process.on('exit', () => { + renameList.forEach((file) => { + fs.renameSync(file.original, `${file.baseName}.mdx`); + }); +}); + export async function transform(info: FileInfo, baseName: string): Promise<[string, string]> { const root = mdxProcessor.parse(info.source); const storyNamespaceName = nameToValidExport(`${baseName}Stories`); From 55a7c97bc98e79f8613fccb007b66c47f992f290 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 22 Feb 2024 16:00:52 +0100 Subject: [PATCH 23/39] Fix issues where stories.js files were generated although a story doesn't exist --- .../transforms/__tests__/mdx-to-csf.test.ts | 44 ++++++++ code/lib/codemod/src/transforms/mdx-to-csf.ts | 101 ++++++++++++------ 2 files changed, 115 insertions(+), 30 deletions(-) diff --git a/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts b/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts index 1be65c569edc..36db68148a31 100644 --- a/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts +++ b/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts @@ -598,6 +598,50 @@ it('story child is identifier', async () => { `); }); +it('should replace ArgsTable by Controls', async () => { + const input = dedent` + import { ArgsTable } from '@storybook/addon-docs/blocks'; + import { Button } from './button'; + + Dummy Code + + + `; + + const mdx = await jscodeshift({ source: input, path: 'Foobar.stories.mdx' }); + + expect(mdx).toMatchInlineSnapshot(` + import { Controls } from '@storybook/blocks'; + import { Button } from './button'; + + Dummy Code + + + `); +}); + +it('should not create stories.js file if there are no components', async () => { + const input = dedent` + import { Meta } from '@storybook/addon-docs'; + + + + # Welcome to Storybook + `; + + const mdx = await jscodeshift({ source: input, path: 'Foobar.stories.mdx' }); + + expect(fs.writeFileSync).not.toHaveBeenCalled(); + + expect(mdx).toMatchInlineSnapshot(` + import { Meta } from '@storybook/blocks'; + + + + # Welcome to Storybook + `); +}); + it('nameToValidExport', () => { expect(nameToValidExport('1 starts with digit')).toMatchInlineSnapshot(`$1StartsWithDigit`); expect(nameToValidExport('name')).toMatchInlineSnapshot(`Name`); diff --git a/code/lib/codemod/src/transforms/mdx-to-csf.ts b/code/lib/codemod/src/transforms/mdx-to-csf.ts index d7751965f673..2997a2fa89e0 100644 --- a/code/lib/codemod/src/transforms/mdx-to-csf.ts +++ b/code/lib/codemod/src/transforms/mdx-to-csf.ts @@ -55,7 +55,10 @@ process.on('exit', () => { }); }); -export async function transform(info: FileInfo, baseName: string): Promise<[string, string]> { +export async function transform( + info: FileInfo, + baseName: string +): Promise<[string, string | null]> { const root = mdxProcessor.parse(info.source); const storyNamespaceName = nameToValidExport(`${baseName}Stories`); @@ -81,25 +84,42 @@ export async function transform(info: FileInfo, baseName: string): Promise<[stri node.value = node.value .replaceAll('@storybook/addon-docs/blocks', '@storybook/blocks') .replaceAll('@storybook/addon-docs', '@storybook/blocks'); + + if (node.value.includes('@storybook/blocks')) { + // @ts-ignore + const file: BabelFile = new babel.File( + { filename: 'info.path' }, + { code: node.value, ast: babelParse(node.value) } + ); + + file.path.traverse({ + ImportDeclaration(path) { + if (path.node.source.value === '@storybook/blocks') { + path.get('specifiers').forEach((specifier) => { + if (specifier.isImportSpecifier()) { + const imported = specifier.get('imported'); + if (imported.isIdentifier() && imported.node.name === 'ArgsTable') { + imported.node.name = 'Controls'; + } + } + }); + } + }, + }); + + node.value = recast.print(file.ast).code; + } }); const file = getEsmAst(root); visit(root, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node, index, parent) => { if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') { - if (is(node, { name: 'Meta' })) { - metaAttributes.push(...node.attributes); - node.attributes = [ - { - type: 'mdxJsxAttribute', - name: 'of', - value: { - type: 'mdxJsxAttributeValueExpression', - value: storyNamespaceName, - }, - }, - ]; + if (is(node, { name: 'ArgsTable' })) { + node.name = 'Controls'; + node.attributes = []; } + if (is(node, { name: 'Story' })) { const nameAttribute = node.attributes.find( (it) => it.type === 'mdxJsxAttribute' && it.name === 'name' @@ -174,21 +194,6 @@ export async function transform(info: FileInfo, baseName: string): Promise<[stri return undefined; }); - const metaProperties = metaAttributes.flatMap((attribute) => { - if (attribute.type === 'mdxJsxAttribute') { - if (typeof attribute.value === 'string') { - return [t.objectProperty(t.identifier(attribute.name), t.stringLiteral(attribute.value))]; - } - return [ - t.objectProperty( - t.identifier(attribute.name), - babelParseExpression(attribute.value?.value ?? '') as any as t.Expression - ), - ]; - } - return []; - }); - file.path.traverse({ // remove mdx imports from csf ImportDeclaration(path) { @@ -203,11 +208,47 @@ export async function transform(info: FileInfo, baseName: string): Promise<[stri }, }); - if (storiesMap.size === 0 && metaAttributes.length === 0) { + if (storiesMap.size === 0) { // A CSF file must have at least one story, so skip migrating if this is the case. - return [mdxProcessor.stringify(root), '']; + return [mdxProcessor.stringify(root), null]; } + // Rewrites the Meta tag to use the new story namespace + visit(root, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node, index, parent) => { + if ( + (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') && + is(node, { name: 'Meta' }) + ) { + metaAttributes.push(...node.attributes); + console.log({ storyNamespaceName }); + node.attributes = [ + { + type: 'mdxJsxAttribute', + name: 'of', + value: { + type: 'mdxJsxAttributeValueExpression', + value: storyNamespaceName, + }, + }, + ]; + } + }); + + const metaProperties = metaAttributes.flatMap((attribute) => { + if (attribute.type === 'mdxJsxAttribute') { + if (typeof attribute.value === 'string') { + return [t.objectProperty(t.identifier(attribute.name), t.stringLiteral(attribute.value))]; + } + return [ + t.objectProperty( + t.identifier(attribute.name), + babelParseExpression(attribute.value?.value ?? '') as any as t.Expression + ), + ]; + } + return []; + }); + addStoriesImport(root, baseName, storyNamespaceName); const newStatements: t.Statement[] = [ From f0a899d8c864a79d52d3bbea7944058c759a513e Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 23 Feb 2024 08:50:46 +0100 Subject: [PATCH 24/39] Upgrade migration-guide to mention limitations of mdx-to-csf codemod --- docs/migration-guide.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 1fa7d7c5ad4c..ce5c0d17404c 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -145,6 +145,10 @@ Storybook now requires that MDX pages reference stories written in CSF, rather t You’ll also need to update your stories glob in `.storybook/main.js` to include the newly created .mdx and .stories.js files if it doesn’t already. +#### Known limitations + +- The codemod does not remove the extracted stories from the `.stories.mdx` files. You will need to do this manually. + **Note:** this migration supports the Storybook 6 ["CSF stories with MDX docs"](https://github.com/storybookjs/storybook/blob/6e19f0fe426d58f0f7981a42c3d0b0384fab49b1/code/addons/docs/docs/recipes.md#csf-stories-with-mdx-docs) recipe. ## Troubleshooting From fd8c625521bf9c976a25c6763d824a7b46436cd3 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 23 Feb 2024 10:15:58 +0100 Subject: [PATCH 25/39] Rename broken MDX files to .mdx.broken --- code/lib/codemod/src/index.ts | 7 ++++++- code/lib/codemod/src/transforms/mdx-to-csf.ts | 21 +++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/code/lib/codemod/src/index.ts b/code/lib/codemod/src/index.ts index e458e8ed9906..67eb20799190 100644 --- a/code/lib/codemod/src/index.ts +++ b/code/lib/codemod/src/index.ts @@ -87,7 +87,12 @@ export async function runCodemod(codemod: any, { glob, logger, dryRun, rename, p shell: true, } ); - if (result.status === 1) { + + if (codemod === 'mdx-to-csf' && result.status === 1) { + logger.log( + 'The codemod was not able to transform the files mentioned above. We have renamed the files to .mdx.broken. Please check the files and rename them back to .mdx after you have either manually transformed them to mdx + csf or fixed the issues so that the codemod can transform them.' + ); + } else if (result.status === 1) { logger.log('Skipped renaming because of errors.'); return; } diff --git a/code/lib/codemod/src/transforms/mdx-to-csf.ts b/code/lib/codemod/src/transforms/mdx-to-csf.ts index 2997a2fa89e0..eca57e8a8d7e 100644 --- a/code/lib/codemod/src/transforms/mdx-to-csf.ts +++ b/code/lib/codemod/src/transforms/mdx-to-csf.ts @@ -25,6 +25,7 @@ import type { MdxFlowExpression } from 'mdast-util-mdx-expression'; const mdxProcessor = remark().use(remarkMdx) as ReturnType; const renameList: { original: string; baseName: string }[] = []; +const brokenList: { original: string; baseName: string }[] = []; export default async function jscodeshift(info: FileInfo) { const parsed = path.parse(info.path); @@ -39,20 +40,29 @@ export default async function jscodeshift(info: FileInfo) { baseName += '_'; } - const result = await transform(info, path.basename(baseName)); + try { + const result = await transform(info, path.basename(baseName)); + + if (result[1] != null) { + fs.writeFileSync(`${baseName}.stories.js`, result[1]); + } - if (result[1] != null) { - fs.writeFileSync(`${baseName}.stories.js`, result[1]); renameList.push({ original: info.path, baseName }); - } - return result[0]; + return result[0]; + } catch (e) { + brokenList.push({ original: info.path, baseName }); + throw e; + } } process.on('exit', () => { renameList.forEach((file) => { fs.renameSync(file.original, `${file.baseName}.mdx`); }); + brokenList.forEach((file) => { + fs.renameSync(file.original, `${file.original}.broken`); + }); }); export async function transform( @@ -220,7 +230,6 @@ export async function transform( is(node, { name: 'Meta' }) ) { metaAttributes.push(...node.attributes); - console.log({ storyNamespaceName }); node.attributes = [ { type: 'mdxJsxAttribute', From 14440f17926823789d0fdafdc4bc9e377411a27e Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 23 Feb 2024 12:56:29 +0100 Subject: [PATCH 26/39] Refactor MDX to CSF transform function to return an object instead of a tuple --- code/lib/codemod/src/transforms/mdx-to-csf.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/code/lib/codemod/src/transforms/mdx-to-csf.ts b/code/lib/codemod/src/transforms/mdx-to-csf.ts index eca57e8a8d7e..ed95f433a2ce 100644 --- a/code/lib/codemod/src/transforms/mdx-to-csf.ts +++ b/code/lib/codemod/src/transforms/mdx-to-csf.ts @@ -41,15 +41,15 @@ export default async function jscodeshift(info: FileInfo) { } try { - const result = await transform(info, path.basename(baseName)); + const { csf, mdx } = await transform(info, path.basename(baseName)); - if (result[1] != null) { - fs.writeFileSync(`${baseName}.stories.js`, result[1]); + if (csf != null) { + fs.writeFileSync(`${baseName}.stories.js`, csf); } renameList.push({ original: info.path, baseName }); - return result[0]; + return mdx; } catch (e) { brokenList.push({ original: info.path, baseName }); throw e; @@ -68,7 +68,7 @@ process.on('exit', () => { export async function transform( info: FileInfo, baseName: string -): Promise<[string, string | null]> { +): Promise<{ mdx: string; csf: string | null }> { const root = mdxProcessor.parse(info.source); const storyNamespaceName = nameToValidExport(`${baseName}Stories`); @@ -220,7 +220,10 @@ export async function transform( if (storiesMap.size === 0) { // A CSF file must have at least one story, so skip migrating if this is the case. - return [mdxProcessor.stringify(root), null]; + return { + csf: null, + mdx: mdxProcessor.stringify(root), + }; } // Rewrites the Meta tag to use the new story namespace @@ -354,7 +357,10 @@ export async function transform( filepath: path, }); - return [newMdx, output]; + return { + csf: output, + mdx: newMdx, + }; } function getEsmAst(root: ReturnType) { From 45caf9f70649144485a1d9e911665ab687ee373f Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 23 Feb 2024 13:57:57 +0100 Subject: [PATCH 27/39] Add todo comment --- code/lib/codemod/src/transforms/mdx-to-csf.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/lib/codemod/src/transforms/mdx-to-csf.ts b/code/lib/codemod/src/transforms/mdx-to-csf.ts index ed95f433a2ce..765331e40b2e 100644 --- a/code/lib/codemod/src/transforms/mdx-to-csf.ts +++ b/code/lib/codemod/src/transforms/mdx-to-csf.ts @@ -56,6 +56,8 @@ export default async function jscodeshift(info: FileInfo) { } } +// The JSCodeshift CLI doesn't return a list of files that were transformed or skipped. +// This is a workaround to rename the files after the transformation, which we can remove after we switch from jscodeshift to another solution. process.on('exit', () => { renameList.forEach((file) => { fs.renameSync(file.original, `${file.baseName}.mdx`); From c29718de6204d544713cad496e9ebe141b769cca Mon Sep 17 00:00:00 2001 From: Benjamin Kindle Date: Sat, 24 Feb 2024 15:28:04 -0500 Subject: [PATCH 28/39] perf(manager): improve performance when switching stories 3 changes that add up to a moderate speed-up when switching stories, especially in projects where there are many (several hundred) stories. 1. Fuse and list within the Search component are now only calculated when needed (when the value of the search input changes), not on each render. 2. The value returned by LayoutProvider is now memoized. This has a moderate impact because this hook is used in Node within the sidebar, which may have hundreds of instances. Before this change, useLayout was causing Node to rerender every time, making its React.memo useless. 3. The list of items inside Tree is now memoized. This is helpful because this list is made by mapping over a very large list --- .../src/components/layout/LayoutProvider.tsx | 39 ++++---- .../manager/src/components/sidebar/Search.tsx | 10 +- .../manager/src/components/sidebar/Tree.tsx | 96 +++++++++++-------- 3 files changed, 85 insertions(+), 60 deletions(-) diff --git a/code/ui/manager/src/components/layout/LayoutProvider.tsx b/code/ui/manager/src/components/layout/LayoutProvider.tsx index ae088f6358d0..81c342e0546c 100644 --- a/code/ui/manager/src/components/layout/LayoutProvider.tsx +++ b/code/ui/manager/src/components/layout/LayoutProvider.tsx @@ -1,5 +1,5 @@ import type { FC, PropsWithChildren } from 'react'; -import React, { createContext, useContext, useState } from 'react'; +import React, { createContext, useContext, useMemo, useState } from 'react'; import { useMediaQuery } from '../hooks/useMedia'; import { BREAKPOINT } from '../../constants'; @@ -32,22 +32,29 @@ export const LayoutProvider: FC = ({ children }) => { const isDesktop = useMediaQuery(`(min-width: ${BREAKPOINT}px)`); const isMobile = !isDesktop; - return ( - - {children} - + const contextValue = useMemo( + () => ({ + isMobileMenuOpen, + setMobileMenuOpen, + isMobileAboutOpen, + setMobileAboutOpen, + isMobilePanelOpen, + setMobilePanelOpen, + isDesktop, + isMobile, + }), + [ + isMobileMenuOpen, + setMobileMenuOpen, + isMobileAboutOpen, + setMobileAboutOpen, + isMobilePanelOpen, + setMobilePanelOpen, + isDesktop, + isMobile, + ] ); + return {children}; }; export const useLayout = () => useContext(LayoutContext); diff --git a/code/ui/manager/src/components/sidebar/Search.tsx b/code/ui/manager/src/components/sidebar/Search.tsx index cbbecc8264a6..3939d1ea2a37 100644 --- a/code/ui/manager/src/components/sidebar/Search.tsx +++ b/code/ui/manager/src/components/sidebar/Search.tsx @@ -176,8 +176,8 @@ export const Search = React.memo<{ [api, inputRef, showAllComponents, DEFAULT_REF_ID] ); - const list: SearchItem[] = useMemo(() => { - return dataset.entries.reduce((acc, [refId, { index, status }]) => { + const makeFuse = useCallback(() => { + const list = dataset.entries.reduce((acc, [refId, { index, status }]) => { const groupStatus = getGroupStatus(index || {}, status); if (index) { @@ -196,12 +196,12 @@ export const Search = React.memo<{ } return acc; }, []); + return new Fuse(list, options); }, [dataset]); - const fuse = useMemo(() => new Fuse(list, options), [list]); - const getResults = useCallback( (input: string) => { + const fuse = makeFuse(); if (!input) return []; let results: DownshiftItem[] = []; @@ -229,7 +229,7 @@ export const Search = React.memo<{ return results; }, - [allComponents, fuse] + [allComponents, makeFuse] ); const stateReducer = useCallback( diff --git a/code/ui/manager/src/components/sidebar/Tree.tsx b/code/ui/manager/src/components/sidebar/Tree.tsx index d1a566bddf4c..42aa27ce694a 100644 --- a/code/ui/manager/src/components/sidebar/Tree.tsx +++ b/code/ui/manager/src/components/sidebar/Tree.tsx @@ -481,55 +481,73 @@ export const Tree = React.memo<{ const groupStatus = useMemo(() => getGroupStatus(collapsedData, status), [collapsedData, status]); - return ( - 0}> - - {collapsedItems.map((itemId) => { - const item = collapsedData[itemId]; - const id = createId(itemId, refId); - - if (item.type === 'root') { - const descendants = expandableDescendants[item.id]; - const isFullyExpanded = descendants.every((d: string) => expanded[d]); - return ( - // @ts-expect-error (TODO) - - ); - } - - const isDisplayed = !item.parent || ancestry[itemId].every((a: string) => expanded[a]); - const color = groupStatus[itemId] ? statusMapping[groupStatus[itemId]][1] : null; - + const treeItems = useMemo(() => { + return collapsedItems.map((itemId) => { + const item = collapsedData[itemId]; + const id = createId(itemId, refId); + + if (item.type === 'root') { + const descendants = expandableDescendants[item.id]; + const isFullyExpanded = descendants.every((d: string) => expanded[d]); return ( - itemId === oid || itemId.startsWith(`${oid}-`))} - isDisplayed={isDisplayed} + isOrphan={false} + isDisplayed isSelected={selectedStoryId === itemId} isExpanded={!!expanded[itemId]} setExpanded={setExpanded} + isFullyExpanded={isFullyExpanded} + expandableDescendants={descendants} onSelectStoryId={onSelectStoryId} /> ); - })} + } + + const isDisplayed = !item.parent || ancestry[itemId].every((a: string) => expanded[a]); + const color = groupStatus[itemId] ? statusMapping[groupStatus[itemId]][1] : null; + + return ( + itemId === oid || itemId.startsWith(`${oid}-`))} + isDisplayed={isDisplayed} + isSelected={selectedStoryId === itemId} + isExpanded={!!expanded[itemId]} + setExpanded={setExpanded} + onSelectStoryId={onSelectStoryId} + /> + ); + }); + }, [ + ancestry, + api, + collapsedData, + collapsedItems, + docsMode, + expandableDescendants, + expanded, + groupStatus, + onSelectStoryId, + orphanIds, + refId, + selectedStoryId, + setExpanded, + status, + ]); + return ( + 0}> + + {treeItems} ); }); From a945910ce3635b53543cf0b026fe1dad93318932 Mon Sep 17 00:00:00 2001 From: Anto Lepejian Date: Tue, 20 Feb 2024 10:44:30 +1100 Subject: [PATCH 29/39] done --- code/renderers/react/src/docs/jsxDecorator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/renderers/react/src/docs/jsxDecorator.tsx b/code/renderers/react/src/docs/jsxDecorator.tsx index e43702daf02f..3395a3860557 100644 --- a/code/renderers/react/src/docs/jsxDecorator.tsx +++ b/code/renderers/react/src/docs/jsxDecorator.tsx @@ -130,7 +130,7 @@ export const renderJsx = (code: React.ReactElement, options: JSXOptions) => { return string; }).join('\n'); - return result.replace(/function\s+noRefCheck\(\)\s+\{\}/g, '() => {}'); + return result.replace(/function\s+noRefCheck\(\)\s*\{\}/g, '() => {}'); }; const defaultOpts = { From e8b3c853b879fecc6a27ccd821fce96021187c23 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 26 Feb 2024 11:54:22 +0100 Subject: [PATCH 30/39] revert component values back to array --- .../src/modules/preview-web/docs-context/DocsContext.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts index 5c83e764ec02..b22ad4afe444 100644 --- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts +++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts @@ -17,7 +17,7 @@ import type { StoryStore } from '../../store'; import type { DocsContextProps } from './DocsContextProps'; export class DocsContext implements DocsContextProps { - private componentStoriesValue: Set>; + private componentStoriesValue: PreparedStory[]; private storyIdToCSFFile: Map>; @@ -38,7 +38,7 @@ export class DocsContext implements DocsContextProps /** The CSF files known (via the index) to be refererenced by this docs file */ csfFiles: CSFFile[] ) { - this.componentStoriesValue = new Set(); + this.componentStoriesValue = []; this.storyIdToCSFFile = new Map(); this.exportToStory = new Map(); this.exportsToCSFFile = new Map(); @@ -83,7 +83,7 @@ export class DocsContext implements DocsContextProps stories.forEach((story) => { this.nameToStoryId.set(story.name, story.id); - this.componentStoriesValue.add(story); + this.componentStoriesValue.push(story); if (!this.primaryStory) this.primaryStory = story; }); } @@ -207,7 +207,7 @@ export class DocsContext implements DocsContextProps }; componentStories = () => { - return Array.from(this.componentStoriesValue); + return this.componentStoriesValue; }; componentStoriesFromCSFFile = (csfFile: CSFFile) => { From c6086234108f16533622ad17d5a0894cbf8152a1 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 26 Feb 2024 14:54:39 +0100 Subject: [PATCH 31/39] Fix Yarn2Proxy findInstallations method --- .../core-common/src/js-package-manager/Yarn2Proxy.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts index 4b69af2c40ff..7d1361f5bde8 100644 --- a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts @@ -104,15 +104,10 @@ export class Yarn2Proxy extends JsPackageManager { } public async findInstallations(pattern: string[]) { + console.log(['info', '--name-only', '--recursive', ...pattern].join(' ')); const commandResult = await this.executeCommand({ command: 'yarn', - args: [ - 'info', - '--name-only', - '--recursive', - pattern.map((p) => `"${p}"`).join(' '), - `"${pattern}"`, - ], + args: ['info', '--name-only', '--recursive', ...pattern], env: { FORCE_COLOR: 'false', }, @@ -261,7 +256,7 @@ export class Yarn2Proxy extends JsPackageManager { lines.forEach((packageName) => { if ( !packageName || - !pattern.some((p) => new RegExp(`"${p.replace(/\*/g, '.*')}`).test(packageName)) + !pattern.some((p) => new RegExp(`${p.replace(/\*/g, '.*')}`).test(packageName)) ) { return; } From 9b20b891856d7ae6f03d2596b05dc227bece2485 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 26 Feb 2024 16:25:27 +0100 Subject: [PATCH 32/39] Don't show empty arg tables in doc pages --- code/ui/blocks/src/blocks/Controls.tsx | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/code/ui/blocks/src/blocks/Controls.tsx b/code/ui/blocks/src/blocks/Controls.tsx index 2adef888e66a..b7a1398c1994 100644 --- a/code/ui/blocks/src/blocks/Controls.tsx +++ b/code/ui/blocks/src/blocks/Controls.tsx @@ -59,16 +59,19 @@ export const Controls: FC = (props) => { const hasSubcomponents = Boolean(subcomponents) && Object.keys(subcomponents).length > 0; if (!hasSubcomponents) { - return ( - - ); + if (Object.keys(filteredArgTypes).length > 0 || Object.keys(args).length > 0) { + return ( + + ); + } + return null; } const mainComponentName = getComponentName(component); From 741cb275621cb265c7adc8453553622c7dc5fd86 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Mon, 26 Feb 2024 16:38:14 +0100 Subject: [PATCH 33/39] remove deprecation of `manager-api`'s `types` export --- code/lib/manager-api/src/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/code/lib/manager-api/src/index.tsx b/code/lib/manager-api/src/index.tsx index 24e288782adc..d82d241ce7f5 100644 --- a/code/lib/manager-api/src/index.tsx +++ b/code/lib/manager-api/src/index.tsx @@ -520,7 +520,6 @@ export { addons } from './lib/addons'; /** * We need to rename this so it's not compiled to a straight re-export * Our globalization plugin can't handle an import and export of the same name in different lines - * @deprecated */ const typesX = types; From 1e163b809fb0454fb23cbca79a1095bc9ff0709b Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 26 Feb 2024 16:56:54 +0100 Subject: [PATCH 34/39] Fix overflow bug by using a better link --- code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts index 6a517fc0be0c..7dbc514d77b9 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts @@ -75,7 +75,8 @@ export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPa Make sure to assign an explicit ${chalk.cyan('fn')} to your args for those usages. - For more information please visit our migration guide: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#implicit-actions-can-not-be-used-during-rendering-for-example-in-the-play-function + For more information please visit our docs: + https://storybook.js.org/docs/8.0/essentials/actions#via-storybooktest-fn-spy-function `; }, }; From 68c535031d93458638b1cd8b793f3551e1718856 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 27 Feb 2024 09:16:29 +0100 Subject: [PATCH 35/39] Add E2E test for multiple CSF files with same title in autodocs --- .../docs2/multiple-csf-files-a.stories.ts | 23 +++++++++++++++++++ .../docs2/multiple-csf-files-b.stories.ts | 23 +++++++++++++++++++ code/e2e-tests/addon-docs.spec.ts | 15 ++++++++++++ .../svelte/template/components/Html.svelte | 2 +- 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 code/addons/docs/template/stories/docs2/multiple-csf-files-a.stories.ts create mode 100644 code/addons/docs/template/stories/docs2/multiple-csf-files-b.stories.ts diff --git a/code/addons/docs/template/stories/docs2/multiple-csf-files-a.stories.ts b/code/addons/docs/template/stories/docs2/multiple-csf-files-a.stories.ts new file mode 100644 index 000000000000..c77284a296f3 --- /dev/null +++ b/code/addons/docs/template/stories/docs2/multiple-csf-files-a.stories.ts @@ -0,0 +1,23 @@ +import { global as globalThis } from '@storybook/global'; + +export default { + title: 'Multiple CSF Files Same Title', + component: globalThis.Components.Html, + tags: ['autodocs'], + args: { + content: '

paragraph

', + }, + parameters: { + chromatic: { disable: true }, + }, +}; + +export const DefaultA = {}; + +export const SpanContent = { + args: { content: 'span' }, +}; + +export const CodeContent = { + args: { content: 'code' }, +}; diff --git a/code/addons/docs/template/stories/docs2/multiple-csf-files-b.stories.ts b/code/addons/docs/template/stories/docs2/multiple-csf-files-b.stories.ts new file mode 100644 index 000000000000..955c04af9f9e --- /dev/null +++ b/code/addons/docs/template/stories/docs2/multiple-csf-files-b.stories.ts @@ -0,0 +1,23 @@ +import { global as globalThis } from '@storybook/global'; + +export default { + title: 'Multiple CSF Files Same Title', + component: globalThis.Components.Html, + tags: ['autodocs'], + args: { + content: '

paragraph

', + }, + parameters: { + chromatic: { disable: true }, + }, +}; + +export const DefaultB = {}; + +export const H1Content = { + args: { content: '

heading 1

' }, +}; + +export const H2Content = { + args: { content: '

heading 2

' }, +}; diff --git a/code/e2e-tests/addon-docs.spec.ts b/code/e2e-tests/addon-docs.spec.ts index 4ae3da33e99e..72470acb62ab 100644 --- a/code/e2e-tests/addon-docs.spec.ts +++ b/code/e2e-tests/addon-docs.spec.ts @@ -210,4 +210,19 @@ test.describe('addon-docs', () => { await expect(componentReactVersion).toHaveText(expectedReactVersion); await expect(componentReactDomVersion).toHaveText(expectedReactVersion); }); + + test('should have stories from multiple CSF files in autodocs', async ({ page }) => { + const sbPage = new SbPage(page); + await sbPage.navigateToStory('/addons/docs/multiple-csf-files-same-title', 'docs'); + const root = sbPage.previewRoot(); + + const storyHeadings = root.locator('.sb-anchor > h3'); + await expect(await storyHeadings.count()).toBe(6); + await expect(storyHeadings.nth(0)).toHaveText('Default A'); + await expect(storyHeadings.nth(1)).toHaveText('Span Content'); + await expect(storyHeadings.nth(2)).toHaveText('Code Content'); + await expect(storyHeadings.nth(3)).toHaveText('Default B'); + await expect(storyHeadings.nth(4)).toHaveText('H 1 Content'); + await expect(storyHeadings.nth(5)).toHaveText('H 2 Content'); + }); }); diff --git a/code/renderers/svelte/template/components/Html.svelte b/code/renderers/svelte/template/components/Html.svelte index e341acf73280..bc155de5d4b1 100644 --- a/code/renderers/svelte/template/components/Html.svelte +++ b/code/renderers/svelte/template/components/Html.svelte @@ -5,4 +5,4 @@ export let content = ''; -
{@html content}>
+
{@html content}
From 6975de8286b4df20efa99f9c41938702b44707a9 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 27 Feb 2024 11:39:47 +0100 Subject: [PATCH 36/39] fix issues with types, change the comment to not be jsdoc --- code/lib/manager-api/src/index.tsx | 6 ++---- code/lib/types/src/modules/addons.ts | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/code/lib/manager-api/src/index.tsx b/code/lib/manager-api/src/index.tsx index d82d241ce7f5..fbb8b74a6420 100644 --- a/code/lib/manager-api/src/index.tsx +++ b/code/lib/manager-api/src/index.tsx @@ -517,10 +517,8 @@ export function useArgTypes(): ArgTypes { export { addons } from './lib/addons'; -/** - * We need to rename this so it's not compiled to a straight re-export - * Our globalization plugin can't handle an import and export of the same name in different lines - */ +// We need to rename this so it's not compiled to a straight re-export +// Our globalization plugin can't handle an import and export of the same name in different lines const typesX = types; export { typesX as types }; diff --git a/code/lib/types/src/modules/addons.ts b/code/lib/types/src/modules/addons.ts index fe4fa8551cfa..c1325e3f07ec 100644 --- a/code/lib/types/src/modules/addons.ts +++ b/code/lib/types/src/modules/addons.ts @@ -358,7 +358,12 @@ export interface Addon_BaseType { * This is called as a function, so if you want to use hooks, * your function needs to return a JSX.Element within which components are rendered */ - render: (renderOptions: Partial) => ReactElement | null; + render: (props: Partial) => ReturnType>>; + // TODO: for Storybook 9 I'd like to change this to be: + // render: FC>; + // This would bring it in line with how every other addon is set up. + // We'd need to change how the render function is called in the manager: + // https://github.com/storybookjs/storybook/blob/4e6fc0dde0842841d99cb3cf5148ca293a950301/code/ui/manager/src/components/preview/Preview.tsx#L105 /** * @unstable */ From f104e662ba59401d53b65e5caa4b1fcf48e8281b Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 27 Feb 2024 12:36:08 +0100 Subject: [PATCH 37/39] Add story for empty argTypes and address review --- .../ui/blocks/src/blocks/Controls.stories.tsx | 15 ++++++++++-- code/ui/blocks/src/blocks/Controls.tsx | 24 +++++++++---------- .../src/examples/EmptyArgTypes.stories.tsx | 19 +++++++++++++++ 3 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 code/ui/blocks/src/examples/EmptyArgTypes.stories.tsx diff --git a/code/ui/blocks/src/blocks/Controls.stories.tsx b/code/ui/blocks/src/blocks/Controls.stories.tsx index 9d32d9fe12f1..598485dd93a6 100644 --- a/code/ui/blocks/src/blocks/Controls.stories.tsx +++ b/code/ui/blocks/src/blocks/Controls.stories.tsx @@ -6,17 +6,19 @@ import * as ExampleStories from '../examples/ControlsParameters.stories'; import * as SubcomponentsExampleStories from '../examples/ControlsWithSubcomponentsParameters.stories'; import { within } from '@storybook/test'; import type { PlayFunctionContext } from '@storybook/csf'; +import * as EmptyArgTypesStories from '../examples/EmptyArgTypes.stories'; -const meta: Meta = { +const meta = { component: Controls, parameters: { relativeCsfPaths: [ '../examples/ControlsParameters.stories', + '../examples/EmptyArgTypes.stories', '../examples/ControlsWithSubcomponentsParameters.stories', ], docsStyles: true, }, -}; +} satisfies Meta; export default meta; type Story = StoryObj; @@ -142,3 +144,12 @@ export const SubcomponentsSortProp: Story = { sort: 'alpha', }, }; + +/** + * When a story is defined without any argTypes or args, the Docs UI should not display the control component. + */ +export const EmptyArgTypes: Story = { + args: { + of: EmptyArgTypesStories.Default, + }, +}; diff --git a/code/ui/blocks/src/blocks/Controls.tsx b/code/ui/blocks/src/blocks/Controls.tsx index b7a1398c1994..f47194033f1d 100644 --- a/code/ui/blocks/src/blocks/Controls.tsx +++ b/code/ui/blocks/src/blocks/Controls.tsx @@ -59,19 +59,19 @@ export const Controls: FC = (props) => { const hasSubcomponents = Boolean(subcomponents) && Object.keys(subcomponents).length > 0; if (!hasSubcomponents) { - if (Object.keys(filteredArgTypes).length > 0 || Object.keys(args).length > 0) { - return ( - - ); + if (!(Object.keys(filteredArgTypes).length > 0 || Object.keys(args).length > 0)) { + return null; } - return null; + return ( + + ); } const mainComponentName = getComponentName(component); diff --git a/code/ui/blocks/src/examples/EmptyArgTypes.stories.tsx b/code/ui/blocks/src/examples/EmptyArgTypes.stories.tsx new file mode 100644 index 000000000000..b72fb9794acb --- /dev/null +++ b/code/ui/blocks/src/examples/EmptyArgTypes.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import type { ControlsParameters } from './ControlsParameters'; +import React from 'react'; + +const meta = { + title: 'examples/Empty ArgTypes for Control blocks', + // note that component is not specified, so no argtypes can be generated + render: () =>
I am a story without args or argTypes
, + parameters: { chromatic: { disableSnapshot: true } }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +/** + * There are no argTypes or args, so this story won't show any controls in the docs page. + * In the control addon it will show a UI how to set up controls. + */ +export const Default: Story = {}; From aefa17aa3e2c5ee87153b58fae77202cd24a8975 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 27 Feb 2024 14:08:40 +0100 Subject: [PATCH 38/39] cleanup --- code/ui/manager/src/components/sidebar/Search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ui/manager/src/components/sidebar/Search.tsx b/code/ui/manager/src/components/sidebar/Search.tsx index 3939d1ea2a37..0df164a2d35b 100644 --- a/code/ui/manager/src/components/sidebar/Search.tsx +++ b/code/ui/manager/src/components/sidebar/Search.tsx @@ -5,7 +5,7 @@ import Downshift from 'downshift'; import type { FuseOptions } from 'fuse.js'; import Fuse from 'fuse.js'; import { global } from '@storybook/global'; -import React, { useMemo, useRef, useState, useCallback } from 'react'; +import React, { useRef, useState, useCallback } from 'react'; import { CloseIcon, SearchIcon } from '@storybook/icons'; import { DEFAULT_REF_ID } from './Sidebar'; import type { From 679f8223eb073d4ed539266f2b9a81e787f5a00e Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 27 Feb 2024 14:34:48 +0100 Subject: [PATCH 39/39] Typo --- code/lib/cli/src/autoblock/block-dependencies-versions.ts | 2 +- code/lib/cli/src/automigrate/fixes/mdx-gfm.ts | 2 +- code/lib/cli/src/automigrate/types.ts | 2 +- code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/code/lib/cli/src/autoblock/block-dependencies-versions.ts b/code/lib/cli/src/autoblock/block-dependencies-versions.ts index 3bde65192ef2..45b5181dee0c 100644 --- a/code/lib/cli/src/autoblock/block-dependencies-versions.ts +++ b/code/lib/cli/src/autoblock/block-dependencies-versions.ts @@ -81,7 +81,7 @@ export const blocker = createBlocker({ default: return dedent` Support for ${data.packageName} version < ${data.minimumVersion} has been removed. - Since version 8, Storybook needs minimum version of ${data.minimumVersion}, but you had version ${data.installedVersion}. + Since version 8, Storybook needs a minimum version of ${data.minimumVersion}, but you have version ${data.installedVersion}. Please update this dependency. `; diff --git a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts index 2f3c570d4522..98bcaa736b87 100644 --- a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts +++ b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts @@ -84,7 +84,7 @@ export const mdxgfm: Fix = { Because of this you need to explicitly add the GFM plugin in the addon-docs options: https://storybook.js.org/docs/react/writing-docs/mdx#lack-of-github-flavored-markdown-gfm - We recommend you follow the guide on the link above, however we can add a temporary Storybook addon that helps make this migration easier. + We recommend that you follow the guide in the link above; however, we can add a temporary Storybook addon to help make this migration easier. We'll install the addon and add it to your storybook config. `; }, diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index 80cc4e74defc..36b4bac18c47 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -23,7 +23,7 @@ export interface RunOptions { * promptType defines how the user will be prompted to apply an automigration fix * - auto: the fix will be applied automatically * - manual: the user will be prompted to apply the fix - * - notification: the user will be notified about some changes. A fix isn't required, thought + * - notification: the user will be notified about some changes. A fix isn't required, though */ export type Prompt = 'auto' | 'manual' | 'notification'; diff --git a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts index 7d1361f5bde8..09f535e2dfa5 100644 --- a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts @@ -104,7 +104,6 @@ export class Yarn2Proxy extends JsPackageManager { } public async findInstallations(pattern: string[]) { - console.log(['info', '--name-only', '--recursive', ...pattern].join(' ')); const commandResult = await this.executeCommand({ command: 'yarn', args: ['info', '--name-only', '--recursive', ...pattern],