From ec2ebca40a4f5687d57d7c2d5fd8aebf49e69302 Mon Sep 17 00:00:00 2001 From: Juri Strumpflohner Date: Mon, 29 Mar 2021 13:24:27 +0200 Subject: [PATCH] fix(storybook): linting react storybook files with eslint (#5146) ISSUES CLOSED: #3867 --- e2e/storybook/src/storybook.test.ts | 14 +++ packages/storybook/migrations.json | 5 + .../.storybook/tsconfig.json__tmpl__ | 2 +- .../update-11-5-3/update-lint-ignores.spec.ts | 58 ++++++++++++ .../update-11-5-3/update-lint-ignores.ts | 94 +++++++++++++++++++ 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 packages/storybook/src/migrations/update-11-5-3/update-lint-ignores.spec.ts create mode 100644 packages/storybook/src/migrations/update-11-5-3/update-lint-ignores.ts diff --git a/e2e/storybook/src/storybook.test.ts b/e2e/storybook/src/storybook.test.ts index e340f6ad57..867e29c85f 100644 --- a/e2e/storybook/src/storybook.test.ts +++ b/e2e/storybook/src/storybook.test.ts @@ -27,6 +27,20 @@ describe('Storybook schematics', () => { ).toContain(`Storybook`); }, 1000000); + it('should lint a React based storybook without errors', () => { + newProject(); + + const reactStorybookLib = uniq('test-ui-lib-react'); + runCLI(`generate @nrwl/react:lib ${reactStorybookLib} --no-interactive`); + runCLI( + `generate @nrwl/react:storybook-configuration ${reactStorybookLib} --generateStories --no-interactive` + ); + + // build React lib + const output = runCLI(`run ${reactStorybookLib}:lint`); + expect(output).toContain('All files pass linting.'); + }, 1000000); + it('should build a React based storybook that references another lib', () => { const proj = newProject(); diff --git a/packages/storybook/migrations.json b/packages/storybook/migrations.json index e748ca7f35..63855f2c42 100644 --- a/packages/storybook/migrations.json +++ b/packages/storybook/migrations.json @@ -25,6 +25,11 @@ "version": "11.0.12", "description": "Update storybook if installed and above 6", "factory": "./src/migrations/update-11-0-12/update-storybook" + }, + "update-11-5-3": { + "version": "11.5.3-beta.1", + "description": "Update react storybook lint config", + "factory": "./src/migrations/update-11-5-3/update-lint-ignores" } }, "packageJsonUpdates": { diff --git a/packages/storybook/src/generators/configuration/project-files/.storybook/tsconfig.json__tmpl__ b/packages/storybook/src/generators/configuration/project-files/.storybook/tsconfig.json__tmpl__ index c83e9f7c5a..c5ad8d071a 100644 --- a/packages/storybook/src/generators/configuration/project-files/.storybook/tsconfig.json__tmpl__ +++ b/packages/storybook/src/generators/configuration/project-files/.storybook/tsconfig.json__tmpl__ @@ -4,5 +4,5 @@ "emitDecoratorMetadata": true }, "exclude": ["../**/*.spec.ts" <% if(uiFramework === '@storybook/react') { %>, "../**/*.spec.js", "../**/*.spec.tsx", "../**/*.spec.jsx"<% } %>], - "include": ["../src/**/*"] + "include": ["../src/**/*", "*.js"] } diff --git a/packages/storybook/src/migrations/update-11-5-3/update-lint-ignores.spec.ts b/packages/storybook/src/migrations/update-11-5-3/update-lint-ignores.spec.ts new file mode 100644 index 0000000000..7526e7a8da --- /dev/null +++ b/packages/storybook/src/migrations/update-11-5-3/update-lint-ignores.spec.ts @@ -0,0 +1,58 @@ +import { Tree } from '@angular-devkit/schematics'; +import { getFileContent } from '@nrwl/workspace/testing'; +import { runMigration } from '../../utils/testing'; + +describe('Update 11-5-3', () => { + let tree: Tree; + + beforeEach(async () => { + tree = Tree.empty(); + + // create workspace + tree.create( + 'workspace.json', + JSON.stringify({ + projects: { + ['home-ui-react']: { + projectType: 'library', + root: 'libs/home/ui-react', + sourceRoot: 'libs/home/ui-react/src', + architect: { + storybook: { + builder: '@nrwl/storybook:storybook', + options: { + uiFramework: '@storybook/react', + port: 4400, + config: { + configFolder: 'libs/home/ui-react/.storybook', + }, + }, + }, + }, + }, + }, + }) + ); + + tree.create( + 'libs/home/ui-react/.storybook/tsconfig.json', + JSON.stringify({ + extends: '../tsconfig.json', + include: ['../src/**/*'], + }) + ); + }); + + it(`should add storybook tsconfig to lint target and update tsconfigs in project for React project`, async () => { + tree = await runMigration('update-11-5-3', {}, tree); + + expect(getFileContent(tree, 'libs/home/ui-react/.storybook/tsconfig.json')) + .toMatchInlineSnapshot(` + "{ + \\"extends\\": \\"../tsconfig.json\\", + \\"include\\": [\\"../src/**/*\\", \\"./*.js\\"] + } + " + `); + }); +}); diff --git a/packages/storybook/src/migrations/update-11-5-3/update-lint-ignores.ts b/packages/storybook/src/migrations/update-11-5-3/update-lint-ignores.ts new file mode 100644 index 0000000000..7bebb5fb38 --- /dev/null +++ b/packages/storybook/src/migrations/update-11-5-3/update-lint-ignores.ts @@ -0,0 +1,94 @@ +import * as path from 'path'; +import { + chain, + Tree, + SchematicContext, + Rule, +} from '@angular-devkit/schematics'; + +import { + formatFiles, + readJsonInTree, + updateWorkspaceInTree, + serializeJson, +} from '@nrwl/workspace'; + +import { isFramework, TsConfig } from '../../utils/utilities'; +import { normalize } from '@angular-devkit/core'; + +interface ProjectDefinition { + root: string; + sourceRoot: string; + projectType: 'library' | 'application'; + + schematic?: Record; + architect: Record< + string, + import('@angular-devkit/core').workspaces.TargetDefinition + >; +} + +export default function (tree: Tree, context: SchematicContext) { + return chain([update, formatFiles()]); +} + +function update(tree: Tree, context: SchematicContext): Rule { + return updateWorkspaceInTree((config, context, tree) => { + Object.entries(config.projects).forEach( + ([projectName, projectConfig]) => { + updateTsconfigInclude(tree, context, { projectName, projectConfig }); + } + ); + + return config; + }); +} + +function updateTsconfigInclude( + tree: Tree, + context: SchematicContext, + options: { + projectName: string; + projectConfig: ProjectDefinition; + } +) { + const architect = options.projectConfig.architect; + + const paths = { + tsConfigStorybook: normalize( + path.join(options.projectConfig.root, '.storybook/tsconfig.json') + ), + }; + + const hasStorybookConfig = + architect.storybook && tree.exists(paths.tsConfigStorybook); + + if (!hasStorybookConfig) { + context.logger.info( + `${options.projectName}: no storybook configured. skipping migration...` + ); + return; + } + + const isReactProject = isFramework('react', { + uiFramework: architect.storybook.options?.uiFramework as Parameters< + typeof isFramework + >[1]['uiFramework'], + }); + + const tsConfig = { + storybook: readJsonInTree(tree, paths.tsConfigStorybook), + }; + + if (isReactProject && Array.isArray(tsConfig.storybook.include)) { + tsConfig.storybook.include = uniqueArray([ + ...tsConfig.storybook.include, + './*.js', + ]); + tree.overwrite(paths.tsConfigStorybook, serializeJson(tsConfig.storybook)); + } +} + +function uniqueArray>(value: T) { + return [...new Set(value)] as T; +}