feat(storybook): add configureStaticServe option (#15688)

This commit is contained in:
Caleb Ukle 2023-03-22 14:38:32 -07:00 committed by GitHub
parent b124b97c36
commit b3dd65adbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 199 additions and 47 deletions

View File

@ -39,6 +39,13 @@
"default": true, "default": true,
"x-priority": "important" "x-priority": "important"
}, },
"configureStaticServe": {
"type": "boolean",
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
"x-prompt": "Configure a static file server for the storybook instance?",
"default": true,
"x-priority": "important"
},
"cypressDirectory": { "cypressDirectory": {
"type": "string", "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."

View File

@ -39,6 +39,13 @@
"default": true, "default": true,
"x-priority": "important" "x-priority": "important"
}, },
"configureStaticServe": {
"type": "boolean",
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
"x-prompt": "Configure a static file server for the storybook instance?",
"default": true,
"x-priority": "important"
},
"cypressDirectory": { "cypressDirectory": {
"type": "string", "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."

View File

@ -67,6 +67,11 @@
"default": true, "default": true,
"x-deprecated": "Nx only supports standaloneConfig" "x-deprecated": "Nx only supports standaloneConfig"
}, },
"configureStaticServe": {
"type": "boolean",
"description": "Add a static-storybook to serve the static storybook built files.",
"default": false
},
"configureTestRunner": { "configureTestRunner": {
"type": "boolean", "type": "boolean",
"description": "Add a Storybook Test-Runner target." "description": "Add a Storybook Test-Runner target."

View File

@ -38,6 +38,11 @@
"type": "boolean", "type": "boolean",
"default": true, "default": true,
"x-deprecated": "Nx only supports standaloneConfig" "x-deprecated": "Nx only supports standaloneConfig"
},
"ciTargetName": {
"type": "string",
"description": "The name of the devServerTarget to use for the Cypress CI configuration. Used to control if using <storybook-project>:static-storybook:ci or <storybook-project>:storybook:ci",
"x-priority": "internal"
} }
}, },
"required": ["name"], "required": ["name"],

View File

@ -19,5 +19,6 @@ export async function generateStorybookConfiguration(
tsConfiguration: options.tsConfiguration, tsConfiguration: options.tsConfiguration,
configureTestRunner: options.configureTestRunner, configureTestRunner: options.configureTestRunner,
storybook7Configuration: options.storybook7Configuration, storybook7Configuration: options.storybook7Configuration,
configureStaticServe: options.configureStaticServe,
}); });
} }

View File

@ -2,6 +2,7 @@ import type { Linter } from '@nrwl/linter';
export interface StorybookConfigurationOptions { export interface StorybookConfigurationOptions {
configureCypress: boolean; configureCypress: boolean;
configureStaticServe?: boolean;
generateCypressSpecs: boolean; generateCypressSpecs: boolean;
generateStories: boolean; generateStories: boolean;
linter: Linter; linter: Linter;

View File

@ -39,6 +39,13 @@
"default": true, "default": true,
"x-priority": "important" "x-priority": "important"
}, },
"configureStaticServe": {
"type": "boolean",
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
"x-prompt": "Configure a static file server for the storybook instance?",
"default": true,
"x-priority": "important"
},
"cypressDirectory": { "cypressDirectory": {
"type": "string", "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."

View File

@ -65,6 +65,7 @@ export async function storybookConfigurationGenerator(
standaloneConfig: schema.standaloneConfig, standaloneConfig: schema.standaloneConfig,
tsConfiguration: schema.tsConfiguration, tsConfiguration: schema.tsConfiguration,
configureTestRunner: schema.configureTestRunner, configureTestRunner: schema.configureTestRunner,
configureStaticServe: schema.configureStaticServe,
bundler, bundler,
storybook7Configuration: schema.storybook7Configuration, storybook7Configuration: schema.storybook7Configuration,
storybook7UiFramework: storybook7UiFramework:

View File

@ -13,5 +13,6 @@ export interface StorybookConfigureSchema {
ignorePaths?: string[]; ignorePaths?: string[];
bundler?: 'webpack' | 'vite'; bundler?: 'webpack' | 'vite';
configureTestRunner?: boolean; configureTestRunner?: boolean;
configureStaticServe?: boolean;
storybook7Configuration?: boolean; storybook7Configuration?: boolean;
} }

View File

@ -39,6 +39,13 @@
"default": true, "default": true,
"x-priority": "important" "x-priority": "important"
}, },
"configureStaticServe": {
"type": "boolean",
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
"x-prompt": "Configure a static file server for the storybook instance?",
"default": true,
"x-priority": "important"
},
"cypressDirectory": { "cypressDirectory": {
"type": "string", "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."

View File

@ -11,6 +11,7 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { Linter } from '@nrwl/linter'; import { Linter } from '@nrwl/linter';
import { libraryGenerator } from '@nrwl/workspace/generators'; import { libraryGenerator } from '@nrwl/workspace/generators';
import { nxVersion } from '../../utils/versions';
import { TsConfig } from '../../utils/utilities'; import { TsConfig } from '../../utils/utilities';
import configurationGenerator from './configuration'; import configurationGenerator from './configuration';
import * as workspaceConfiguration from './test-configs/workspace-conifiguration.json'; import * as workspaceConfiguration from './test-configs/workspace-conifiguration.json';
@ -42,6 +43,7 @@ describe('@nrwl/storybook:configuration', () => {
devDependencies: { devDependencies: {
'@storybook/addon-essentials': '~6.2.9', '@storybook/addon-essentials': '~6.2.9',
'@storybook/react': '~6.2.9', '@storybook/react': '~6.2.9',
'@nrwl/web': nxVersion,
}, },
}); });
}); });
@ -328,6 +330,82 @@ describe('@nrwl/storybook:configuration', () => {
}, },
}); });
}); });
it('should add static-storybook target', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
uiFramework: '@storybook/react',
configureStaticServe: true,
});
expect(
readProjectConfiguration(tree, 'test-ui-lib').targets[
'static-storybook'
]
).toMatchInlineSnapshot(`
Object {
"configurations": Object {
"ci": Object {
"buildTarget": "test-ui-lib:build-storybook:ci",
},
},
"executor": "@nrwl/web:file-server",
"options": Object {
"buildTarget": "test-ui-lib:build-storybook",
"staticFilePath": "dist/storybook/test-ui-lib",
},
}
`);
expect(
readJson(tree, 'package.json').devDependencies['@nrwl/web']
).toBeTruthy();
});
it('should use static-storybook:ci in cypress project', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
uiFramework: '@storybook/react',
configureStaticServe: true,
configureCypress: true,
});
expect(
readProjectConfiguration(tree, 'test-ui-lib').targets[
'static-storybook'
]
).toMatchInlineSnapshot(`
Object {
"configurations": Object {
"ci": Object {
"buildTarget": "test-ui-lib:build-storybook:ci",
},
},
"executor": "@nrwl/web:file-server",
"options": Object {
"buildTarget": "test-ui-lib:build-storybook",
"staticFilePath": "dist/storybook/test-ui-lib",
},
}
`);
expect(readProjectConfiguration(tree, 'test-ui-lib-e2e').targets.e2e)
.toMatchInlineSnapshot(`
Object {
"configurations": Object {
"ci": Object {
"devServerTarget": "test-ui-lib:static-storybook:ci",
},
},
"executor": "@nrwl/cypress:cypress",
"options": Object {
"cypressConfig": "apps/test-ui-lib-e2e/cypress.config.ts",
"devServerTarget": "test-ui-lib:storybook",
"testingType": "e2e",
},
}
`);
expect(
readJson(tree, 'package.json').devDependencies['@nrwl/web']
).toBeTruthy();
});
}); });
describe('for other types of projects - Next.js and the swc compiler', () => { describe('for other types of projects - Next.js and the swc compiler', () => {

View File

@ -16,6 +16,7 @@ import { initGenerator } from '../init/init';
import { import {
addAngularStorybookTask, addAngularStorybookTask,
addBuildStorybookToCacheableOperations, addBuildStorybookToCacheableOperations,
addStaticTarget,
addStorybookTask, addStorybookTask,
addStorybookToNamedInputs, addStorybookToNamedInputs,
configureTsProjectConfig, configureTsProjectConfig,
@ -32,6 +33,7 @@ import {
isStorybookV7, isStorybookV7,
} from '../../utils/utilities'; } from '../../utils/utilities';
import { import {
nxVersion,
storybookNextAddonVersion, storybookNextAddonVersion,
storybookSwcAddonVersion, storybookSwcAddonVersion,
storybookTestRunnerVersion, storybookTestRunnerVersion,
@ -180,6 +182,10 @@ export async function configurationGenerator(
); );
} }
if (schema.configureStaticServe) {
addStaticTarget(tree, schema);
}
const e2eProject = await getE2EProjectName(tree, schema.name); const e2eProject = await getE2EProjectName(tree, schema.name);
if (schema.configureCypress && !e2eProject) { if (schema.configureCypress && !e2eProject) {
const cypressTask = await cypressProjectGenerator(tree, { const cypressTask = await cypressProjectGenerator(tree, {
@ -188,6 +194,9 @@ export async function configurationGenerator(
linter: schema.linter, linter: schema.linter,
directory: schema.cypressDirectory, directory: schema.cypressDirectory,
standaloneConfig: schema.standaloneConfig, standaloneConfig: schema.standaloneConfig,
ciTargetName: schema.configureStaticServe
? 'static-storybook'
: undefined,
}); });
tasks.push(cypressTask); tasks.push(cypressTask);
} else { } else {
@ -196,57 +205,31 @@ export async function configurationGenerator(
); );
} }
if (schema.tsConfiguration) { const devDeps = {};
tasks.push(
addDependenciesToPackageJson(
tree,
{},
{
['@storybook/core-common']: storybookVersion,
['ts-node']: tsNodeVersion,
}
)
);
}
if (schema.tsConfiguration) {
devDeps['@storybook/core-common'] = storybookVersion;
devDeps['ts-node'] = tsNodeVersion;
}
if ( if (
nextBuildTarget && nextBuildTarget &&
projectType === 'application' && projectType === 'application' &&
!schema.storybook7Configuration !schema.storybook7Configuration
) { ) {
tasks.push( devDeps['storybook-addon-next'] = storybookNextAddonVersion;
addDependenciesToPackageJson( devDeps['storybook-addon-swc'] = storybookSwcAddonVersion;
tree,
{},
{
['storybook-addon-next']: storybookNextAddonVersion,
['storybook-addon-swc']: storybookSwcAddonVersion,
}
)
);
} else if (compiler === 'swc') { } else if (compiler === 'swc') {
tasks.push( devDeps['storybook-addon-swc'] = storybookSwcAddonVersion;
addDependenciesToPackageJson(
tree,
{},
{
['storybook-addon-swc']: storybookSwcAddonVersion,
}
)
);
} }
if (schema.configureTestRunner === true) { if (schema.configureTestRunner === true) {
tasks.push( devDeps['@storybook/test-runner'] = storybookTestRunnerVersion;
addDependenciesToPackageJson(
tree,
{},
{
'@storybook/test-runner': storybookTestRunnerVersion,
}
)
);
} }
if (schema.configureStaticServe) {
devDeps['@nrwl/web'] = nxVersion;
}
tasks.push(addDependenciesToPackageJson(tree, {}, devDeps));
await formatFiles(tree); await formatFiles(tree);

View File

@ -12,6 +12,7 @@ export interface StorybookConfigureSchema {
cypressDirectory?: string; cypressDirectory?: string;
standaloneConfig?: boolean; standaloneConfig?: boolean;
configureTestRunner?: boolean; configureTestRunner?: boolean;
configureStaticServe?: boolean;
storybook7Configuration?: boolean; // TODO(katerina): Change when Storybook 7 storybook7Configuration?: boolean; // TODO(katerina): Change when Storybook 7
storybook7UiFramework?: UiFramework7; // TODO(katerina): Change when Storybook 7 storybook7UiFramework?: UiFramework7; // TODO(katerina): Change when Storybook 7
} }

View File

@ -67,6 +67,11 @@
"default": true, "default": true,
"x-deprecated": "Nx only supports standaloneConfig" "x-deprecated": "Nx only supports standaloneConfig"
}, },
"configureStaticServe": {
"type": "boolean",
"description": "Add a static-storybook to serve the static storybook built files.",
"default": false
},
"configureTestRunner": { "configureTestRunner": {
"type": "boolean", "type": "boolean",
"description": "Add a Storybook Test-Runner target." "description": "Add a Storybook Test-Runner target."

View File

@ -1,5 +1,6 @@
import { import {
createProjectGraphAsync, createProjectGraphAsync,
ensurePackage,
generateFiles, generateFiles,
joinPathFragments, joinPathFragments,
logger, logger,
@ -26,6 +27,7 @@ import {
} from '../../utils/utilities'; } from '../../utils/utilities';
import { StorybookConfigureSchema } from './schema'; import { StorybookConfigureSchema } from './schema';
import { UiFramework, UiFramework7 } from '../../utils/models'; import { UiFramework, UiFramework7 } from '../../utils/models';
import { nxVersion } from '../../utils/versions';
const DEFAULT_PORT = 4400; const DEFAULT_PORT = 4400;
@ -140,6 +142,28 @@ export function addAngularStorybookTask(
updateProjectConfiguration(tree, projectName, projectConfig); updateProjectConfiguration(tree, projectName, projectConfig);
} }
export function addStaticTarget(tree: Tree, opts: StorybookConfigureSchema) {
const nrwlWeb = ensurePackage<typeof import('@nrwl/web')>(
'@nrwl/web',
nxVersion
);
nrwlWeb.webStaticServeGenerator(tree, {
buildTarget: `${opts.name}:build-storybook`,
outputPath: joinPathFragments('dist/storybook', opts.name),
targetName: 'static-storybook',
});
const projectConfig = readProjectConfiguration(tree, opts.name);
projectConfig.targets['static-storybook'].configurations = {
ci: {
buildTarget: `${opts.name}:build-storybook:ci`,
},
};
updateProjectConfiguration(tree, opts.name, projectConfig);
}
export function configureTsProjectConfig( export function configureTsProjectConfig(
tree: Tree, tree: Tree,
schema: StorybookConfigureSchema schema: StorybookConfigureSchema

View File

@ -30,6 +30,7 @@ export interface CypressConfigureSchema {
directory?: string; directory?: string;
linter: Linter; linter: Linter;
standaloneConfig?: boolean; standaloneConfig?: boolean;
ciTargetName?: string;
} }
export async function cypressProjectGenerator( export async function cypressProjectGenerator(
@ -65,7 +66,11 @@ export async function cypressProjectGenerator(
); );
removeUnneededFiles(tree, generatedCypressProjectName, schema.js); removeUnneededFiles(tree, generatedCypressProjectName, schema.js);
addBaseUrlToCypressConfig(tree, generatedCypressProjectName); addBaseUrlToCypressConfig(tree, generatedCypressProjectName);
updateAngularJsonBuilder(tree, generatedCypressProjectName, schema.name); updateAngularJsonBuilder(tree, {
e2eProjectName: generatedCypressProjectName,
targetProjectName: schema.name,
ciTargetName: schema.ciTargetName,
});
await formatFiles(tree); await formatFiles(tree);
@ -107,24 +112,29 @@ function addBaseUrlToCypressConfig(tree: Tree, projectName: string) {
function updateAngularJsonBuilder( function updateAngularJsonBuilder(
tree: Tree, tree: Tree,
e2eProjectName: string, opts: {
targetProjectName: string e2eProjectName: string;
targetProjectName: string;
ciTargetName?: string;
}
) { ) {
const project = readProjectConfiguration(tree, e2eProjectName); const project = readProjectConfiguration(tree, opts.e2eProjectName);
const e2eTarget = project.targets.e2e; const e2eTarget = project.targets.e2e;
project.targets.e2e = { project.targets.e2e = {
...e2eTarget, ...e2eTarget,
options: <any>{ options: <any>{
...e2eTarget.options, ...e2eTarget.options,
devServerTarget: `${targetProjectName}:storybook`, devServerTarget: `${opts.targetProjectName}:storybook`,
}, },
configurations: { configurations: {
ci: { ci: {
devServerTarget: `${targetProjectName}:storybook:ci`, devServerTarget: opts.ciTargetName
? `${opts.targetProjectName}:${opts.ciTargetName}:ci`
: `${opts.targetProjectName}:storybook:ci`,
}, },
}, },
}; };
updateProjectConfiguration(tree, e2eProjectName, project); updateProjectConfiguration(tree, opts.e2eProjectName, project);
} }
function projectAlreadyHasCypress(tree: Tree): boolean { function projectAlreadyHasCypress(tree: Tree): boolean {

View File

@ -38,6 +38,11 @@
"type": "boolean", "type": "boolean",
"default": true, "default": true,
"x-deprecated": "Nx only supports standaloneConfig" "x-deprecated": "Nx only supports standaloneConfig"
},
"ciTargetName": {
"type": "string",
"description": "The name of the devServerTarget to use for the Cypress CI configuration. Used to control if using <storybook-project>:static-storybook:ci or <storybook-project>:storybook:ci",
"x-priority": "internal"
} }
}, },
"required": ["name"] "required": ["name"]

View File

@ -74,6 +74,7 @@ describe('migrate-stories-to-6-2 schematic', () => {
await runAngularStorybookSchematic(appTree, { await runAngularStorybookSchematic(appTree, {
name: 'test-ui-lib', name: 'test-ui-lib',
configureCypress: true, configureCypress: true,
configureStaticServe: false,
}); });
appTree.write( appTree.write(

View File

@ -67,6 +67,7 @@ describe('testing utilities', () => {
await runAngularStorybookSchematic(appTree, { await runAngularStorybookSchematic(appTree, {
name: 'test-ui-lib', name: 'test-ui-lib',
configureCypress: true, configureCypress: true,
configureStaticServe: false,
}); });
appTree.write( appTree.write(

View File

@ -127,6 +127,8 @@ const IGNORE_MATCHES_IN_PACKAGE = {
'@storybook/core', '@storybook/core',
'@storybook/core-server', '@storybook/core-server',
'@storybook/types', '@storybook/types',
// lazy installed with ensurePackage
'@nrwl/web',
'rxjs', 'rxjs',
], ],
nx: [ nx: [