Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(storybook): add projectBuildConfig as an option in generator #9655

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/generated/packages/angular.json
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions docs/generated/packages/react.json
Expand Up @@ -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 `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean"
Expand Down
4 changes: 4 additions & 0 deletions docs/generated/packages/storybook.json
Expand Up @@ -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.",
Expand Down
Expand Up @@ -7,4 +7,5 @@ export interface StorybookConfigurationOptions {
linter: Exclude<Linter, Linter.TsLint>;
name: string;
cypressDirectory?: string;
projectBuildConfig?: string;
}
Expand Up @@ -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,
Expand Down
Expand Up @@ -9,4 +9,5 @@ export interface StorybookConfigureSchema {
linter?: Linter;
cypressDirectory?: string;
standaloneConfig?: boolean;
projectBuildConfig?: string;
}
Expand Up @@ -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 `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean"
Expand Down
36 changes: 36 additions & 0 deletions packages/storybook/src/executors/utils.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
}): {
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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',
Expand Down
Expand Up @@ -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(
Expand Down Expand Up @@ -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, {
Expand Down Expand Up @@ -326,7 +335,8 @@ function addStorybookTask(
tree: Tree,
projectName: string,
uiFramework: string,
buildTargetForAngularProjects: string
buildTargetForAngularProjects: string,
customProjectBuildConfig?: string
) {
if (uiFramework === '@storybook/react-native') {
return;
Expand All @@ -342,7 +352,10 @@ function addStorybookTask(
},
projectBuildConfig:
uiFramework === '@storybook/angular'
? buildTargetForAngularProjects
? customProjectBuildConfig &&
customProjectBuildConfigIsValid(tree, customProjectBuildConfig)
? customProjectBuildConfig
: buildTargetForAngularProjects
? projectName
: `${projectName}:build-storybook`
: undefined,
Expand All @@ -364,7 +377,10 @@ function addStorybookTask(
},
projectBuildConfig:
uiFramework === '@storybook/angular'
? buildTargetForAngularProjects
? customProjectBuildConfig &&
customProjectBuildConfigIsValid(tree, customProjectBuildConfig)
? customProjectBuildConfig
: buildTargetForAngularProjects
? projectName
: `${projectName}:build-storybook`
: undefined,
Expand Down
Expand Up @@ -11,4 +11,5 @@ export interface StorybookConfigureSchema {
js?: boolean;
cypressDirectory?: string;
standaloneConfig?: boolean;
projectBuildConfig?: string;
}
4 changes: 4 additions & 0 deletions packages/storybook/src/generators/configuration/schema.json
Expand Up @@ -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.",
Expand Down