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,
"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": {
"type": "string",
"description": "A directory where the Cypress project will be placed. Placed at the root by default."

View File

@ -39,6 +39,13 @@
"default": true,
"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": {
"type": "string",
"description": "A directory where the Cypress project will be placed. Placed at the root by default."

View File

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

View File

@ -38,6 +38,11 @@
"type": "boolean",
"default": true,
"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"],

View File

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

View File

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

View File

@ -39,6 +39,13 @@
"default": true,
"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": {
"type": "string",
"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,
tsConfiguration: schema.tsConfiguration,
configureTestRunner: schema.configureTestRunner,
configureStaticServe: schema.configureStaticServe,
bundler,
storybook7Configuration: schema.storybook7Configuration,
storybook7UiFramework:

View File

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

View File

@ -39,6 +39,13 @@
"default": true,
"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": {
"type": "string",
"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 { libraryGenerator } from '@nrwl/workspace/generators';
import { nxVersion } from '../../utils/versions';
import { TsConfig } from '../../utils/utilities';
import configurationGenerator from './configuration';
import * as workspaceConfiguration from './test-configs/workspace-conifiguration.json';
@ -42,6 +43,7 @@ describe('@nrwl/storybook:configuration', () => {
devDependencies: {
'@storybook/addon-essentials': '~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', () => {

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
import {
createProjectGraphAsync,
ensurePackage,
generateFiles,
joinPathFragments,
logger,
@ -26,6 +27,7 @@ import {
} from '../../utils/utilities';
import { StorybookConfigureSchema } from './schema';
import { UiFramework, UiFramework7 } from '../../utils/models';
import { nxVersion } from '../../utils/versions';
const DEFAULT_PORT = 4400;
@ -140,6 +142,28 @@ export function addAngularStorybookTask(
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(
tree: Tree,
schema: StorybookConfigureSchema

View File

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

View File

@ -38,6 +38,11 @@
"type": "boolean",
"default": true,
"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"]

View File

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

View File

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

View File

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