From 1de896f3e88bc21f6bde3087d363d6f3e4e740b8 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Fri, 3 Jun 2022 18:31:18 +0300 Subject: [PATCH] feat(storybook): choose to generate ts config (#10572) ISSUES CLOSED: #10394 --- docs/generated/packages/react-native.json | 7 ++- docs/generated/packages/react.json | 7 ++- docs/generated/packages/storybook.json | 7 ++- .../storybook-configuration/schema.d.ts | 1 + .../storybook-configuration/schema.json | 7 ++- .../storybook-configuration/schema.d.ts | 1 + .../storybook-configuration/schema.json | 7 ++- .../configuration/configuration.spec.ts | 60 +++++++++++++++++++ .../generators/configuration/configuration.ts | 10 +++- .../.storybook/main.ts__tmpl__ | 28 +++++++++ .../.storybook/preview.ts__tmpl__ | 0 .../.storybook/tsconfig.json__tmpl__ | 14 +++++ .../root-files-ts/.storybook/main.ts | 12 ++++ .../root-files-ts/.storybook/tsconfig.json | 14 +++++ .../src/generators/configuration/schema.d.ts | 1 + .../src/generators/configuration/schema.json | 7 ++- .../configuration/util-functions.ts | 38 ++++++++++-- 17 files changed, 209 insertions(+), 12 deletions(-) create mode 100644 packages/storybook/src/generators/configuration/project-files-ts/.storybook/main.ts__tmpl__ create mode 100644 packages/storybook/src/generators/configuration/project-files-ts/.storybook/preview.ts__tmpl__ create mode 100644 packages/storybook/src/generators/configuration/project-files-ts/.storybook/tsconfig.json__tmpl__ create mode 100644 packages/storybook/src/generators/configuration/root-files-ts/.storybook/main.ts create mode 100644 packages/storybook/src/generators/configuration/root-files-ts/.storybook/tsconfig.json diff --git a/docs/generated/packages/react-native.json b/docs/generated/packages/react-native.json index 3fe9403588b21..3c7649b3279ff 100644 --- a/docs/generated/packages/react-native.json +++ b/docs/generated/packages/react-native.json @@ -355,7 +355,12 @@ }, "js": { "type": "boolean", - "description": "Generate JavaScript files rather than TypeScript files.", + "description": "Generate JavaScript story files rather than TypeScript story files.", + "default": false + }, + "tsConfiguration": { + "type": "boolean", + "description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.", "default": false }, "linter": { diff --git a/docs/generated/packages/react.json b/docs/generated/packages/react.json index 883713c0f190e..8d333daeee3af 100644 --- a/docs/generated/packages/react.json +++ b/docs/generated/packages/react.json @@ -625,7 +625,12 @@ }, "js": { "type": "boolean", - "description": "Generate JavaScript files rather than TypeScript files.", + "description": "Generate JavaScript story files rather than TypeScript story files.", + "default": false + }, + "tsConfiguration": { + "type": "boolean", + "description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.", "default": false }, "linter": { diff --git a/docs/generated/packages/storybook.json b/docs/generated/packages/storybook.json index 79f84b57bccdd..682e90834d011 100644 --- a/docs/generated/packages/storybook.json +++ b/docs/generated/packages/storybook.json @@ -139,7 +139,12 @@ }, "js": { "type": "boolean", - "description": "Generate JavaScript files rather than TypeScript files.", + "description": "Generate JavaScript story files rather than TypeScript story files.", + "default": false + }, + "tsConfiguration": { + "type": "boolean", + "description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.", "default": false }, "standaloneConfig": { diff --git a/packages/react-native/src/generators/storybook-configuration/schema.d.ts b/packages/react-native/src/generators/storybook-configuration/schema.d.ts index eee8589dab265..46345d5aeef50 100644 --- a/packages/react-native/src/generators/storybook-configuration/schema.d.ts +++ b/packages/react-native/src/generators/storybook-configuration/schema.d.ts @@ -4,6 +4,7 @@ export interface StorybookConfigureSchema { name: string; generateStories?: boolean; js?: boolean; + tsConfiguration?: boolean; linter?: Linter; standaloneConfig?: boolean; } diff --git a/packages/react-native/src/generators/storybook-configuration/schema.json b/packages/react-native/src/generators/storybook-configuration/schema.json index 2f74ad3daed12..69766ef1524ad 100644 --- a/packages/react-native/src/generators/storybook-configuration/schema.json +++ b/packages/react-native/src/generators/storybook-configuration/schema.json @@ -22,7 +22,12 @@ }, "js": { "type": "boolean", - "description": "Generate JavaScript files rather than TypeScript files.", + "description": "Generate JavaScript story files rather than TypeScript story files.", + "default": false + }, + "tsConfiguration": { + "type": "boolean", + "description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.", "default": false }, "linter": { diff --git a/packages/react/src/generators/storybook-configuration/schema.d.ts b/packages/react/src/generators/storybook-configuration/schema.d.ts index 3ad4734793ad9..c151d4cae1f9f 100644 --- a/packages/react/src/generators/storybook-configuration/schema.d.ts +++ b/packages/react/src/generators/storybook-configuration/schema.d.ts @@ -6,6 +6,7 @@ export interface StorybookConfigureSchema { generateStories?: boolean; generateCypressSpecs?: boolean; js?: boolean; + tsConfiguration?: boolean; linter?: Linter; cypressDirectory?: string; standaloneConfig?: boolean; diff --git a/packages/react/src/generators/storybook-configuration/schema.json b/packages/react/src/generators/storybook-configuration/schema.json index 8e07e51f5d3a7..fa57fbbcbded3 100644 --- a/packages/react/src/generators/storybook-configuration/schema.json +++ b/packages/react/src/generators/storybook-configuration/schema.json @@ -38,7 +38,12 @@ }, "js": { "type": "boolean", - "description": "Generate JavaScript files rather than TypeScript files.", + "description": "Generate JavaScript story files rather than TypeScript story files.", + "default": false + }, + "tsConfiguration": { + "type": "boolean", + "description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.", "default": false }, "linter": { diff --git a/packages/storybook/src/generators/configuration/configuration.spec.ts b/packages/storybook/src/generators/configuration/configuration.spec.ts index 3d4d834ca78d7..8ca14dff7ea57 100644 --- a/packages/storybook/src/generators/configuration/configuration.spec.ts +++ b/packages/storybook/src/generators/configuration/configuration.spec.ts @@ -81,6 +81,31 @@ describe('@nrwl/storybook:configuration', () => { ).toBeFalsy(); }); + it('should generate TypeScript Configuration files', async () => { + await configurationGenerator(tree, { + name: 'test-ui-lib', + uiFramework: '@storybook/angular', + standaloneConfig: false, + tsConfiguration: true, + }); + + // Root + expect(tree.exists('.storybook/tsconfig.json')).toBeTruthy(); + expect(tree.exists('.storybook/main.ts')).toBeTruthy(); + const rootStorybookTsconfigJson = readJson( + tree, + '.storybook/tsconfig.json' + ); + expect(rootStorybookTsconfigJson.extends).toBe('../tsconfig.base.json'); + + // Local + expect( + tree.exists('libs/test-ui-lib/.storybook/tsconfig.json') + ).toBeTruthy(); + expect(tree.exists('libs/test-ui-lib/.storybook/main.ts')).toBeTruthy(); + expect(tree.exists('libs/test-ui-lib/.storybook/preview.ts')).toBeTruthy(); + }); + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { tree.rename('tsconfig.base.json', 'tsconfig.json'); @@ -350,4 +375,39 @@ describe('@nrwl/storybook:configuration', () => { readJson(tree, 'libs/test-ui-lib2/.storybook/tsconfig.json').files ).toMatchSnapshot(); }); + + it('should generate TS config for project if root config is TS', async () => { + await configurationGenerator(tree, { + name: 'test-ui-lib', + uiFramework: '@storybook/angular', + standaloneConfig: false, + tsConfiguration: true, + }); + + const newContents = `module.exports = { + stories: [], + addons: ['@storybook/addon-essentials', 'new-addon'], +}; +`; + // Setup a new lib + await libraryGenerator(tree, { + name: 'test-ui-lib-2', + standaloneConfig: false, + }); + + tree.write('.storybook/main.ts', newContents); + await configurationGenerator(tree, { + name: 'test-ui-lib-2', + uiFramework: '@storybook/angular', + standaloneConfig: false, + }); + + expect(tree.read('.storybook/main.ts', 'utf-8')).toEqual(newContents); + expect(tree.exists('libs/test-ui-lib-2/.storybook/main.ts')).toBeTruthy(); + expect( + tree.exists('libs/test-ui-lib-2/.storybook/preview.ts') + ).toBeTruthy(); + expect(tree.exists('libs/test-ui-lib-2/.storybook/main.js')).toBeFalsy(); + expect(tree.exists('libs/test-ui-lib-2/.storybook/preview.js')).toBeFalsy(); + }); }); diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index b5f80100b7e64..e4be613dd721a 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -39,8 +39,14 @@ export async function configurationGenerator( }); tasks.push(initTask); - createRootStorybookDir(tree, schema.js); - createProjectStorybookDir(tree, schema.name, schema.uiFramework, schema.js); + createRootStorybookDir(tree, schema.js, schema.tsConfiguration); + createProjectStorybookDir( + tree, + schema.name, + schema.uiFramework, + schema.js, + schema.tsConfiguration + ); configureTsProjectConfig(tree, schema); configureTsSolutionConfig(tree, schema); updateLintConfig(tree, schema); diff --git a/packages/storybook/src/generators/configuration/project-files-ts/.storybook/main.ts__tmpl__ b/packages/storybook/src/generators/configuration/project-files-ts/.storybook/main.ts__tmpl__ new file mode 100644 index 0000000000000..4f13b72ddc568 --- /dev/null +++ b/packages/storybook/src/generators/configuration/project-files-ts/.storybook/main.ts__tmpl__ @@ -0,0 +1,28 @@ +import { rootMain } from '<%= offsetFromRoot %>../.storybook/main'; +import type { StorybookConfig, Options } from '@storybook/core-common'; + +const config: StorybookConfig = { + ...rootMain, + <% if (useWebpack5) { %> + core: { ...rootMain.core, builder: 'webpack5' }, + <% } %> + + stories: [ + ...rootMain.stories, + '../src/app/**/*.stories.mdx', + '../src/app/**/*.stories.@(js|jsx|ts|tsx)', + ], + addons: [...(rootMain.addons || []) <% if(uiFramework === '@storybook/react') { %>, '@nrwl/react/plugins/storybook' <% } %><% if(uiFramework === '@storybook/react-native') { %>, '@storybook/addon-ondevice-actions', '@storybook/addon-ondevice-backgrounds', '@storybook/addon-ondevice-controls', '@storybook/addon-ondevice-notes' <% } %>], + webpackFinal: async (config, { configType }: Options) => { + // apply any global webpack configs that might have been specified in .storybook/main.ts + if (rootMain.webpackFinal) { + config = await rootMain.webpackFinal(config, { configType } as Options); + } + + // add your own webpack tweaks if needed + + return config; + }, +}; + +module.exports = config; diff --git a/packages/storybook/src/generators/configuration/project-files-ts/.storybook/preview.ts__tmpl__ b/packages/storybook/src/generators/configuration/project-files-ts/.storybook/preview.ts__tmpl__ new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/storybook/src/generators/configuration/project-files-ts/.storybook/tsconfig.json__tmpl__ b/packages/storybook/src/generators/configuration/project-files-ts/.storybook/tsconfig.json__tmpl__ new file mode 100644 index 0000000000000..6a09ea98e12b5 --- /dev/null +++ b/packages/storybook/src/generators/configuration/project-files-ts/.storybook/tsconfig.json__tmpl__ @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "emitDecoratorMetadata": true + <% if(uiFramework === '@storybook/react') { %>, "outDir": "" <% } %> + }, + <% if(uiFramework === '@storybook/react') { %>"files": [ + "<%= offsetFromRoot %>../node_modules/@nrwl/react/typings/styled-jsx.d.ts", + "<%= offsetFromRoot %>../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "<%= offsetFromRoot %>../node_modules/@nrwl/react/typings/image.d.ts" + ],<% } %> + "exclude": ["../**/*.spec.ts" <% if(uiFramework === '@storybook/react') { %>, "../**/*.spec.js", "../**/*.spec.tsx", "../**/*.spec.jsx"<% } %>], + "include": ["../src/**/*", "*.js", "*.ts" <% if(uiFramework === '@storybook/react-native') { %>, "*.tsx"<% } %>] +} diff --git a/packages/storybook/src/generators/configuration/root-files-ts/.storybook/main.ts b/packages/storybook/src/generators/configuration/root-files-ts/.storybook/main.ts new file mode 100644 index 0000000000000..acaa1f82c83bc --- /dev/null +++ b/packages/storybook/src/generators/configuration/root-files-ts/.storybook/main.ts @@ -0,0 +1,12 @@ +import type { StorybookConfig } from '@storybook/core-common'; + +export const rootMain: StorybookConfig = { + stories: [], + addons: ['@storybook/addon-essentials'], + // webpackFinal: async (config, { configType }) => { + // // Make whatever fine-grained changes you need that should apply to all storybook configs + + // // Return the altered config + // return config; + // }, +}; diff --git a/packages/storybook/src/generators/configuration/root-files-ts/.storybook/tsconfig.json b/packages/storybook/src/generators/configuration/root-files-ts/.storybook/tsconfig.json new file mode 100644 index 0000000000000..1e25d668b5a97 --- /dev/null +++ b/packages/storybook/src/generators/configuration/root-files-ts/.storybook/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../<%= rootTsConfigPath %>", + "exclude": [ + "../**/*.spec.js", + "../**/*.test.js", + "../**/*.spec.ts", + "../**/*.test.ts", + "../**/*.spec.tsx", + "../**/*.test.tsx", + "../**/*.spec.jsx", + "../**/*.test.jsx" + ], + "include": ["../**/*"] +} diff --git a/packages/storybook/src/generators/configuration/schema.d.ts b/packages/storybook/src/generators/configuration/schema.d.ts index 905a35c29c8ce..35ec0437fe326 100644 --- a/packages/storybook/src/generators/configuration/schema.d.ts +++ b/packages/storybook/src/generators/configuration/schema.d.ts @@ -9,6 +9,7 @@ export interface StorybookConfigureSchema { configureCypress?: boolean; linter?: Linter; js?: boolean; + tsConfiguration?: 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 10aee5c5aa425..22e3ef2945f1d 100644 --- a/packages/storybook/src/generators/configuration/schema.json +++ b/packages/storybook/src/generators/configuration/schema.json @@ -41,7 +41,12 @@ }, "js": { "type": "boolean", - "description": "Generate JavaScript files rather than TypeScript files.", + "description": "Generate JavaScript story files rather than TypeScript story files.", + "default": false + }, + "tsConfiguration": { + "type": "boolean", + "description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.", "default": false }, "standaloneConfig": { diff --git a/packages/storybook/src/generators/configuration/util-functions.ts b/packages/storybook/src/generators/configuration/util-functions.ts index ca7752ba642b5..2b8e173b97448 100644 --- a/packages/storybook/src/generators/configuration/util-functions.ts +++ b/packages/storybook/src/generators/configuration/util-functions.ts @@ -238,7 +238,11 @@ export function normalizeSchema( }; } -export function createRootStorybookDir(tree: Tree, js: boolean) { +export function createRootStorybookDir( + tree: Tree, + js: boolean, + tsConfiguration: boolean +) { if (tree.exists('.storybook')) { logger.warn( `.storybook folder already exists at root! Skipping generating files in it.` @@ -247,7 +251,10 @@ export function createRootStorybookDir(tree: Tree, js: boolean) { } logger.debug(`adding .storybook folder to the root directory`); - const templatePath = join(__dirname, './root-files'); + const templatePath = join( + __dirname, + tsConfiguration ? './root-files-ts' : './root-files' + ); generateFiles(tree, templatePath, '', { rootTsConfigPath: getRootTsConfigPathInTree(tree), }); @@ -261,8 +268,28 @@ export function createProjectStorybookDir( tree: Tree, projectName: string, uiFramework: StorybookConfigureSchema['uiFramework'], - js: boolean + js: boolean, + tsConfiguration: boolean ) { + // Check if root main file is .ts or .js + if (tree.exists('.storybook/main.ts')) { + logger.info( + `The root Storybook configuration is in TypeScript, + so Nx will generate TypeScript Storybook configuration files + in this project's .storybook folder as well.` + ); + tsConfiguration = true; + } else { + if (tree.exists('.storybook/main.js')) { + logger.info( + `The root Storybook configuration is in JavaScript, + so Nx will generate JavaScript Storybook configuration files + in this project's .storybook folder as well.` + ); + tsConfiguration = false; + } + } + const { root, projectType } = readProjectConfiguration(tree, projectName); const projectDirectory = projectType === 'application' ? 'app' : 'lib'; @@ -276,7 +303,10 @@ export function createProjectStorybookDir( } logger.debug(`adding .storybook folder to ${projectDirectory}`); - const templatePath = join(__dirname, './project-files'); + const templatePath = join( + __dirname, + tsConfiguration ? './project-files-ts' : './project-files' + ); generateFiles(tree, templatePath, root, { tmpl: '',