From 9579aec307620f4b4caccc934443c152842a2851 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 5 Apr 2022 19:16:25 +0300 Subject: [PATCH] feat(storybook): add projectBuildConfig as an option in generator (#9655) Co-authored-by: Colum Ferry --- docs/generated/packages/angular.json | 4 + docs/generated/packages/react.json | 4 + docs/generated/packages/storybook.json | 4 + .../storybook-configuration/schema.d.ts | 1 + .../storybook-configuration/schema.json | 4 + .../storybook-configuration/schema.d.ts | 1 + .../storybook-configuration/schema.json | 4 + packages/storybook/src/executors/utils.ts | 36 ++++++ .../configuration/configuration.spec.ts | 116 +++++++++++++++++- .../generators/configuration/configuration.ts | 26 +++- .../src/generators/configuration/schema.d.ts | 1 + .../src/generators/configuration/schema.json | 4 + 12 files changed, 199 insertions(+), 6 deletions(-) diff --git a/docs/generated/packages/angular.json b/docs/generated/packages/angular.json index 9b4a59b31e027..53066b39b158c 100644 --- a/docs/generated/packages/angular.json +++ b/docs/generated/packages/angular.json @@ -1638,6 +1638,10 @@ "type": "string", "enum": ["eslint", "none"], "default": "eslint" + }, + "projectBuildConfig": { + "description": "Provide a custom projectBuildConfig for the Angular executor. If left blank, Nx will use the default.", + "type": "string" } }, "additionalProperties": false, diff --git a/docs/generated/packages/react.json b/docs/generated/packages/react.json index 1ef50ca916951..8ef0eef2c4269 100644 --- a/docs/generated/packages/react.json +++ b/docs/generated/packages/react.json @@ -620,6 +620,10 @@ "enum": ["eslint", "tslint"], "default": "eslint" }, + "projectBuildConfig": { + "description": "Provide a custom projectBuildConfig for the Angular executor. If left blank, Nx will use the default.", + "type": "string" + }, "standaloneConfig": { "description": "Split the project configuration into `/project.json` rather than including it inside `workspace.json`.", "type": "boolean" diff --git a/docs/generated/packages/storybook.json b/docs/generated/packages/storybook.json index 83512a68f6509..65b4b2bf23920 100644 --- a/docs/generated/packages/storybook.json +++ b/docs/generated/packages/storybook.json @@ -76,6 +76,10 @@ "enum": ["eslint", "tslint", "none"], "default": "eslint" }, + "projectBuildConfig": { + "description": "Provide a custom projectBuildConfig for the Angular executor. If left blank, Nx will use the default.", + "type": "string" + }, "js": { "type": "boolean", "description": "Generate JavaScript files rather than TypeScript files.", diff --git a/packages/angular/src/generators/storybook-configuration/schema.d.ts b/packages/angular/src/generators/storybook-configuration/schema.d.ts index b05f4685ad100..cc30ec1c4579b 100644 --- a/packages/angular/src/generators/storybook-configuration/schema.d.ts +++ b/packages/angular/src/generators/storybook-configuration/schema.d.ts @@ -7,4 +7,5 @@ export interface StorybookConfigurationOptions { linter: Exclude; name: string; cypressDirectory?: string; + projectBuildConfig?: string; } diff --git a/packages/angular/src/generators/storybook-configuration/schema.json b/packages/angular/src/generators/storybook-configuration/schema.json index 87b38e01a9ef1..d963a917e21dd 100644 --- a/packages/angular/src/generators/storybook-configuration/schema.json +++ b/packages/angular/src/generators/storybook-configuration/schema.json @@ -41,6 +41,10 @@ "type": "string", "enum": ["eslint", "none"], "default": "eslint" + }, + "projectBuildConfig": { + "description": "Provide a custom projectBuildConfig for the Angular executor. If left blank, Nx will use the default.", + "type": "string" } }, "additionalProperties": false, diff --git a/packages/react/src/generators/storybook-configuration/schema.d.ts b/packages/react/src/generators/storybook-configuration/schema.d.ts index 3de38336e0a77..3ad4734793ad9 100644 --- a/packages/react/src/generators/storybook-configuration/schema.d.ts +++ b/packages/react/src/generators/storybook-configuration/schema.d.ts @@ -9,4 +9,5 @@ export interface StorybookConfigureSchema { linter?: Linter; cypressDirectory?: string; standaloneConfig?: boolean; + projectBuildConfig?: string; } diff --git a/packages/react/src/generators/storybook-configuration/schema.json b/packages/react/src/generators/storybook-configuration/schema.json index f0f0029647f62..8e07e51f5d3a7 100644 --- a/packages/react/src/generators/storybook-configuration/schema.json +++ b/packages/react/src/generators/storybook-configuration/schema.json @@ -47,6 +47,10 @@ "enum": ["eslint", "tslint"], "default": "eslint" }, + "projectBuildConfig": { + "description": "Provide a custom projectBuildConfig for the Angular executor. If left blank, Nx will use the default.", + "type": "string" + }, "standaloneConfig": { "description": "Split the project configuration into `/project.json` rather than including it inside `workspace.json`.", "type": "boolean" diff --git a/packages/storybook/src/executors/utils.ts b/packages/storybook/src/executors/utils.ts index e7eaa6fbc0593..5e2aa5d067cf7 100644 --- a/packages/storybook/src/executors/utils.ts +++ b/packages/storybook/src/executors/utils.ts @@ -3,8 +3,10 @@ import { joinPathFragments, logger, parseTargetString, + readProjectConfiguration, readTargetOptions, TargetConfiguration, + Tree, } from '@nrwl/devkit'; import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils'; import 'dotenv/config'; @@ -302,6 +304,40 @@ export function isStorybookLT6() { ); } +export function customProjectBuildConfigIsValid( + tree: Tree, + projectBuildConfig: string +): boolean { + if (projectBuildConfig.includes(':')) { + const { project, target } = parseTargetString(projectBuildConfig); + const projectConfig = readProjectConfiguration(tree, project); + if (projectConfig?.targets?.[target]) { + return true; + } else { + logger.warn(` + The projectBuildConfig you provided is not valid. + ${!projectConfig ? 'The project ' + project + ' does not exist.' : ''} + ${ + projectConfig && + !projectConfig.targets?.[target] && + `The project ${project} does not have the target ${target}.` + } + The default projectBuildConfig is going to be used. + `); + } + } else { + try { + return Boolean(readProjectConfiguration(tree, projectBuildConfig)); + } catch (e) { + logger.warn(` + The projectBuildConfig you provided is not valid. + The project ${projectBuildConfig} does not exist. + The default projectBuildConfig is going to be used. + `); + } + } +} + export function findStorybookAndBuildTargets(targets: { [targetName: string]: TargetConfiguration; }): { diff --git a/packages/storybook/src/generators/configuration/configuration.spec.ts b/packages/storybook/src/generators/configuration/configuration.spec.ts index 0774285258494..466c572cc1e7c 100644 --- a/packages/storybook/src/generators/configuration/configuration.spec.ts +++ b/packages/storybook/src/generators/configuration/configuration.spec.ts @@ -9,9 +9,20 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { Linter } from '@nrwl/linter'; import { libraryGenerator } from '@nrwl/workspace/generators'; - +import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter'; import { TsConfig } from '../../utils/utilities'; import configurationGenerator from './configuration'; +import { nxVersion, storybookVersion } from '../../utils/versions'; + +const runAngularLibrarySchematic = wrapAngularDevkitSchematic( + '@schematics/angular', + 'library' +); + +const runAngularApplicationSchematic = wrapAngularDevkitSchematic( + '@schematics/angular', + 'application' +); describe('@nrwl/storybook:configuration', () => { let tree: Tree; @@ -265,6 +276,109 @@ describe('@nrwl/storybook:configuration', () => { }); }); + it('should update workspace file for angular libs with custom projectBuildConfig', async () => { + const newTree = createTreeWithEmptyWorkspace(); + + await runAngularLibrarySchematic(newTree, { + name: 'ui-lib', + }); + + await runAngularApplicationSchematic(newTree, { + name: 'test-app', + }); + + writeJson(newTree, 'package.json', { + devDependencies: { + '@nrwl/storybook': nxVersion, + '@storybook/addon-essentials': storybookVersion, + '@storybook/angular': storybookVersion, + }, + }); + + writeJson(newTree, 'ui-lib/tsconfig.json', {}); + writeJson(newTree, 'test-app/tsconfig.json', {}); + + await configurationGenerator(newTree, { + name: 'ui-lib', + uiFramework: '@storybook/angular', + standaloneConfig: false, + projectBuildConfig: 'test-app', + }); + + const project = readProjectConfiguration(newTree, 'ui-lib'); + + expect(project.targets.storybook?.options?.projectBuildConfig).toBe( + 'test-app' + ); + }); + + it('should update workspace file for angular libs with default projectBuildConfig if the one provided is invalid', async () => { + const newTree = createTreeWithEmptyWorkspace(); + + await runAngularLibrarySchematic(newTree, { + name: 'ui-lib', + }); + + await runAngularApplicationSchematic(newTree, { + name: 'test-app', + }); + + writeJson(newTree, 'package.json', { + devDependencies: { + '@nrwl/storybook': nxVersion, + '@storybook/addon-essentials': storybookVersion, + '@storybook/angular': storybookVersion, + }, + }); + + writeJson(newTree, 'ui-lib/tsconfig.json', {}); + writeJson(newTree, 'test-app/tsconfig.json', {}); + + await configurationGenerator(newTree, { + name: 'ui-lib', + uiFramework: '@storybook/angular', + standaloneConfig: false, + projectBuildConfig: 'test-app:asdfasdf', + }); + + const project = readProjectConfiguration(newTree, 'ui-lib'); + + expect(project.targets.storybook?.options?.projectBuildConfig).toBe( + 'ui-lib:build-storybook' + ); + }); + + it('should update workspace file for angular libs with default projectBuildConfig if the project provided does not exist', async () => { + const newTree = createTreeWithEmptyWorkspace(); + + await runAngularLibrarySchematic(newTree, { + name: 'ui-lib', + }); + + writeJson(newTree, 'package.json', { + devDependencies: { + '@nrwl/storybook': nxVersion, + '@storybook/addon-essentials': storybookVersion, + '@storybook/angular': storybookVersion, + }, + }); + + writeJson(newTree, 'ui-lib/tsconfig.json', {}); + + await configurationGenerator(newTree, { + name: 'ui-lib', + uiFramework: '@storybook/angular', + standaloneConfig: false, + projectBuildConfig: 'test-app', + }); + + const project = readProjectConfiguration(newTree, 'ui-lib'); + + expect(project.targets.storybook?.options?.projectBuildConfig).toBe( + 'ui-lib:build-storybook' + ); + }); + it('should update `tsconfig.lib.json` file', async () => { await configurationGenerator(tree, { name: 'test-ui-lib', diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index 4d28b0847447b..bab9bbef0d387 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -30,7 +30,10 @@ import { StorybookConfigureSchema } from './schema'; import { initGenerator } from '../init/init'; import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils'; import { gte } from 'semver'; -import { findStorybookAndBuildTargets } from '../../executors/utils'; +import { + customProjectBuildConfigIsValid, + findStorybookAndBuildTargets, +} from '../../executors/utils'; import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export async function configurationGenerator( @@ -63,7 +66,13 @@ export async function configurationGenerator( configureTsProjectConfig(tree, schema); configureTsSolutionConfig(tree, schema); updateLintConfig(tree, schema); - addStorybookTask(tree, schema.name, schema.uiFramework, buildTarget); + addStorybookTask( + tree, + schema.name, + schema.uiFramework, + buildTarget, + schema.projectBuildConfig + ); if (schema.configureCypress) { if (projectType !== 'application') { const cypressTask = await cypressProjectGenerator(tree, { @@ -326,7 +335,8 @@ function addStorybookTask( tree: Tree, projectName: string, uiFramework: string, - buildTargetForAngularProjects: string + buildTargetForAngularProjects: string, + customProjectBuildConfig?: string ) { if (uiFramework === '@storybook/react-native') { return; @@ -342,7 +352,10 @@ function addStorybookTask( }, projectBuildConfig: uiFramework === '@storybook/angular' - ? buildTargetForAngularProjects + ? customProjectBuildConfig && + customProjectBuildConfigIsValid(tree, customProjectBuildConfig) + ? customProjectBuildConfig + : buildTargetForAngularProjects ? projectName : `${projectName}:build-storybook` : undefined, @@ -364,7 +377,10 @@ function addStorybookTask( }, projectBuildConfig: uiFramework === '@storybook/angular' - ? buildTargetForAngularProjects + ? customProjectBuildConfig && + customProjectBuildConfigIsValid(tree, customProjectBuildConfig) + ? customProjectBuildConfig + : buildTargetForAngularProjects ? projectName : `${projectName}:build-storybook` : undefined, diff --git a/packages/storybook/src/generators/configuration/schema.d.ts b/packages/storybook/src/generators/configuration/schema.d.ts index c5696f46cbd6c..905a35c29c8ce 100644 --- a/packages/storybook/src/generators/configuration/schema.d.ts +++ b/packages/storybook/src/generators/configuration/schema.d.ts @@ -11,4 +11,5 @@ export interface StorybookConfigureSchema { js?: boolean; cypressDirectory?: string; standaloneConfig?: boolean; + projectBuildConfig?: string; } diff --git a/packages/storybook/src/generators/configuration/schema.json b/packages/storybook/src/generators/configuration/schema.json index 59ec341fa86b6..10aee5c5aa425 100644 --- a/packages/storybook/src/generators/configuration/schema.json +++ b/packages/storybook/src/generators/configuration/schema.json @@ -35,6 +35,10 @@ "enum": ["eslint", "tslint", "none"], "default": "eslint" }, + "projectBuildConfig": { + "description": "Provide a custom projectBuildConfig for the Angular executor. If left blank, Nx will use the default.", + "type": "string" + }, "js": { "type": "boolean", "description": "Generate JavaScript files rather than TypeScript files.",