@nx/react
Version:
175 lines (174 loc) • 7.63 kB
JavaScript
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;
;