feat(storybook): add storybook 8 support (#27214)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #27175 #26914 #22941 #26531
This commit is contained in:
Colum Ferry 2024-08-14 16:37:20 +01:00 committed by GitHub
parent 3212bd12c0
commit 11f30dbd00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 3252 additions and 2280 deletions

View File

@ -9620,6 +9620,14 @@
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "migrate-8",
"path": "/nx-api/storybook/generators/migrate-8",
"name": "migrate-8",
"children": [],
"isExternal": false,
"disableCollapsible": false
}
],
"isExternal": false,

View File

@ -2929,6 +2929,15 @@
"originalFilePath": "/packages/storybook/src/generators/migrate-7/schema.json",
"path": "/nx-api/storybook/generators/migrate-7",
"type": "generator"
},
"/nx-api/storybook/generators/migrate-8": {
"description": "Migrate to Storybook version 8.",
"file": "generated/packages/storybook/generators/migrate-8.json",
"hidden": false,
"name": "migrate-8",
"originalFilePath": "/packages/storybook/src/generators/migrate-8/schema.json",
"path": "/nx-api/storybook/generators/migrate-8",
"type": "generator"
}
},
"path": "/nx-api/storybook"

View File

@ -2899,6 +2899,15 @@
"originalFilePath": "/packages/storybook/src/generators/migrate-7/schema.json",
"path": "storybook/generators/migrate-7",
"type": "generator"
},
{
"description": "Migrate to Storybook version 8.",
"file": "generated/packages/storybook/generators/migrate-8.json",
"hidden": false,
"name": "migrate-8",
"originalFilePath": "/packages/storybook/src/generators/migrate-8/schema.json",
"path": "storybook/generators/migrate-8",
"type": "generator"
}
],
"githubRoot": "https://github.com/nrwl/nx/blob/master",

View File

@ -6,7 +6,8 @@
"$id": "NxAngularComponentCypressSpecGenerator",
"type": "object",
"cli": "nx",
"description": "Creates a Cypress spec for a UI component that has a story.",
"description": "Creates a Storybook Cypress spec for a UI component that has a story.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
"properties": {
"projectName": {
"type": "string",

View File

@ -27,11 +27,13 @@
},
"generateCypressSpecs": {
"type": "boolean",
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator."
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"cypressProject": {
"type": "string",
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default."
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"skipFormat": {
"description": "Skip formatting files.",

View File

@ -6,7 +6,8 @@
"cli": "nx",
"$id": "NxReactComponentCypressSpec",
"title": "Create component Cypress spec",
"description": "Create a Cypress spec for a UI component that has a story.",
"description": "Create a Storybook Cypress spec for a UI component that has a story.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
"type": "object",
"properties": {
"project": {

View File

@ -19,11 +19,13 @@
},
"generateCypressSpecs": {
"type": "boolean",
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator."
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"cypressProject": {
"type": "string",
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default."
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"interactionTests": {
"type": "boolean",

View File

@ -18,12 +18,18 @@
"x-dropdown": "projects",
"x-priority": "important"
},
"interactionTests": {
"type": "boolean",
"description": "Set up Storybook interaction tests.",
"x-prompt": "Do you want to set up Storybook interaction tests?",
"x-priority": "important",
"alias": ["configureTestRunner"],
"default": true
},
"configureCypress": {
"type": "boolean",
"description": "Run the cypress-configure generator.",
"x-prompt": "Configure a cypress e2e app to run against the storybook instance?",
"default": true,
"x-priority": "important"
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"generateStories": {
"type": "boolean",
@ -35,9 +41,7 @@
"generateCypressSpecs": {
"type": "boolean",
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
"x-prompt": "Automatically generate test files in the Cypress E2E app generated by the cypress-configure generator?",
"default": true,
"x-priority": "important"
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"configureStaticServe": {
"type": "boolean",
@ -48,7 +52,8 @@
},
"cypressDirectory": {
"type": "string",
"description": "A directory where the Cypress project will be placed. Placed at the root by default."
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"js": {
"type": "boolean",

File diff suppressed because one or more lines are too long

View File

@ -19,11 +19,13 @@
},
"generateCypressSpecs": {
"type": "boolean",
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator."
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"cypressProject": {
"type": "string",
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default."
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"interactionTests": {
"type": "boolean",

View File

@ -675,6 +675,7 @@
- [cypress-project](/nx-api/storybook/generators/cypress-project)
- [convert-to-inferred](/nx-api/storybook/generators/convert-to-inferred)
- [migrate-7](/nx-api/storybook/generators/migrate-7)
- [migrate-8](/nx-api/storybook/generators/migrate-8)
- [tao](/nx-api/tao)
- [vite](/nx-api/vite)
- [documents](/nx-api/vite/documents)

View File

@ -9,7 +9,5 @@ module.exports = {
name: '@storybook/react-webpack5',
options: {},
},
docs: {
autodocs: true,
},
docs: {},
};

View File

@ -22,3 +22,4 @@ export const decorators = [
);
},
];
export const tags = ['autodocs'];

View File

@ -7,6 +7,7 @@ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
options: {},

View File

@ -1,11 +1,9 @@
module.exports = {
stories: ['../src/lib/**/*.stories.@(mdx|js|jsx|ts|tsx)'],
stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@nx/react/plugins/storybook'],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
docs: {
autodocs: true,
},
docs: {},
};

View File

@ -1,3 +1,4 @@
import 'graph/client/.storybook/tailwind-imports.css';
export const parameters = {};
export const tags = ['autodocs'];

View File

@ -5,7 +5,5 @@ module.exports = {
name: '@storybook/react-webpack5',
options: {},
},
docs: {
autodocs: true,
},
docs: {},
};

View File

@ -1,3 +1,4 @@
import './tailwind-imports.css';
export const parameters = {};
export const tags = ['autodocs'];

View File

@ -8,6 +8,7 @@ import { mergeConfig } from 'vite';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: ['@storybook/addon-essentials'],
framework: {
name: '@storybook/react-vite',
options: {},
@ -17,6 +18,8 @@ const config: StorybookConfig = {
mergeConfig(config, {
plugins: [nxViteTsPaths()],
}),
docs: {},
};
export default config;

View File

@ -1 +1,2 @@
import './tailwind.css';
export const tags = ['autodocs'];

View File

@ -7,9 +7,7 @@ const config: StorybookConfig = {
name: '@storybook/react-webpack5',
options: {},
},
docs: {
autodocs: true,
},
docs: {},
};
export default config;

View File

@ -1 +1,2 @@
import './tailwind-imports.css';
export const tags = ['autodocs'];

View File

@ -5,7 +5,5 @@ module.exports = {
name: '@storybook/react-webpack5',
options: {},
},
docs: {
autodocs: true,
},
docs: {},
};

View File

@ -1 +1,2 @@
import './tailwind-imports.css';
export const tags = ['autodocs'];

View File

@ -96,12 +96,13 @@
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-url": "^8.0.2",
"@schematics/angular": "~18.1.0",
"@storybook/addon-essentials": "7.5.3",
"@storybook/core-server": "7.5.3",
"@storybook/react": "7.5.3",
"@storybook/react-vite": "7.5.3",
"@storybook/react-webpack5": "7.5.3",
"@storybook/types": "^7.1.1",
"@storybook/addon-essentials": "^8.2.8",
"@storybook/addon-interactions": "^8.2.8",
"@storybook/core-server": "^8.2.8",
"@storybook/react": "^8.2.8",
"@storybook/react-vite": "^8.2.8",
"@storybook/react-webpack5": "^8.2.8",
"@storybook/types": "^8.2.8",
"@supabase/supabase-js": "^2.26.0",
"@svgr/rollup": "^8.1.0",
"@svgr/webpack": "^8.0.1",
@ -179,7 +180,7 @@
"eslint-plugin-playwright": "^0.15.3",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-storybook": "^0.6.12",
"eslint-plugin-storybook": "^0.8.0",
"express": "^4.19.2",
"fast-xml-parser": "^4.2.7",
"figures": "3.2.0",
@ -270,7 +271,8 @@
"source-map": "0.7.3",
"source-map-loader": "^5.0.0",
"source-map-support": "0.5.19",
"storybook-dark-mode": "^3.0.0",
"storybook": "^8.2.8",
"storybook-dark-mode": "^4.0.2",
"style-loader": "^3.3.0",
"tar-stream": "~2.2.0",
"tcp-port-used": "^1.0.2",
@ -394,7 +396,9 @@
"!{workspaceRoot}/packages/**/.eslintrc.json"
]
},
"outputs": ["{workspaceRoot}/docs/generated"]
"outputs": [
"{workspaceRoot}/docs/generated"
]
}
},
"packageManager": "pnpm@8.15.7"

View File

@ -3,7 +3,8 @@
"$id": "NxAngularComponentCypressSpecGenerator",
"type": "object",
"cli": "nx",
"description": "Creates a Cypress spec for a UI component that has a story.",
"description": "Creates a Storybook Cypress spec for a UI component that has a story.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
"properties": {
"projectName": {
"type": "string",

View File

@ -27,11 +27,13 @@
},
"generateCypressSpecs": {
"type": "boolean",
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator."
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"cypressProject": {
"type": "string",
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default."
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"skipFormat": {
"description": "Skip formatting files.",

View File

@ -7,7 +7,7 @@ exports[`StorybookConfiguration generator should configure storybook to use webp
const config: StorybookConfig = {
stories: [
'../**/*.stories.@(js|jsx|ts|tsx|mdx)' ],
'../**/*.@(mdx|stories.@(js|jsx|ts|tsx))' ],
addons: ['@storybook/addon-essentials' , '@storybook/addon-interactions' ],
framework: {
name: '@storybook/angular',

View File

@ -1,5 +1,12 @@
{
"generators": {},
"generators": {
"add-vue-to-storybook-config": {
"cli": "nx",
"version": "19.6.0-beta.0",
"description": "Add vue() plugin to viteFinal in Storybook config files when it is missing.",
"implementation": "./src/migrations/update-19-6-0/add-vue-plugin-to-storybook-config"
}
},
"packageJsonUpdates": {
"18.2.0": {
"version": "18.2.0-beta.0",

View File

@ -35,7 +35,8 @@
"@nx/js": "file:../js",
"@nx/eslint": "file:../eslint",
"@nx/vue": "file:../vue",
"@nx/vite": "file:../vite"
"@nx/vite": "file:../vite",
"@phenomnomnominal/tsquery": "~5.0.1"
},
"peerDependencies": {},
"publishConfig": {

View File

@ -5,9 +5,10 @@ exports[`nuxt:storybook-configuration should configure with vue3 framework and s
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { mergeConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/vue3-vite',
@ -16,7 +17,7 @@ const config: StorybookConfig = {
viteFinal: async (config) =>
mergeConfig(config, {
plugins: [nxViteTsPaths()],
plugins: [vue(), nxViteTsPaths()],
}),
};

View File

@ -29,7 +29,7 @@ export async function storybookConfigurationGenerator(
joinPathFragments(
root,
'.storybook',
'preview.' + options.tsConfiguration ? 'ts' : 'js'
'preview.' + (options.tsConfiguration ? 'ts' : 'js')
),
`import '../src/assets/css/styles.css';`
);

View File

@ -0,0 +1,63 @@
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import addVuePluginToStorybookConfig from './add-vue-plugin-to-storybook-config';
describe('addVuePluginToStorybookConfig', () => {
it('should update the storybook config to add the vue() plugin when the project is nuxt', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace();
tree.write(
'apps/nuxt/.storybook/main.ts',
`import type { StorybookConfig } from '@storybook/vue3-vite';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { mergeConfig } from 'vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: ['@storybook/addon-essentials'],
framework: {
name: '@storybook/vue3-vite',
options: {},
},
viteFinal: async (config) =>
mergeConfig(config, {
plugins: [nxViteTsPaths()],
}),
};
export default config;`
);
tree.write('apps/nuxt/nuxt.config.ts', '');
// ACT
await addVuePluginToStorybookConfig(tree);
// ASSERT
expect(tree.read('apps/nuxt/.storybook/main.ts', 'utf-8'))
.toMatchInlineSnapshot(`
"import vue from '@vitejs/plugin-vue';
import type { StorybookConfig } from '@storybook/vue3-vite';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { mergeConfig } from 'vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: ['@storybook/addon-essentials'],
framework: {
name: '@storybook/vue3-vite',
options: {},
},
viteFinal: async (config) =>
mergeConfig(config, {
plugins: [vue(), nxViteTsPaths()],
}),
};
export default config;
"
`);
});
});

View File

@ -0,0 +1,71 @@
import {
formatFiles,
joinPathFragments,
type Tree,
visitNotIgnoredFiles,
} from '@nx/devkit';
import { dirname } from 'path';
import { tsquery } from '@phenomnomnominal/tsquery';
export default async function (tree: Tree) {
visitNotIgnoredFiles(tree, '', (path) => {
if (
!path.endsWith('.storybook/main.ts') &&
!path.endsWith('.storybook/main.js')
) {
return;
}
const projectRoot = dirname(dirname(path));
const possibleNuxtConfigPaths = [
joinPathFragments(projectRoot, 'nuxt.config.ts'),
joinPathFragments(projectRoot, 'nuxt.config.js'),
];
if (!possibleNuxtConfigPaths.some((p) => tree.exists(p))) {
return;
}
const pathToStorybookConfig = path;
const storybookConfigContents = tree.read(pathToStorybookConfig, 'utf-8');
if (
!storybookConfigContents.includes('viteFinal') &&
!storybookConfigContents.includes('@vitejs/plugin-vue')
) {
return;
}
const VITE_FINAL_PLUGINS_SELECTOR =
'PropertyAssignment:has(Identifier[name=viteFinal]) PropertyAssignment:has(Identifier[name=plugins]) > ArrayLiteralExpression';
const ast = tsquery.ast(storybookConfigContents);
const nodes = tsquery(ast, VITE_FINAL_PLUGINS_SELECTOR, {
visitAllChildren: true,
});
if (!nodes.length) {
// This would be an invalid config modified by the user already if it does work
// Therefore, do not touch their config file
return;
}
const pluginsValueNode = nodes[0];
if (pluginsValueNode.getText().includes('vue()')) {
// The plugin has already been registered, do nothing
return;
}
const updatedPluginsValue = `[vue(), ${pluginsValueNode
.getText()
.slice(1)}`;
let newStorybookConfigContents = `${storybookConfigContents.slice(
0,
pluginsValueNode.getStart()
)}${updatedPluginsValue}${storybookConfigContents.slice(
pluginsValueNode.getEnd()
)}`;
newStorybookConfigContents = `import vue from '@vitejs/plugin-vue';
${newStorybookConfigContents}`;
tree.write(pathToStorybookConfig, newStorybookConfigContents);
});
await formatFiles(tree);
}

View File

@ -3,7 +3,8 @@
"cli": "nx",
"$id": "NxReactComponentCypressSpec",
"title": "Create component Cypress spec",
"description": "Create a Cypress spec for a UI component that has a story.",
"description": "Create a Storybook Cypress spec for a UI component that has a story.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
"type": "object",
"properties": {
"project": {

View File

@ -19,11 +19,13 @@
},
"generateCypressSpecs": {
"type": "boolean",
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator."
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"cypressProject": {
"type": "string",
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default."
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"interactionTests": {
"type": "boolean",

View File

@ -5,9 +5,10 @@ exports[`react:storybook-configuration should configure everything and install c
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { mergeConfig } from 'vite';
import react from '@vitejs/plugin-react';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
@ -16,7 +17,7 @@ const config: StorybookConfig = {
viteFinal: async (config) =>
mergeConfig(config, {
plugins: [nxViteTsPaths()],
plugins: [react(), nxViteTsPaths()],
}),
};

View File

@ -23,7 +23,7 @@ exports[`Storybook Configuration it should create a storybook configuration and
"import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
@ -66,7 +66,7 @@ exports[`Storybook Configuration it should create a storybook configuration and
"import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
@ -122,7 +122,7 @@ exports[`Storybook Configuration it should create a storybook configuration and
"import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',

View File

@ -2,6 +2,7 @@ import { Linter } from '@nx/eslint';
export interface StorybookConfigurationSchema {
project: string;
interactionTests?: boolean;
configureCypress: boolean;
generateStories?: boolean;
generateCypressSpecs?: boolean;

View File

@ -18,12 +18,18 @@
"x-dropdown": "projects",
"x-priority": "important"
},
"interactionTests": {
"type": "boolean",
"description": "Set up Storybook interaction tests.",
"x-prompt": "Do you want to set up Storybook interaction tests?",
"x-priority": "important",
"alias": ["configureTestRunner"],
"default": true
},
"configureCypress": {
"type": "boolean",
"description": "Run the cypress-configure generator.",
"x-prompt": "Configure a cypress e2e app to run against the storybook instance?",
"default": true,
"x-priority": "important"
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"generateStories": {
"type": "boolean",
@ -35,9 +41,7 @@
"generateCypressSpecs": {
"type": "boolean",
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
"x-prompt": "Automatically generate test files in the Cypress E2E app generated by the cypress-configure generator?",
"default": true,
"x-priority": "important"
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"configureStaticServe": {
"type": "boolean",
@ -48,7 +52,8 @@
},
"cypressDirectory": {
"type": "string",
"description": "A directory where the Cypress project will be placed. Placed at the root by default."
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"js": {
"type": "boolean",

View File

@ -0,0 +1,122 @@
---
title: Storybook 8 Migration Generator Examples
description: This page contains examples for the @nx/storybook:migrate-8 generator.
---
Storybook 8 is a major release that brings a lot of new features and improvements. You can read more about it in the [Storybook 8.0.0 release article](https://storybook.js.org/blog/storybook-8/). Apart from the new features and improvements it introduces, it also brings some breaking changes. You can read more about them in the [Storybook 8 migration docs](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800) and the [Storybook 8.0.0 migration guide](https://storybook.js.org/docs/react/migration-guide).
You can now migrate your existing Nx workspace with Storybook configuration to use Storybook version 8. To help you, Nx offers the `@nx/storybook:migrate-8` generator. This generator will help you migrate your existing Storybook setup to version 8.
## How to use it
Just call:
```bash
npx nx g @nx/storybook:migrate-8
```
{% callout type="warning" title="Commit your changes" %}
It is advised that you start with a clean git history before running this generator, since it is going to be making lots of changes to your workspace.
{% /callout %}
You can run this generator using the above command, without passing any options. This will start the migration process for all your projects that have Storybook configured. The logs will explain what is happening in every step, and the logs are mixed Nx and Storybook CLI logs. During the process you will be prompted by the Storybook CLI to accept the automigration scripts. You can read more about that in the next section.
When the generator finishes, you will see a summary of the changes that were made to your workspace, and it will also create a new file, called `storybook-migration-summary.md` at the root of your project, which will contain a list of all the changes that were made to your workspace.
### Accept the automigration prompts
The Storybook CLI (running through our generator) will prompt you to run some code generators and modifiers.
You can say `yes` to these prompts, which are usually the following (there may be more or less, depending on your setup,
and depending on the latest versions of the Storybook CLI - this code is NOT managed by Nx, but by Storybook):
- `mainjsFramework`: It will try to add the `framework` field in your project's `.storybook/main.js|ts` file.
- `eslintPlugin`: installs the `eslint-plugin-storybook`
- `newFrameworks`: removes unused dependencies (eg. `@storybook/builder-webpack5`, `@storybook/manager-webpack5`, `@storybook/builder-vite`)
- `autodocsTrue`: adds `autodocs: true` to your project's `.storybook/main.js|ts` file
### Check the result
Once the generator finishes, and the Storybook CLI automigration scripts have run, you should check the result. Examples of migrated `.storybook/main.js|ts` files would look like this:
#### Full example for Angular projects
Here is an example of a project-level `.storybook/main.js|ts` file for an Angular project that has been migrated to Storybook version 8:
```ts {% fileName="apps/my-angular-app/.storybook/main.js" %}
const config = {
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-essentials'],
framework: {
name: '@storybook/angular',
options: {},
},
};
export default config;
```
#### Full example for React projects with Vite
Here is an example of a project-level `.storybook/main.js|ts` file for a React project using Vite that has been migrated to Storybook version 8:
```ts {% fileName="apps/my-react-app/.storybook/main.js" %}
const config = {
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-essentials'],
framework: {
name: '@storybook/react-vite',
options: {
builder: {
viteConfigPath: 'apps/rv1/vite.config.ts',
},
},
},
};
export default config;
```
### Make sure that all works by running Storybook
You can now use Storybook 8! 🎉
```bash
npx nx build-storybook PROJECT_NAME
```
and
```bash
npx nx storybook PROJECT_NAME
```
## Run the generator by automatically accepting the Storybook CLI prompts
You can run the generator with the `--autoAcceptAllPrompts` flag, which will automatically accept all the Storybook CLI prompts. This is useful if you want to run the generator in a CI environment, or if you want to run the generator in a script. Or if you are sure that you want to accept all the prompts!
```bash
npx nx g @nx/storybook:migrate-8 --autoAcceptAllPrompts
```
The Storybook CLI may still ask you about some things, but mostly it should just run the whole migration suite uninterrupted.
## Run the migration manually
Nx gives you the ability to run all the migration steps one by one, manually, but still with the help of our migrator. To help you out with the commands that you need to run, Nx will print out the instructions if you run the generator with the `--onlyShowListOfCommands` flag, like this:
```bash
npx nx g @nx/storybook:migrate-8 --onlyShowListOfCommands
```
Essentially, the way to run the migration manually is the following:
1. Call the Nx generator to show you the list of commands:
`npx nx g @nx/storybook:migrate-8 --onlyShowListOfCommands`
2. Call the Storybook upgrade script:
`npx storybook@latest upgrade`
3. Call the Storybook automigrate scripts for each one of the projects using Storybook (the `@nx/storybook:migrate-8` will give you the list of all the commands)
## Report any issues and bugs
Please report any issues and bugs you find [on the Nx GitHub page](https://github.com/nrwl/nx/issues/new/choose) or on the [Storybook GitHub page](https://github.com/storybookjs/storybook/issues/new/choose).

View File

@ -32,6 +32,12 @@
"schema": "./src/generators/migrate-7/schema.json",
"description": "Migrate to Storybook version 7.",
"hidden": false
},
"migrate-8": {
"factory": "./src/generators/migrate-8/migrate-8",
"schema": "./src/generators/migrate-8/schema.json",
"description": "Migrate to Storybook version 8.",
"hidden": false
}
}
}

View File

@ -23,6 +23,12 @@
"version": "16.5.0-beta.0",
"description": "Move .storybook/tsconfig.json to tsconfig.storybook.json for non-Angular projects.",
"factory": "./src/migrations/update-16-5-0/move-storybook-tsconfig"
},
"update-19-6-0-add-nx-packages": {
"cli": "nx",
"version": "19.6.0-beta.0",
"description": "Update workspace to use Storybook v8",
"implementation": "./src/migrations/update-19-6-0/update-sb-8"
}
},
"packageJsonUpdates": {
@ -861,6 +867,35 @@
"alwaysAddToPackageJson": false
}
}
},
"19.6.0": {
"version": "19.6.0-beta.1",
"packages": {
"@storybook/core-server": {
"version": "^8.2.8",
"alwaysAddToPackageJson": false
},
"@storybook/angular": {
"version": "^8.2.8",
"alwaysAddToPackageJson": false
},
"@storybook/react": {
"version": "^8.2.8",
"alwaysAddToPackageJson": false
},
"@storybook/addon-essentials": {
"version": "^8.2.8",
"alwaysAddToPackageJson": false
},
"@storybook/addon-interactions": {
"version": "^8.2.8",
"alwaysAddToPackageJson": true
},
"storybook": {
"version": "^8.2.8",
"alwaysAddToPackageJson": true
}
}
}
}
}

View File

@ -1,5 +1,4 @@
import { ExecutorContext, logger } from '@nx/devkit';
import * as build from '@storybook/core-server';
import { CLIOptions } from '@storybook/types';
import {
pleaseUpgrade,
@ -12,10 +11,15 @@ export default async function buildStorybookExecutor(
context: ExecutorContext
) {
storybookConfigExistsCheck(options.configDir, context.projectName);
const storybook7 = storybookMajorVersion() >= 7;
if (!storybook7) {
const storybookMajor = storybookMajorVersion();
if (storybookMajor > 0 && storybookMajor <= 6) {
throw pleaseUpgrade();
} else if (storybookMajor === 7) {
logger.warn(
`Support for Storybook 7 is deprecated. Please upgrade to Storybook 8. See https://nx.dev/nx-api/storybook/generators/migrate-8 for more details.`
);
}
const buildOptions: CLIOptions = options;
logger.info(`NX Storybook builder starting ...`);
await runInstance(buildOptions);
@ -24,14 +28,15 @@ export default async function buildStorybookExecutor(
return { success: true };
}
function runInstance(options: CLIOptions): Promise<void | {
async function runInstance(options: CLIOptions): Promise<void | {
port: number;
address: string;
networkAddress: string;
}> {
const storybookCore = await import('@storybook/core-server');
const env = process.env.NODE_ENV ?? 'production';
process.env.NODE_ENV = env;
return build.build({
return storybookCore.build({
...options,
mode: 'static',
});

View File

@ -1,5 +1,4 @@
import { ExecutorContext } from '@nx/devkit';
import * as build from '@storybook/core-server';
import { ExecutorContext, logger } from '@nx/devkit';
import {
pleaseUpgrade,
storybookConfigExistsCheck,
@ -14,12 +13,15 @@ export default async function* storybookExecutor(
success: boolean;
info?: { port: number; baseUrl?: string };
}> {
const sbVersion = storybookMajorVersion();
const sbLessThan7 = sbVersion < 7 && sbVersion > 0;
if (sbLessThan7) {
const storybookMajor = storybookMajorVersion();
if (storybookMajor > 0 && storybookMajor <= 6) {
throw pleaseUpgrade();
} else if (storybookMajor === 7) {
logger.warn(
`Support for Storybook 7 is deprecated. Please upgrade to Storybook 8. See https://nx.dev/nx-api/storybook/generators/migrate-8 for more details.`
);
}
storybookConfigExistsCheck(options.configDir, context.projectName);
const buildOptions: CLIOptions = options;
const result = await runInstance(buildOptions);
@ -35,14 +37,15 @@ export default async function* storybookExecutor(
await new Promise<{ success: boolean }>(() => {});
}
function runInstance(options: CLIOptions): Promise<void | {
async function runInstance(options: CLIOptions): Promise<void | {
port: number;
address: string;
networkAddress: string;
}> {
const storybookCore = await import('@storybook/core-server');
const env = process.env.NODE_ENV ?? 'development';
process.env.NODE_ENV = env;
return build.build({
return storybookCore.build({
...options,
mode: 'dev',
});

View File

@ -5,9 +5,10 @@ exports[`@nx/storybook:configuration for workspaces with Root project basic func
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { mergeConfig } from 'vite';
import react from '@vitejs/plugin-react';
const config: StorybookConfig = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
@ -16,7 +17,7 @@ const config: StorybookConfig = {
viteFinal: async (config) =>
mergeConfig(config, {
plugins: [nxViteTsPaths()],
plugins: [react(), nxViteTsPaths()],
}),
};
@ -69,7 +70,7 @@ exports[`@nx/storybook:configuration for workspaces with Root project basic func
"import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',

View File

@ -4,7 +4,7 @@ exports[`@nx/storybook:configuration for Storybook v7 basic functionalities shou
"import type { StorybookConfig } from '@storybook/angular';
const config: StorybookConfig = {
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/angular',
@ -24,7 +24,7 @@ exports[`@nx/storybook:configuration for Storybook v7 basic functionalities shou
"import type { StorybookConfig } from '@storybook/angular';
const config: StorybookConfig = {
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/angular',
@ -77,7 +77,7 @@ exports[`@nx/storybook:configuration for Storybook v7 basic functionalities shou
exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configuration for all types of projects should contain the correct configuration in "apps/main-vite/.storybook/" 1`] = `
"const config = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
@ -103,7 +103,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
@ -129,7 +129,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
@ -155,7 +155,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/nextjs';
const config: StorybookConfig = {
stories: ['../components/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../components/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/nextjs',
@ -177,7 +177,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
@ -201,7 +201,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configuration for all types of projects should contain the correct configuration in "apps/reapp/.storybook/" 1`] = `
"const config = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',
@ -227,7 +227,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
@ -253,7 +253,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/web-components-vite';
const config: StorybookConfig = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/web-components-vite',
@ -279,7 +279,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/web-components-webpack5';
const config: StorybookConfig = {
stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/web-components-webpack5',
@ -301,7 +301,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
@ -327,7 +327,7 @@ exports[`@nx/storybook:configuration for Storybook v7 generate Storybook configu
"import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/react-vite',

View File

@ -61,8 +61,13 @@ export async function configurationGeneratorInternal(
tree: Tree,
rawSchema: StorybookConfigureSchema
) {
if (storybookMajorVersion() === 6) {
const storybookMajor = storybookMajorVersion();
if (storybookMajor > 0 && storybookMajor === 6) {
throw new Error(pleaseUpgrade());
} else if (storybookMajor === 7) {
logger.warn(
`Support for Storybook 7 is deprecated. Please upgrade to Storybook 8. See https://nx.dev/nx-api/storybook/generators/migrate-8 for more details.`
);
}
const schema = normalizeSchema(tree, rawSchema);

View File

@ -2,13 +2,13 @@ import type { StorybookConfig } from '<%= uiFramework %>';
<% if (usesVite && !viteConfigFilePath) { %>
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { mergeConfig } from 'vite';
<% } %>
<% } %><% if (uiFramework === '@storybook/vue3-vite' && !viteConfigFilePath) { %>import vue from '@vitejs/plugin-vue'<% } %><% if (uiFramework === '@storybook/react-vite' && !viteConfigFilePath) { %>import react from '@vitejs/plugin-react'<% } %>
const config: StorybookConfig = {
stories: [
<% if(uiFramework === '@storybook/angular' && projectType === 'library') { %>
'../**/*.stories.@(js|jsx|ts|tsx|mdx)' <% } else { %>
'../<%= projectDirectory %>/**/*.stories.@(js|jsx|ts|tsx|mdx)'
'../**/*.@(mdx|stories.@(js|jsx|ts|tsx))' <% } else { %>
'../<%= projectDirectory %>/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'
<% } %>],
addons: ['@storybook/addon-essentials' <% if(interactionTests) { %>, '@storybook/addon-interactions' <% } %><% if(uiFramework === '@storybook/react-webpack5') { %>, '@nx/react/plugins/storybook' <% } %>],
framework: {
@ -43,7 +43,7 @@ const config: StorybookConfig = {
},<% } %><% if (usesVite && !viteConfigFilePath) { %>
viteFinal: async (config) =>
mergeConfig(config, {
plugins: [nxViteTsPaths()],
plugins: [<% if(uiFramework === '@storybook/vue3-vite') { %>vue(), <% } %><% if(uiFramework === '@storybook/react-vite') { %>react(), <% } %>nxViteTsPaths()],
}),
<% } %>
};

View File

@ -1,13 +1,13 @@
<% if (usesVite && !viteConfigFilePath) { %>
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { mergeConfig } from 'vite';
<% } %>
<% } %><% if (uiFramework === '@storybook/vue3-vite' && !viteConfigFilePath) { %>import vue from '@vitejs/plugin-vue'<% } %>
const config = {
stories: [
<% if(uiFramework === '@storybook/angular' && projectType === 'library') { %>
'../**/*.stories.@(js|jsx|ts|tsx|mdx)' <% } else { %>
'../<%= projectDirectory %>/**/*.stories.@(js|jsx|ts|tsx|mdx)'
'../**/*.@(mdx|stories.@(js|jsx|ts|tsx))' <% } else { %>
'../<%= projectDirectory %>/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'
<% } %>],
addons: ['@storybook/addon-essentials' <% if(interactionTests) { %>, '@storybook/addon-interactions' <% } %><% if(uiFramework === '@storybook/react-webpack5') { %>, '@nx/react/plugins/storybook' <% } %>],
framework: {
@ -43,7 +43,7 @@ const config = {
},<% } %><% if (usesVite && !viteConfigFilePath) { %>
viteFinal: async (config) =>
mergeConfig(config, {
plugins: [nxViteTsPaths()],
plugins: [<% if(uiFramework === '@storybook/vue3-vite') { %>vue(), <% } %>nxViteTsPaths()],
}),
<% } %>
};

View File

@ -31,16 +31,16 @@ function checkDependenciesInstalled(
};
if (schema.addPlugin) {
let storybook7VersionToInstall = storybookVersion;
let storybookVersionToInstall = storybookVersion;
if (
storybookMajorVersion() >= 7 &&
getInstalledStorybookVersion() &&
gte(getInstalledStorybookVersion(), '7.0.0')
) {
storybook7VersionToInstall = getInstalledStorybookVersion();
storybookVersionToInstall = getInstalledStorybookVersion();
}
devDependencies['storybook'] = storybook7VersionToInstall;
devDependencies['storybook'] = storybookVersionToInstall;
}
return addDependenciesToPackageJson(

View File

@ -0,0 +1,100 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Helper functions for the Storybook 8 migration generator getAllStorybookInfo and onlyShowGuide should return all info for all projects with Storybook 1`] = `
{
"nextapp": {
"configDir": "apps/nextapp/.storybook",
},
"nextapp-ts": {
"configDir": "apps/nextapp-ts/.storybook",
},
"ngapp": {
"configDir": "apps/ngapp/.storybook",
},
"ngapp-ts": {
"configDir": "apps/ngapp-ts/.storybook",
},
"plugin-app": {
"configDir": "plugin-apps/plugin/.storybook",
},
"rv1": {
"configDir": "apps/rv1/.storybook",
},
"rv2-ts": {
"configDir": "apps/rv2-ts/.storybook",
},
"rw1": {
"configDir": "apps/rw1/.storybook",
},
"wv1": {
"configDir": "apps/wv1/.storybook",
},
"ww1": {
"configDir": "apps/ww1/.storybook",
},
}
`;
exports[`Helper functions for the Storybook 8 migration generator logResult should create the summary file with the correct content 1`] = `
"# Storybook 8 Migration Summary
## Upgrade Storybook packages
The following command was ran to upgrade the Storybook packages:
\`\`\`bash
npx storybook@latest upgrade
\`\`\`
## The Storybook automigration scripts were ran
The following commands ran successfully and your Storybook configuration was successfully migrated to the latest version 7:
- \`npx storybook@latest automigrate --config-dir apps/nextapp/.storybook\`
- \`npx storybook@latest automigrate --config-dir apps/rv1/.storybook\`
Please make sure to check the results yourself and make sure that everything is working as expected.
### Some migrations failed
The following commands failed and your Storybook configuration for these projects was not
migrated to the latest version 7:
- \`npx storybook@latest automigrate --config-dir apps/rv2-ts/.storybook\`
- \`npx storybook@latest automigrate --config-dir apps/rw1/.storybook\`
You can run these commands again, manually, and follow the instructions in the
output of these commands to migrate your Storybook configuration to the latest version 8.
Also, we may have missed something. Please make sure to check the logs of the Storybook CLI commands that were run, and look for
the \`❌ Failed trying to evaluate\` message or \`❌ The migration failed to update\` message. This will indicate if a command was
unsuccessful, and will help you run the migration again, manually.
## Next steps
You can make sure everything is working as expected by trying
to build or serve your Storybook as you normally would.
\`\`\`bash
npx nx build-storybook project-name
\`\`\`
\`\`\`bash
npx nx storybook project-name
\`\`\`
Please read the [Storybook 8.0.0 release article](https://storybook.js.org/blog/storybook-8/) and the
official [Storybook 8.0.0 migration guide](https://storybook.js.org/docs/react/migration-guide)
for more information.
"
`;

View File

@ -0,0 +1,106 @@
import { getPackageManagerCommand, output } from '@nx/devkit';
import { execSync } from 'child_process';
import { Schema } from './schema';
export function callUpgrade(schema: Schema): 1 | Buffer {
const pm = getPackageManagerCommand();
try {
output.log({
title: `Calling sb upgrade`,
bodyLines: [
` Nx will call the Storybook CLI to upgrade your @storybook/* packages to the latest version.`,
`📖 You can read more about the Storybook upgrade command here: https://storybook.js.org/docs/react/configure/upgrading`,
],
color: 'blue',
});
execSync(
`${pm.dlx} storybook@latest upgrade ${
schema.autoAcceptAllPrompts ? '--yes' : ''
}`,
{
stdio: [0, 1, 2],
}
);
output.log({
title: `Storybook packages upgraded.`,
bodyLines: [
`☑️ The upgrade command was successful.`,
`Your Storybook packages are now at the latest version.`,
],
color: 'green',
});
} catch (e) {
output.log({
title: 'Migration failed',
bodyLines: [
`🚨 The Storybook CLI failed to upgrade your @storybook/* packages to the latest version.`,
`Please try running the sb upgrade command manually:`,
`${pm.exec} storybook@latest upgrade`,
],
color: 'red',
});
console.log(e);
return 1;
}
}
export function callAutomigrate(
allStorybookProjects: {
[key: string]: {
configDir: string;
};
},
schema: Schema
): { successfulProjects: {}; failedProjects: {} } {
output.log({
title: `⚙️ Calling sb automigrate`,
bodyLines: [
` Nx will call the Storybook CLI to automigrate the Storybook configuration of all your projects that use Storybook.`,
`📖 You can read more about the Storybook automigrate command here: https://storybook.js.org/docs/react/configure/upgrading#automigrate-script`,
],
color: 'green',
});
const resultOfMigration = {
successfulProjects: {},
failedProjects: {},
};
Object.entries(allStorybookProjects).forEach(
([projectName, storybookProjectInfo]) => {
const pm = getPackageManagerCommand();
const commandToRun = `${pm.dlx} storybook@latest automigrate --config-dir ${storybookProjectInfo.configDir}`;
try {
output.log({
title: `Calling sb automigrate for ${projectName}`,
bodyLines: ['Command:', commandToRun],
color: 'green',
});
execSync(
`${commandToRun} ${schema.autoAcceptAllPrompts ? '--yes' : ''}`,
{
stdio: 'inherit',
}
);
resultOfMigration.successfulProjects[projectName] = commandToRun;
} catch (e) {
output.error({
title: 'Migration failed',
bodyLines: [
`🚨 The Storybook CLI failed to automigrate the Storybook configuration of your project.`,
`The error was: ${e}`,
`Please try running the sb automigrate command manually:`,
commandToRun,
],
});
resultOfMigration.failedProjects[projectName] = commandToRun;
}
}
);
return resultOfMigration;
}

View File

@ -0,0 +1,56 @@
# Storybook 8 Migration Summary
## Upgrade Storybook packages
The following command was ran to upgrade the Storybook packages:
```bash
npx storybook@latest upgrade
```
## The Storybook automigration scripts were ran
<% if ( hasSuccessfulProjects ) { %>
The following commands ran successfully and your Storybook configuration was successfully migrated to the latest version 7:
<% for (let command of successfulProjects) { %>
- `<%= command %>`
<% } %>
Please make sure to check the results yourself and make sure that everything is working as expected.
<% } %>
<% if ( hasFailedProjects ) { %>
### Some migrations failed
The following commands failed and your Storybook configuration for these projects was not
migrated to the latest version 7:
<% for (let command of failedProjects) { %>
- `<%= command %>`
<% } %>
You can run these commands again, manually, and follow the instructions in the
output of these commands to migrate your Storybook configuration to the latest version 8.
<% } %>
Also, we may have missed something. Please make sure to check the logs of the Storybook CLI commands that were run, and look for
the `❌ Failed trying to evaluate` message or `❌ The migration failed to update` message. This will indicate if a command was
unsuccessful, and will help you run the migration again, manually.
## Next steps
You can make sure everything is working as expected by trying
to build or serve your Storybook as you normally would.
```bash
npx nx build-storybook project-name
```
```bash
npx nx storybook project-name
```
Please read the [Storybook 8.0.0 release article](https://storybook.js.org/blog/storybook-8/) and the
official [Storybook 8.0.0 migration guide](https://storybook.js.org/docs/react/migration-guide)
for more information.

View File

@ -0,0 +1,391 @@
import {
addProjectConfiguration,
getPackageManagerCommand,
output,
ProjectConfiguration,
Tree,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import * as allProjects from './test-configs/all-projects.json';
import {
getAllStorybookInfo,
logResult,
onlyShowGuide,
} from './helper-functions';
describe('Helper functions for the Storybook 8 migration generator', () => {
let tree: Tree;
beforeEach(async () => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
addAllProjectsToWorkspace(tree);
});
describe('getAllStorybookInfo and onlyShowGuide', () => {
let allStorybookInfo;
beforeEach(() => {
allStorybookInfo = getAllStorybookInfo(tree);
});
it('should return all info for all projects with Storybook', () => {
expect(allStorybookInfo).toMatchSnapshot();
});
it('should onlyShowGuide and the correct instructions', () => {
const outputSpy = jest.spyOn(output, 'log').mockImplementation();
onlyShowGuide(allStorybookInfo);
const pm = getPackageManagerCommand();
expect(outputSpy).toHaveBeenCalledWith(
expect.objectContaining({
bodyLines: [
'You can run the following commands manually to upgrade your Storybook projects to Storybook 8:',
'',
'1. Call the Storybook upgrade script:',
`${pm.exec} storybook@latest upgrade`,
'',
'2. Call the Storybook automigrate scripts:',
'Run the following commands for each Storybook project:',
`${pm.exec} storybook@latest automigrate --config-dir apps/nextapp/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir apps/nextapp-ts/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir apps/ngapp/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir apps/ngapp-ts/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir apps/rv1/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir apps/rv2-ts/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir apps/rw1/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir apps/wv1/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir apps/ww1/.storybook`,
`${pm.exec} storybook@latest automigrate --config-dir plugin-apps/plugin/.storybook`,
'',
],
title: 'Storybook 8 Migration Guide',
})
);
});
});
describe('logResult', () => {
it('should create the summary file with the correct content', () => {
logResult(tree, {
successfulProjects: {
nextapp:
'npx storybook@latest automigrate --config-dir apps/nextapp/.storybook',
rv1: 'npx storybook@latest automigrate --config-dir apps/rv1/.storybook',
},
failedProjects: {
'rv2-ts': `npx storybook@latest automigrate --config-dir apps/rv2-ts/.storybook`,
rw1: 'npx storybook@latest automigrate --config-dir apps/rw1/.storybook',
},
});
expect(
tree.read('storybook-migration-summary.md', 'utf-8')
).toMatchSnapshot();
});
});
});
export function addAllProjectsToWorkspace(tree: Tree) {
for (const [name, project] of Object.entries(allProjects)) {
addProjectConfiguration(tree, name, project as ProjectConfiguration);
}
writeMainJs(tree);
writeViteConfig(tree);
addPluginProjects(tree);
}
function writeViteConfig(tree: Tree) {
tree.write(
`apps/rv1/vite.config.js`,
`const { defineConfig } = require('vite');`
);
tree.write(
`apps/rv2-ts/vite.config.ts`,
`const { defineConfig } = require('vite');`
);
tree.write(
`apps/wv1/vite.config.ts`,
`const { defineConfig } = require('vite');`
);
}
function writeMainJs(tree: Tree) {
tree.write(
`apps/rv1/.storybook/main.js`,
`
const { mergeConfig } = require('vite');
const viteTsConfigPaths = require('vite-tsconfig-paths').default;
module.exports = {
core: { builder: '@storybook/builder-vite' },
stories: [
'../src/app/**/*.stories.mdx',
'../src/app/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: ['@storybook/addon-essentials'],
async viteFinal(config, { configType }) {
return mergeConfig(config, {
plugins: [
viteTsConfigPaths({
root: '../../../',
}),
],
});
},
};
`
);
tree.write(
`apps/rv2-ts/.storybook/main.ts`,
`
import type { StorybookConfig } from '@storybook/core-common';
import { mergeConfig } from 'vite';
import viteTsConfigPaths from 'vite-tsconfig-paths';
const config: StorybookConfig = {
core: { builder: '@storybook/builder-vite' },
stories: [
'../src/app/**/*.stories.mdx',
'../src/app/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: ['@storybook/addon-essentials'],
async viteFinal(config: any) {
return mergeConfig(config, {
plugins: [
viteTsConfigPaths({
root: '../../../',
}),
],
});
},
} as StorybookConfig;
module.exports = config;
`
);
tree.write(
`apps/nextapp/.storybook/main.js`,
`
const path = require('path');
module.exports = {
core: { builder: 'webpack5' },
stories: [
'../components/**/*.stories.mdx',
'../components/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@nx/react/plugins/storybook',
'storybook-addon-swc',
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
],
};
`
);
tree.write(
`apps/nextapp-ts/.storybook/main.ts`,
`
import type { StorybookConfig } from '@storybook/core-common';
import path from 'path';
const config: StorybookConfig = {
core: { builder: 'webpack5' },
stories: [
'../components/**/*.stories.mdx',
'../components/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@nx/react/plugins/storybook',
'storybook-addon-swc',
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
],
} as StorybookConfig;
module.exports = config;
`
);
tree.write(
`apps/rw1/.storybook/main.js`,
`
const path = require('path');
module.exports = {
core: { builder: 'webpack5' },
stories: [
'../components/**/*.stories.mdx',
'../components/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@nx/react/plugins/storybook',
'storybook-addon-swc',
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
],
};
`
);
tree.write(
`apps/wv1/.storybook/main.js`,
`
const path = require('path');
module.exports = {
core: { builder: 'webpack5' },
stories: [
'../components/**/*.stories.mdx',
'../components/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@nx/react/plugins/storybook',
'storybook-addon-swc',
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
],
};
`
);
tree.write(
`apps/ww1/.storybook/main.js`,
`
const path = require('path');
module.exports = {
core: { builder: 'webpack5' },
stories: [
'../components/**/*.stories.mdx',
'../components/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@nx/react/plugins/storybook',
'storybook-addon-swc',
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
],
};
`
);
tree.write(
`apps/ngapp/.storybook/main.js`,
`
const path = require('path');
module.exports = {
core: { builder: 'webpack5' },
stories: [
'../components/**/*.stories.mdx',
'../components/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@nx/react/plugins/storybook',
'storybook-addon-swc',
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
],
};
`
);
tree.write(
`apps/ngapp-ts/.storybook/main.ts`,
`
const path = require('path');
module.exports = {
core: { builder: 'webpack5' },
stories: [
'../components/**/*.stories.mdx',
'../components/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@nx/react/plugins/storybook',
'storybook-addon-swc',
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
],
};
`
);
}
function addPluginProjects(tree: Tree) {
tree.write(
`plugin-apps/plugin/.storybook/main.ts`,
`
import type { StorybookConfig } from '@storybook/core-common';
import path from 'path';
const config: StorybookConfig = {
core: { builder: 'webpack5' },
stories: [
'../components/**/*.stories.mdx',
'../components/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@nx/react/plugins/storybook',
'storybook-addon-swc',
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
],
} as StorybookConfig;
module.exports = config;
`
);
tree.write(
`plugin-apps/plugin/project.json`,
JSON.stringify({
name: 'plugin-app',
root: 'plugin-apps/plugin',
sourceRoot: 'plugin-apps/plugin/src',
type: 'application',
targets: {},
})
);
}

View File

@ -0,0 +1,212 @@
import {
generateFiles,
getPackageManagerCommand,
output,
readProjectConfiguration,
Tree,
workspaceRoot,
visitNotIgnoredFiles,
joinPathFragments,
readJson,
} from '@nx/devkit';
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
import { fileExists } from 'nx/src/utils/fileutils';
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
export function onlyShowGuide(storybookProjects: {
[key: string]: {
configDir: string;
};
}) {
const pm = getPackageManagerCommand();
output.log({
title: 'Storybook 8 Migration Guide',
bodyLines: [
`You can run the following commands manually to upgrade your Storybook projects to Storybook 8:`,
``,
`1. Call the Storybook upgrade script:`,
`${pm.exec} storybook@latest upgrade`,
``,
`2. Call the Storybook automigrate scripts:`,
`Run the following commands for each Storybook project:`,
...Object.entries(storybookProjects).map(
([_projectName, storybookProjectInfo]) => {
return `${pm.exec} storybook@latest automigrate --config-dir ${storybookProjectInfo.configDir}`;
}
),
``,
],
});
}
export function getAllStorybookInfo(tree: Tree): {
[key: string]: {
configDir: string;
};
} {
const allStorybookDirs: { [key: string]: { configDir: string } } = {};
visitNotIgnoredFiles(tree, '', (storybookConfigPath) => {
if (
!storybookConfigPath.endsWith('.storybook/main.ts') &&
!storybookConfigPath.endsWith('.storybook/main.js')
) {
return;
}
const storybookConfigDir = dirname(storybookConfigPath);
let projectRoot = '';
if (storybookConfigPath.includes('/.storybook')) {
projectRoot = storybookConfigDir.replace('/.storybook', '');
} else {
projectRoot = storybookConfigDir.replace('.storybook', '');
}
if (projectRoot === '') {
projectRoot = '.';
}
const packageOrProjectJson = [
joinPathFragments(projectRoot, 'package.json'),
joinPathFragments(projectRoot, 'project.json'),
].find((p) => tree.exists(p));
if (!packageOrProjectJson) {
return;
}
const projectName = readJson(tree, packageOrProjectJson)?.name;
if (!projectName) {
return;
}
allStorybookDirs[projectName] = {
configDir: storybookConfigDir,
};
});
return allStorybookDirs;
}
export function handleMigrationResult(
migrateResult: {
successfulProjects: {};
failedProjects: {};
},
allStorybookProjects: {
[key: string]: {
configDir: string;
};
}
): { successfulProjects: {}; failedProjects: {} } {
if (
fileExists(join(workspaceRoot, 'migration-storybook.log')) &&
Object.keys(migrateResult.successfulProjects)?.length
) {
const sbLogFile = readFileSync(
join(workspaceRoot, 'migration-storybook.log'),
'utf-8'
);
Object.keys(migrateResult.successfulProjects).forEach((projectName) => {
if (
sbLogFile.includes(
`The migration failed to update your ${allStorybookProjects[projectName].configDir}`
)
) {
migrateResult.failedProjects[projectName] =
migrateResult.successfulProjects[projectName];
delete migrateResult.successfulProjects[projectName];
}
});
}
if (
Object.keys(allStorybookProjects)?.length ===
Object.keys(migrateResult.successfulProjects)?.length ||
Object.keys(migrateResult.failedProjects)?.length === 0
) {
output.log({
title: `Storybook configuration migrated.`,
bodyLines: [
`☑️ The automigrate command was successful.`,
`All your projects were migrated successfully.`,
],
color: 'green',
});
} else {
if (Object.keys(migrateResult.failedProjects).length) {
if (Object.keys(migrateResult.failedProjects).length) {
output.log({
title: `Storybook configuration migrated.`,
bodyLines: [
`☑️ The automigrate command was successful.`,
`The following projects were migrated successfully:`,
...Object.keys(migrateResult.successfulProjects).map(
(project) => ` - ${project}`
),
],
color: 'green',
});
}
output.log({
title: `Failed migrations.`,
bodyLines: [
`There were some projects that were not migrated successfully.`,
`⚠️ The following projects were not migrated successfully:`,
...Object.keys(migrateResult.failedProjects).map(
(project) => ` - ${project}`
),
`You can run the following commands to migrate them manually:`,
...Object.entries(migrateResult.failedProjects).map(
([_project, command]) => `- ${command}`
),
],
color: 'red',
});
}
}
return migrateResult;
}
export function checkStorybookInstalled(packageJson): boolean {
return (
(packageJson.dependencies['@storybook/core-server'] ||
packageJson.devDependencies['@storybook/core-server']) &&
(packageJson.dependencies['@nx/storybook'] ||
packageJson.devDependencies['@nx/storybook'] ||
packageJson.dependencies['@nrwl/storybook'] ||
packageJson.devDependencies['@nrwl/storybook'])
);
}
export function logResult(
tree: Tree,
migrationSummary: {
successfulProjects: { [key: string]: string };
failedProjects: { [key: string]: string };
}
) {
output.log({
title: `Migration complete!`,
bodyLines: [
`🎉 Your Storybook configuration has been migrated to Storybook ^8.0.0!`,
`📖 You can see a summary of the tasks that were performed in the storybook-migration-summary.md file in the root of your workspace.`,
],
color: 'green',
});
generateFiles(tree, join(__dirname, 'files'), '.', {
tmpl: '',
successfulProjects: Object.entries(
migrationSummary?.successfulProjects
)?.map(([_projectName, command]) => command),
failedProjects: Object.entries(migrationSummary?.failedProjects)?.map(
([_projectName, command]) => command
),
hasFailedProjects:
Object.keys(migrationSummary?.failedProjects)?.length > 0,
hasSuccessfulProjects:
Object.keys(migrationSummary?.successfulProjects)?.length > 0,
});
}

View File

@ -0,0 +1,55 @@
import { formatFiles, readJson, Tree } from '@nx/devkit';
import { output } from 'nx/src/utils/output';
import { callAutomigrate, callUpgrade } from './calling-storybook-cli';
import {
checkStorybookInstalled,
getAllStorybookInfo,
handleMigrationResult,
logResult,
onlyShowGuide,
} from './helper-functions';
import { Schema } from './schema';
export async function migrate8Generator(tree: Tree, schema: Schema) {
const packageJson = readJson(tree, 'package.json');
if (!checkStorybookInstalled(packageJson)) {
output.error({
title: 'No Storybook packages installed',
bodyLines: [
`🚨 Nx did not find any Storybook packages installed in your workspace.`,
`So no migration is necessary.`,
],
});
return;
}
const allStorybookProjects: {
[key: string]: {
configDir: string;
};
} = getAllStorybookInfo(tree);
if (schema.onlyShowListOfCommands) {
onlyShowGuide(allStorybookProjects);
return;
}
if (!schema.noUpgrade) {
callUpgrade(schema);
}
if (Object.entries(allStorybookProjects).length) {
let migrateResult: {
successfulProjects: {};
failedProjects: {};
};
migrateResult = callAutomigrate(allStorybookProjects, schema);
migrateResult = handleMigrationResult(migrateResult, allStorybookProjects);
logResult(tree, migrateResult);
}
await formatFiles(tree);
}
export default migrate8Generator;

View File

@ -0,0 +1,5 @@
export interface Schema {
autoAcceptAllPrompts?: boolean;
onlyShowListOfCommands?: boolean;
noUpgrade?: boolean;
}

View File

@ -0,0 +1,25 @@
{
"cli": "nx",
"title": "Migrate Storybook to version 8.",
"description": "Migrate Storybook to version 8.",
"$id": "migrate-8",
"type": "object",
"properties": {
"autoAcceptAllPrompts": {
"type": "boolean",
"description": "Say yes to all the prompts from the Storybook CLI migration scripts.",
"default": false
},
"onlyShowListOfCommands": {
"type": "boolean",
"description": "Only show the steps that you need to follow in order to migrate. This does NOT make any changes to your code.",
"default": false
},
"noUpgrade": {
"type": "boolean",
"description": "Skip upgrading Storybook packages. Only use this option if you are already on version 8, and you do not want the latest beta.",
"default": false
}
},
"examplesFile": "../../../docs/migrate-8-generator-examples.md"
}

View File

@ -0,0 +1,756 @@
{
"nextapp": {
"name": "nextapp",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/nextapp",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/next:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"root": "apps/nextapp",
"outputPath": "dist/apps/nextapp"
},
"configurations": {
"development": { "outputPath": "apps/nextapp" },
"production": {}
}
},
"serve": {
"executor": "@nx/next:server",
"defaultConfiguration": "development",
"options": { "buildTarget": "nextapp:build", "dev": true },
"configurations": {
"development": {
"buildTarget": "nextapp:build:development",
"dev": true
},
"production": {
"buildTarget": "nextapp:build:production",
"dev": false
}
}
},
"export": {
"executor": "@nx/next:export",
"options": { "buildTarget": "nextapp:build:production" }
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/nextapp/jest.config.ts",
"passWithNoTests": true
},
"configurations": { "ci": { "ci": true, "codeCoverage": true } }
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": { "lintFilePatterns": ["apps/nextapp/**/*.{ts,tsx,js,jsx}"] }
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {
"uiFramework": "@storybook/react",
"port": 4400,
"configDir": "apps/nextapp/.storybook"
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@nx/storybook:build",
"outputs": ["{options.outputDir}"],
"options": {
"uiFramework": "@storybook/react",
"outputDir": "dist/storybook/nextapp",
"configDir": "apps/nextapp/.storybook"
},
"configurations": { "ci": { "quiet": true } }
}
},
"tags": [],
"root": "apps/nextapp"
},
"nextapp-ts": {
"name": "nextapp-ts",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/nextapp-ts",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/next:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"root": "apps/nextapp-ts",
"outputPath": "dist/apps/nextapp-ts"
},
"configurations": {
"development": { "outputPath": "apps/nextapp-ts" },
"production": {}
}
},
"serve": {
"executor": "@nx/next:server",
"defaultConfiguration": "development",
"options": { "buildTarget": "nextapp-ts:build", "dev": true },
"configurations": {
"development": {
"buildTarget": "nextapp-ts:build:development",
"dev": true
},
"production": {
"buildTarget": "nextapp-ts:build:production",
"dev": false
}
}
},
"export": {
"executor": "@nx/next:export",
"options": { "buildTarget": "nextapp-ts:build:production" }
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/nextapp-ts/jest.config.ts",
"passWithNoTests": true
},
"configurations": { "ci": { "ci": true, "codeCoverage": true } }
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/nextapp-ts/**/*.{ts,tsx,js,jsx}"]
}
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {
"uiFramework": "@storybook/react",
"port": 4400,
"configDir": "apps/nextapp-ts/.storybook"
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@nx/storybook:build",
"outputs": ["{options.outputDir}"],
"options": {
"uiFramework": "@storybook/react",
"outputDir": "dist/storybook/nextapp-ts",
"configDir": "apps/nextapp-ts/.storybook"
},
"configurations": { "ci": { "quiet": true } }
}
},
"tags": [],
"root": "apps/nextapp-ts"
},
"ngapp": {
"name": "ngapp",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "apps/ngapp/src",
"prefix": "sbmigrat",
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:browser",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/ngapp",
"index": "apps/ngapp/src/index.html",
"main": "apps/ngapp/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/ngapp/tsconfig.app.json",
"assets": ["apps/ngapp/src/favicon.ico", "apps/ngapp/src/assets"],
"styles": ["apps/ngapp/src/styles.css"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": { "browserTarget": "ngapp:build:production" },
"development": { "browserTarget": "ngapp:build:development" }
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": { "browserTarget": "ngapp:build" }
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/ngapp/**/*.ts", "apps/ngapp/**/*.html"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/ngapp/jest.config.ts",
"passWithNoTests": true
},
"configurations": { "ci": { "ci": true, "codeCoverage": true } }
},
"storybook": {
"executor": "@storybook/angular:start-storybook",
"options": {
"port": 4400,
"configDir": "apps/ngapp/.storybook",
"browserTarget": "ngapp:build",
"compodoc": false
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@storybook/angular:build-storybook",
"outputs": ["{options.outputDir}"],
"options": {
"outputDir": "dist/storybook/ngapp",
"configDir": "apps/ngapp/.storybook",
"browserTarget": "ngapp:build",
"compodoc": false
},
"configurations": { "ci": { "quiet": true } }
}
},
"tags": [],
"root": "apps/ngapp"
},
"ngapp-ts": {
"name": "ngapp-ts",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "apps/ngapp-ts/src",
"prefix": "sbmigrat",
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:browser",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/ngapp-ts",
"index": "apps/ngapp-ts/src/index.html",
"main": "apps/ngapp-ts/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/ngapp-ts/tsconfig.app.json",
"assets": [
"apps/ngapp-ts/src/favicon.ico",
"apps/ngapp-ts/src/assets"
],
"styles": ["apps/ngapp-ts/src/styles.css"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": { "browserTarget": "ngapp-ts:build:production" },
"development": { "browserTarget": "ngapp-ts:build:development" }
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": { "browserTarget": "ngapp-ts:build" }
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"apps/ngapp-ts/**/*.ts",
"apps/ngapp-ts/**/*.html"
]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/ngapp-ts/jest.config.ts",
"passWithNoTests": true
},
"configurations": { "ci": { "ci": true, "codeCoverage": true } }
},
"storybook": {
"executor": "@storybook/angular:start-storybook",
"options": {
"port": 4400,
"configDir": "apps/ngapp-ts/.storybook",
"browserTarget": "ngapp-ts:build",
"compodoc": false
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@storybook/angular:build-storybook",
"outputs": ["{options.outputDir}"],
"options": {
"outputDir": "dist/storybook/ngapp-ts",
"configDir": "apps/ngapp-ts/.storybook",
"browserTarget": "ngapp-ts:build",
"compodoc": false
},
"configurations": { "ci": { "quiet": true } }
}
},
"tags": [],
"root": "apps/ngapp-ts"
},
"rv1": {
"name": "rv1",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/rv1/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": { "outputPath": "dist/apps/rv1" },
"configurations": {
"development": { "mode": "development" },
"production": { "mode": "production" }
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"defaultConfiguration": "development",
"options": { "buildTarget": "rv1:build" },
"configurations": {
"development": {
"buildTarget": "rv1:build:development",
"hmr": true
},
"production": { "buildTarget": "rv1:build:production", "hmr": false }
}
},
"preview": {
"executor": "@nx/vite:preview-server",
"defaultConfiguration": "development",
"options": { "buildTarget": "rv1:build" },
"configurations": {
"development": { "buildTarget": "rv1:build:development" },
"production": { "buildTarget": "rv1:build:production" }
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["coverage/apps/rv1"],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../../coverage/apps/rv1"
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": { "lintFilePatterns": ["apps/rv1/**/*.{ts,tsx,js,jsx}"] }
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {
"uiFramework": "@storybook/react",
"port": 4400,
"configDir": "apps/rv1/.storybook"
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@nx/storybook:build",
"outputs": ["{options.outputDir}"],
"options": {
"uiFramework": "@storybook/react",
"outputDir": "dist/storybook/rv1",
"configDir": "apps/rv1/.storybook"
},
"configurations": { "ci": { "quiet": true } }
}
},
"tags": [],
"root": "apps/rv1"
},
"rv2-ts": {
"name": "rv2-ts",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/rv2-ts/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": { "outputPath": "dist/apps/rv2-ts" },
"configurations": {
"development": { "mode": "development" },
"production": { "mode": "production" }
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"defaultConfiguration": "development",
"options": { "buildTarget": "rv2-ts:build" },
"configurations": {
"development": {
"buildTarget": "rv2-ts:build:development",
"hmr": true
},
"production": {
"buildTarget": "rv2-ts:build:production",
"hmr": false
}
}
},
"preview": {
"executor": "@nx/vite:preview-server",
"defaultConfiguration": "development",
"options": { "buildTarget": "rv2-ts:build" },
"configurations": {
"development": { "buildTarget": "rv2-ts:build:development" },
"production": { "buildTarget": "rv2-ts:build:production" }
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["coverage/apps/rv2-ts"],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../../coverage/apps/rv2-ts"
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": { "lintFilePatterns": ["apps/rv2-ts/**/*.{ts,tsx,js,jsx}"] }
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {
"uiFramework": "@storybook/react",
"port": 4400,
"configDir": "apps/rv2-ts/.storybook"
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@nx/storybook:build",
"outputs": ["{options.outputDir}"],
"options": {
"uiFramework": "@storybook/react",
"outputDir": "dist/storybook/rv2-ts",
"configDir": "apps/rv2-ts/.storybook"
},
"configurations": { "ci": { "quiet": true } }
}
},
"tags": [],
"root": "apps/rv2-ts"
},
"rw1": {
"name": "rw1",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/rw1/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"compiler": "babel",
"outputPath": "dist/apps/rw1",
"index": "apps/rw1/src/index.html",
"baseHref": "/",
"main": "apps/rw1/src/main.tsx",
"tsConfig": "apps/rw1/tsconfig.app.json",
"assets": ["apps/rw1/src/favicon.ico", "apps/rw1/src/assets"],
"styles": ["apps/rw1/src/styles.css"],
"scripts": [],
"isolatedConfig": true,
"webpackConfig": "apps/rw1/webpack.config.js"
},
"configurations": {
"development": {
"extractLicenses": false,
"optimization": false,
"sourceMap": true,
"vendorChunk": true
},
"production": {
"fileReplacements": [
{
"replace": "apps/rw1/src/environments/environment.ts",
"with": "apps/rw1/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false
}
}
},
"serve": {
"executor": "@nx/webpack:dev-server",
"defaultConfiguration": "development",
"options": { "buildTarget": "rw1:build", "hmr": true },
"configurations": {
"development": { "buildTarget": "rw1:build:development" },
"production": { "buildTarget": "rw1:build:production", "hmr": false }
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": { "lintFilePatterns": ["apps/rw1/**/*.{ts,tsx,js,jsx}"] }
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/rw1/jest.config.ts",
"passWithNoTests": true
},
"configurations": { "ci": { "ci": true, "codeCoverage": true } }
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {
"uiFramework": "@storybook/react",
"port": 4400,
"configDir": "apps/rw1/.storybook"
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@nx/storybook:build",
"outputs": ["{options.outputDir}"],
"options": {
"uiFramework": "@storybook/react",
"outputDir": "dist/storybook/rw1",
"configDir": "apps/rw1/.storybook"
},
"configurations": { "ci": { "quiet": true } }
}
},
"tags": [],
"root": "apps/rw1"
},
"wv1": {
"name": "wv1",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "apps/wv1/src",
"tags": [],
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": { "outputPath": "dist/apps/wv1" },
"configurations": {
"development": { "mode": "development" },
"production": { "mode": "production" }
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"defaultConfiguration": "development",
"options": { "buildTarget": "wv1:build" },
"configurations": {
"development": {
"buildTarget": "wv1:build:development",
"hmr": true
},
"production": { "buildTarget": "wv1:build:production", "hmr": false }
}
},
"preview": {
"executor": "@nx/vite:preview-server",
"defaultConfiguration": "development",
"options": { "buildTarget": "wv1:build" },
"configurations": {
"development": { "buildTarget": "wv1:build:development" },
"production": { "buildTarget": "wv1:build:production" }
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["coverage/apps/wv1"],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../../coverage/apps/wv1"
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": { "lintFilePatterns": ["apps/wv1/**/*.ts"] }
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {
"uiFramework": "@storybook/web-components",
"port": 4400,
"configDir": "apps/wv1/.storybook"
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@nx/storybook:build",
"outputs": ["{options.outputDir}"],
"options": {
"uiFramework": "@storybook/web-components",
"outputDir": "dist/storybook/wv1",
"configDir": "apps/wv1/.storybook"
},
"configurations": { "ci": { "quiet": true } }
}
},
"root": "apps/wv1"
},
"ww1": {
"name": "ww1",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "apps/ww1/src",
"tags": [],
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"outputPath": "dist/apps/ww1",
"compiler": "babel",
"main": "apps/ww1/src/main.ts",
"tsConfig": "apps/ww1/tsconfig.app.json",
"webpackConfig": "apps/ww1/webpack.config.js",
"assets": ["apps/ww1/src/favicon.ico", "apps/ww1/src/assets"],
"index": "apps/ww1/src/index.html",
"baseHref": "/",
"styles": ["apps/ww1/src/styles.css"],
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"fileReplacements": [
{
"replace": "apps/ww1/src/environments/environment.ts",
"with": "apps/ww1/src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"executor": "@nx/webpack:dev-server",
"options": { "buildTarget": "ww1:build" },
"configurations": {
"production": { "buildTarget": "ww1:build:production" }
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["coverage/apps/ww1"],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../../coverage/apps/ww1"
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": { "lintFilePatterns": ["apps/ww1/**/*.ts"] }
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {
"uiFramework": "@storybook/web-components",
"port": 4400,
"configDir": "apps/ww1/.storybook"
},
"configurations": { "ci": { "quiet": true } }
},
"build-storybook": {
"executor": "@nx/storybook:build",
"outputs": ["{options.outputDir}"],
"options": {
"uiFramework": "@storybook/web-components",
"outputDir": "dist/storybook/ww1",
"configDir": "apps/ww1/.storybook"
},
"configurations": { "ci": { "quiet": true } }
}
},
"root": "apps/ww1"
}
}

View File

@ -0,0 +1,30 @@
import { Tree } from '@nx/devkit';
import { output } from 'nx/src/utils/output';
import migrate8Generator from '../../generators/migrate-8/migrate-8';
import { storybookMajorVersion } from '../../utils/utilities';
export default async function changeStorybookTargets(tree: Tree) {
const storybookVersion = storybookMajorVersion();
if (!storybookVersion) {
/**
* This just checks if Storybook is installed in the workspace.
* The thing here is that during the previous step of the migration,
* during packageJsonUpdates, Nx has already set Storybook
* to version 8, if Storybook exists in the workspace.
* So, it makes no sense here to check if the version is
* 7, because it will always be.
*/
return;
}
output.log({
title: 'Migrating Storybook to v8',
bodyLines: [
`🚀 This migration will update your Storybook configuration to v8.`,
`It will call the @nx/storybook:migrate-8 generator for you.`,
`You can read more about the migration and how this generator works here:`,
`https://nx.dev/nx-api/storybook/generators/migrate-8`,
],
});
return migrate8Generator(tree, { autoAcceptAllPrompts: true });
}

View File

@ -6,8 +6,8 @@ export const litVersion = '^2.6.1';
export const tsNodeVersion = '10.9.1';
export const tsLibVersion = '^2.3.0';
export const storybookVersion = '^7.5.3';
export const storybookVersion = '^8.2.8';
export const reactVersion = '^18.2.0';
export const viteVersion = '~5.0.0';
export const viteVersion = '^5.0.0';
export const coreJsVersion = '^3.36.1';

View File

@ -19,11 +19,13 @@
},
"generateCypressSpecs": {
"type": "boolean",
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator."
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"cypressProject": {
"type": "string",
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default."
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
},
"interactionTests": {
"type": "boolean",

View File

@ -5,9 +5,10 @@ exports[`vue:storybook-configuration should configure everything and install cor
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { mergeConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
stories: ['../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/vue3-vite',
@ -16,7 +17,7 @@ const config: StorybookConfig = {
viteFinal: async (config) =>
mergeConfig(config, {
plugins: [nxViteTsPaths()],
plugins: [vue(), nxViteTsPaths()],
}),
};

3154
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff