UNPKG

@nx/react

Version:

The React plugin for Nx contains executors and generators for managing React applications and libraries within an Nx workspace. It provides: - Integration with libraries such as Jest, Vitest, Playwright, Cypress, and Storybook. - Generators for applica

175 lines (174 loc) 7.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.webpack = exports.core = void 0; const devkit_1 = require("@nx/devkit"); const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup"); const config_1 = require("@nx/webpack/src/utils/config"); const fs_1 = require("fs"); const path_1 = require("path"); const webpack_1 = require("webpack"); const with_react_1 = require("../with-react"); const merge_plugins_1 = require("./merge-plugins"); // 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 function getClientEnvironment(mode) { // Grab NODE_ENV and NX_* and STORYBOOK_* environment variables and prepare them to be // injected into the application via DefinePlugin in webpack configuration. const NX_PREFIX = /^NX_PUBLIC_/i; const STORYBOOK_PREFIX = /^STORYBOOK_/i; const raw = Object.keys(process.env) .filter((key) => NX_PREFIX.test(key) || STORYBOOK_PREFIX.test(key)) .reduce((env, key) => { env[key] = process.env[key]; return env; }, { // Useful for determining whether we’re running in production mode. NODE_ENV: process.env.NODE_ENV || mode, // Environment variables for Storybook // https://github.com/storybookjs/storybook/blob/bdf9e5ed854b8d34e737eee1a4a05add88265e92/lib/core-common/src/utils/envs.ts#L12-L21 NODE_PATH: process.env.NODE_PATH || '', STORYBOOK: process.env.STORYBOOK || 'true', // This is to support CRA's public folder feature. // In production we set this to dot(.) to allow the browser to access these assets // even when deployed inside a subpath. (like in GitHub pages) // In development this is just empty as we always serves from the root. PUBLIC_URL: mode === 'production' ? '.' : '', }); // Stringify all values so we can feed into webpack DefinePlugin const stringified = { 'process.env': Object.keys(raw).reduce((env, key) => { env[key] = JSON.stringify(raw[key]); return env; }, {}), }; return { stringified }; } const core = (prev, options) => ({ ...prev, disableWebpackDefaults: true, }); exports.core = core; const getProjectData = async (storybookOptions) => { const fallbackData = { workspaceRoot: storybookOptions.configDir, projectRoot: '', sourceRoot: '', }; // Edge-case: not running from Nx if (!process.env.NX_WORKSPACE_ROOT) return fallbackData; const projectGraph = await (0, devkit_1.createProjectGraphAsync)(); const projectNode = projectGraph.nodes[process.env.NX_TASK_TARGET_PROJECT]; return projectNode ? { workspaceRoot: process.env.NX_WORKSPACE_ROOT, projectRoot: projectNode.data.root, sourceRoot: (0, ts_solution_setup_1.getProjectSourceRoot)(projectNode.data), projectNode, } : // Edge-case: missing project node fallbackData; }; const fixBabelConfigurationIfNeeded = (webpackConfig, projectData) => { if (!projectData.projectNode) return; const isUsingBabelUpwardRootMode = Object.keys(projectData.projectNode.data.targets).find((k) => { const targetConfig = projectData.projectNode.data.targets[k]; return (targetConfig.executor === '@nx/webpack:webpack' && targetConfig.options?.babelUpwardRootMode); }); if (isUsingBabelUpwardRootMode) return; let babelrcPath; for (const ext of ['', '.json', '.js', '.cjs', '.mjs', '.cts']) { const candidate = (0, path_1.join)(projectData.workspaceRoot, projectData.projectRoot, `.babelrc${ext}`); if ((0, fs_1.existsSync)(candidate)) { babelrcPath = candidate; break; } } // Unexpected setup, skip. if (!babelrcPath) return; let babelRuleItem; for (const rule of webpackConfig.module.rules) { if (typeof rule === 'string') continue; if (!rule || !Array.isArray(rule.use)) continue; for (const item of rule.use) { if (typeof item !== 'string' && item['loader'].includes('babel-loader')) { babelRuleItem = item; break; } } } if (babelRuleItem) { babelRuleItem.options.configFile = babelrcPath; } }; const webpack = async (storybookWebpackConfig = {}, options) => { devkit_1.logger.info('=> Loading Nx React Storybook Addon from "@nx/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('@nx/webpack'); } catch { throw new Error(`'@nx/webpack' package is not installed. Install it and try again.`); } const { withNx, withWeb } = require('@nx/webpack'); const projectData = await getProjectData(options); const tsconfigPath = (0, fs_1.existsSync)((0, path_1.join)(projectData.projectRoot, 'tsconfig.storybook.json')) ? (0, path_1.join)(projectData.projectRoot, 'tsconfig.storybook.json') : (0, path_1.join)(projectData.projectRoot, 'tsconfig.json'); // The 'tsconfig.json' is mainly for the cypress test to be able to run // because it will look into the cypress project dir and it will not find tsconfig.storybook.json fixBabelConfigurationIfNeeded(storybookWebpackConfig, projectData); const builderOptions = { ...options, root: projectData.workspaceRoot, projectRoot: projectData.projectRoot, sourceRoot: (0, ts_solution_setup_1.getProjectSourceRoot)(projectData.projectNode.data), fileReplacements: [], sourceMap: true, styles: options.styles ?? [], optimization: {}, tsConfig: tsconfigPath, extractCss: storybookWebpackConfig.mode === 'production', target: 'web', }; // ESM build for modern browsers. let baseWebpackConfig = {}; const configure = (0, config_1.composePluginsSync)(withNx({ target: 'web', skipTypeChecking: true }), (0, with_react_1.withReact)()); const finalConfig = configure(baseWebpackConfig, { options: builderOptions, context: { root: devkit_1.workspaceRoot }, // The context is not used here. }); return { ...storybookWebpackConfig, module: { ...storybookWebpackConfig.module, rules: [ ...storybookWebpackConfig.module.rules, ...finalConfig.module.rules, ], }, resolve: { ...storybookWebpackConfig.resolve, fallback: { ...storybookWebpackConfig.resolve?.fallback, // Next.js and other React frameworks may have server-code that uses these modules. // They are not meant for client-side components so skip the fallbacks. assert: false, path: false, util: false, }, plugins: (0, merge_plugins_1.mergePlugins)(...(storybookWebpackConfig.resolve.plugins ?? []), ...(finalConfig.resolve .plugins ?? [])), }, plugins: (0, merge_plugins_1.mergePlugins)(new webpack_1.DefinePlugin(getClientEnvironment(storybookWebpackConfig.mode).stringified), ...(storybookWebpackConfig.plugins ?? []), ...(finalConfig.plugins ?? [])), }; }; exports.webpack = webpack;