From 2561da1d0927bd6cf98ececa0ce9dd70207f182a Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 18 Jan 2023 15:59:45 -0500 Subject: [PATCH] fix(react): add migration to install @nrwl/webpack if needed by Storybook --- packages/react/plugins/storybook/index.ts | 16 ++-- .../component-test/component-test.spec.ts | 1 - .../cypress-component-configuration.spec.ts | 1 - .../src/generators/library/library.spec.ts | 1 - .../generators/stories/stories.lib.spec.ts | 1 - .../configuration.spec.ts | 1 - packages/storybook/migrations.json | 6 ++ .../storybook/src/generators/init/init.ts | 3 + .../ensure-webpack-package.spec.ts | 83 +++++++++++++++++++ .../update-15-5-3/ensure-webpack-package.ts | 41 +++++++++ .../webpack/src/generators/init/init.spec.ts | 2 + packages/webpack/src/generators/init/init.ts | 47 ++++------- 12 files changed, 160 insertions(+), 43 deletions(-) create mode 100644 packages/storybook/src/migrations/update-15-5-3/ensure-webpack-package.spec.ts create mode 100644 packages/storybook/src/migrations/update-15-5-3/ensure-webpack-package.ts diff --git a/packages/react/plugins/storybook/index.ts b/packages/react/plugins/storybook/index.ts index 0c8ad363970e7..7465fef9e5333 100644 --- a/packages/react/plugins/storybook/index.ts +++ b/packages/react/plugins/storybook/index.ts @@ -5,10 +5,7 @@ import { readJsonFile, workspaceRoot, } from '@nrwl/devkit'; -import { - composePlugins, - getBaseWebpackPartial, -} from '@nrwl/webpack/src/utils/config'; +import { composePlugins } from '@nrwl/webpack/src/utils/config'; import { NormalizedWebpackExecutorOptions } from '@nrwl/webpack/src/executors/webpack/schema'; import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils'; import { join } from 'path'; @@ -17,7 +14,6 @@ import { Configuration, DefinePlugin, WebpackPluginInstance } from 'webpack'; import * as mergeWebpack from 'webpack-merge'; import { mergePlugins } from './merge-plugins'; import { withReact } from '../with-react'; -import { withNx, withWeb } from '@nrwl/webpack'; // This is shamelessly taken from CRA and modified for NX use // https://github.com/facebook/create-react-app/blob/4784997f0682e75eb32a897b4ffe34d735912e6c/packages/react-scripts/config/env.js#L71 @@ -96,7 +92,17 @@ export const webpack = async ( logger.info( '=> Loading Nrwl React Storybook preset from "@nrwl/react/plugins/storybook"' ); + // In case anyone is missing dep and did not run migrations. + // See: https://github.com/nrwl/nx/issues/14455 + try { + require.resolve('@nrwl/webpack'); + } catch { + throw new Error( + `'@nrwl/webpack' package is not installed. Install it and try again.` + ); + } + const { withNx, withWeb } = require('@nrwl/webpack'); const tsconfigPath = join(options.configDir, 'tsconfig.json'); const builderOptions: NormalizedWebpackExecutorOptions = { diff --git a/packages/react/src/generators/component-test/component-test.spec.ts b/packages/react/src/generators/component-test/component-test.spec.ts index 88b05bb7ebe3f..fe9acc514544c 100644 --- a/packages/react/src/generators/component-test/component-test.spec.ts +++ b/packages/react/src/generators/component-test/component-test.spec.ts @@ -2,7 +2,6 @@ import { assertMinimumCypressVersion } from '@nrwl/cypress/src/utils/cypress-ver import { Tree } from '@nrwl/devkit'; import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { Linter } from '@nrwl/linter'; -import enquirer = require('enquirer'); import libraryGenerator from '../library/library'; import { componentTestGenerator } from './component-test'; diff --git a/packages/react/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts b/packages/react/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts index 5fa7dae1861be..7ddb5d4773c26 100644 --- a/packages/react/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts +++ b/packages/react/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts @@ -12,7 +12,6 @@ import { applicationGenerator } from '../application/application'; import { componentGenerator } from '../component/component'; import { libraryGenerator } from '../library/library'; import { cypressComponentConfigGenerator } from './cypress-component-configuration'; -import enquirer = require('enquirer'); let projectGraph: ProjectGraph; jest.mock('@nrwl/devkit', () => ({ diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts index 9b10d2bb48ef1..e59e750894e27 100644 --- a/packages/react/src/generators/library/library.spec.ts +++ b/packages/react/src/generators/library/library.spec.ts @@ -11,7 +11,6 @@ import { createTreeWithEmptyWorkspace, } from '@nrwl/devkit/testing'; import { Linter } from '@nrwl/linter'; -import enquirer = require('enquirer'); import { nxVersion } from '../../utils/versions'; import applicationGenerator from '../application/application'; import libraryGenerator from './library'; diff --git a/packages/react/src/generators/stories/stories.lib.spec.ts b/packages/react/src/generators/stories/stories.lib.spec.ts index 58b87eb1b34d9..0c92e83059015 100644 --- a/packages/react/src/generators/stories/stories.lib.spec.ts +++ b/packages/react/src/generators/stories/stories.lib.spec.ts @@ -4,7 +4,6 @@ import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import applicationGenerator from '../application/application'; import { Linter } from '@nrwl/linter'; import libraryGenerator from '../library/library'; -import enquirer = require('enquirer'); describe('react:stories for libraries', () => { let appTree: Tree; diff --git a/packages/react/src/generators/storybook-configuration/configuration.spec.ts b/packages/react/src/generators/storybook-configuration/configuration.spec.ts index 1f33dbbc5dc1d..be9dcb8eada43 100644 --- a/packages/react/src/generators/storybook-configuration/configuration.spec.ts +++ b/packages/react/src/generators/storybook-configuration/configuration.spec.ts @@ -2,7 +2,6 @@ import { installedCypressVersion } from '@nrwl/cypress/src/utils/cypress-version import { logger, Tree } from '@nrwl/devkit'; import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { Linter } from '@nrwl/linter'; -import enquirer = require('enquirer'); import applicationGenerator from '../application/application'; import componentGenerator from '../component/component'; import libraryGenerator from '../library/library'; diff --git a/packages/storybook/migrations.json b/packages/storybook/migrations.json index 94d23f0d7d3f4..bf85b5c8a4a94 100644 --- a/packages/storybook/migrations.json +++ b/packages/storybook/migrations.json @@ -59,6 +59,12 @@ "version": "15.4.6-beta.0", "description": "Refactor the Storybook target options", "factory": "./src/migrations/update-15-4-6/refactor-executor-options" + }, + "update-15-5-3": { + "cli": "nx", + "version": "15.5.3-beta.0", + "description": "Add @nrwl/webpack if it is missing and is used.", + "factory": "./src/migrations/update-15-5-3/ensure-webpack-package" } }, "packageJsonUpdates": { diff --git a/packages/storybook/src/generators/init/init.ts b/packages/storybook/src/generators/init/init.ts index 576dd1cbe027b..96626bc4dd6f9 100644 --- a/packages/storybook/src/generators/init/init.ts +++ b/packages/storybook/src/generators/init/init.ts @@ -112,6 +112,9 @@ function checkDependenciesInstalled(host: Tree, schema: Schema) { devDependencies['@babel/core'] = babelCoreVersion; devDependencies['@babel/preset-typescript'] = babelPresetTypescriptVersion; + if (schema.bundler === 'webpack') { + devDependencies['@nrwl/webpack'] = nxVersion; + } } if (schema.uiFramework === '@storybook/web-components') { diff --git a/packages/storybook/src/migrations/update-15-5-3/ensure-webpack-package.spec.ts b/packages/storybook/src/migrations/update-15-5-3/ensure-webpack-package.spec.ts new file mode 100644 index 0000000000000..8465847496614 --- /dev/null +++ b/packages/storybook/src/migrations/update-15-5-3/ensure-webpack-package.spec.ts @@ -0,0 +1,83 @@ +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { + readJson, + writeJson, + addProjectConfiguration, + Tree, +} from '@nrwl/devkit'; +import update from './ensure-webpack-package'; + +describe('ensure-webpack-package', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + writeJson(tree, 'package.json', { + dependencies: {}, + devDependencies: { + '@nrwl/react': '15.5.0', + }, + }); + }); + + it.each` + config + ${'main.ts'} + ${'main.js'} + `( + 'should install @nrwl/webpack if it is needed by React project', + async ({ config }) => { + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + }); + tree.write( + `myapp/.storybook/${config}`, + ` + module.exports = { + addons: ['@nrwl/react/plugins/storybook'] + }; + ` + ); + + await update(tree); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.devDependencies).toEqual({ + '@nrwl/react': expect.any(String), + '@nrwl/webpack': expect.any(String), + }); + } + ); + + it.each` + config + ${'main.ts'} + ${'main.js'} + ${null} + `( + 'should not install @nrwl/webpack if it is not needed by React project', + async ({ config }) => { + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + }); + + if (config) { + tree.write( + `myapp/.storybook/${config}`, + ` + module.exports = { + addons: [] + }; + ` + ); + } + + await update(tree); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.devDependencies).toEqual({ + '@nrwl/react': expect.any(String), + }); + } + ); +}); diff --git a/packages/storybook/src/migrations/update-15-5-3/ensure-webpack-package.ts b/packages/storybook/src/migrations/update-15-5-3/ensure-webpack-package.ts new file mode 100644 index 0000000000000..4e3d6d279e725 --- /dev/null +++ b/packages/storybook/src/migrations/update-15-5-3/ensure-webpack-package.ts @@ -0,0 +1,41 @@ +import { + addDependenciesToPackageJson, + getProjects, + joinPathFragments, + Tree, +} from '@nrwl/devkit'; +import { nxVersion } from '../../utils/versions'; + +// Add @nrwl/webpack as needed. +// See: https://github.com/nrwl/nx/issues/14455 +export default async function update(tree: Tree) { + const projects = getProjects(tree); + const reactPlugin = '@nrwl/react/plugins/storybook'; + let shouldInstall = false; + + for (const [, config] of projects) { + let sbConfigPath = joinPathFragments(config.root, '.storybook/main.ts'); + + if (!tree.exists(sbConfigPath)) { + sbConfigPath = joinPathFragments(config.root, '.storybook/main.js'); + } + + if (!tree.exists(sbConfigPath)) { + continue; + } + + const sbConfig = tree.read(sbConfigPath, 'utf-8'); + if (sbConfig.includes(reactPlugin)) { + shouldInstall = true; + break; + } + } + + if (shouldInstall) { + return addDependenciesToPackageJson( + tree, + {}, + { '@nrwl/webpack': nxVersion } + ); + } +} diff --git a/packages/webpack/src/generators/init/init.spec.ts b/packages/webpack/src/generators/init/init.spec.ts index 9c6b18840045f..5d70626e171a5 100644 --- a/packages/webpack/src/generators/init/init.spec.ts +++ b/packages/webpack/src/generators/init/init.spec.ts @@ -35,6 +35,7 @@ describe('webpackInitGenerator', () => { name: expect.any(String), dependencies: {}, devDependencies: { + '@nrwl/webpack': expect.any(String), '@swc/helpers': expect.any(String), '@swc/core': expect.any(String), 'swc-loader': expect.any(String), @@ -50,6 +51,7 @@ describe('webpackInitGenerator', () => { name: expect.any(String), dependencies: {}, devDependencies: { + '@nrwl/webpack': expect.any(String), tslib: expect.any(String), }, }); diff --git a/packages/webpack/src/generators/init/init.ts b/packages/webpack/src/generators/init/init.ts index 16119f89f7e76..70275d2888136 100644 --- a/packages/webpack/src/generators/init/init.ts +++ b/packages/webpack/src/generators/init/init.ts @@ -2,14 +2,13 @@ import { addDependenciesToPackageJson, convertNxGenerator, formatFiles, - GeneratorCallback, Tree, } from '@nrwl/devkit'; -import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; import { swcCoreVersion } from '@nrwl/js/src/utils/versions'; import { Schema } from './schema'; import { + nxVersion, reactRefreshVersion, reactRefreshWebpackPluginVersion, svgrWebpackVersion, @@ -21,54 +20,36 @@ import { import { addBabelInputs } from '@nrwl/js/src/utils/add-babel-inputs'; export async function webpackInitGenerator(tree: Tree, schema: Schema) { - const tasks: GeneratorCallback[] = []; - if (schema.compiler === 'babel') { addBabelInputs(tree); } + const devDependencies = { + '@nrwl/webpack': nxVersion, + }; if (schema.compiler === 'swc') { - const swcInstallTask = addDependenciesToPackageJson( - tree, - {}, - { - '@swc/helpers': swcHelpersVersion, - '@swc/core': swcCoreVersion, - 'swc-loader': swcLoaderVersion, - } - ); - tasks.push(swcInstallTask); + devDependencies['@swc/helpers'] = swcHelpersVersion; + devDependencies['@swc/core'] = swcCoreVersion; + devDependencies['swc-loader'] = swcLoaderVersion; } if (schema.compiler === 'tsc') { - const tscInstallTask = addDependenciesToPackageJson( - tree, - {}, - { tslib: tsLibVersion } - ); - tasks.push(tscInstallTask); + devDependencies['tslib'] = tsLibVersion; } if (schema.uiFramework === 'react') { - const reactInstallTask = addDependenciesToPackageJson( - tree, - {}, - { - '@pmmmwh/react-refresh-webpack-plugin': - reactRefreshWebpackPluginVersion, - '@svgr/webpack': svgrWebpackVersion, - 'react-refresh': reactRefreshVersion, - 'url-loader': urlLoaderVersion, - } - ); - tasks.push(reactInstallTask); + devDependencies['@pmmmwh/react-refresh-webpack-plugin'] = + reactRefreshWebpackPluginVersion; + devDependencies['@svgr/webpack'] = svgrWebpackVersion; + devDependencies['react-refresh'] = reactRefreshVersion; + devDependencies['url-loader'] = urlLoaderVersion; } if (!schema.skipFormat) { await formatFiles(tree); } - return runTasksInSerial(...tasks); + return addDependenciesToPackageJson(tree, {}, devDependencies); } export default webpackInitGenerator;