@nx/rspack
Version:
393 lines (392 loc) • 15.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyWebConfig = applyWebConfig;
const core_1 = require("@rspack/core");
const instantiate_script_plugins_1 = require("./instantiate-script-plugins");
const path_1 = require("path");
const hash_format_1 = require("./hash-format");
const normalize_entry_1 = require("./normalize-entry");
const stylesheet_loaders_1 = require("./loaders/stylesheet-loaders");
function applyWebConfig(options, config = {}, { useNormalizedEntry, } = {}) {
if (global.NX_GRAPH_CREATION)
return;
// Defaults that was applied from executor schema previously.
options.runtimeChunk ??= true; // need this for HMR and other things to work
options.extractCss ??= true;
options.generateIndexHtml ??= true;
options.index = options.index
? (0, path_1.join)(options.root, options.index)
: (0, path_1.join)(options.root, options.projectGraph.nodes[options.projectName].data.sourceRoot, 'index.html');
options.styles ??= [];
options.scripts ??= [];
const isProd = process.env.NODE_ENV === 'production' || options.mode === 'production';
const plugins = [
new core_1.EnvironmentPlugin({
NODE_ENV: isProd ? 'production' : 'development',
}),
];
const stylesOptimization = typeof options.optimization === 'object'
? options.optimization.styles
: options.optimization;
if (Array.isArray(options.scripts)) {
plugins.push(...(0, instantiate_script_plugins_1.instantiateScriptPlugins)(options));
}
if (options.index && options.generateIndexHtml) {
plugins.push(new core_1.HtmlRspackPlugin({
template: options.index,
sri: options.subresourceIntegrity ? 'sha256' : undefined,
...(options.baseHref ? { base: { href: options.baseHref } } : {}),
...(config.output?.scriptType === 'module'
? { scriptLoading: 'module' }
: {}),
}));
}
const minimizer = [];
if (isProd && stylesOptimization) {
minimizer.push(new core_1.LightningCssMinimizerRspackPlugin({
test: /\.(?:css|scss|sass|less|styl)$/,
}));
}
if (!options.ssr) {
plugins.push(new core_1.DefinePlugin(getClientEnvironment(process.env.NODE_ENV).stringified));
}
const entries = {};
const globalStylePaths = [];
// Determine hashing format.
const hashFormat = (0, hash_format_1.getOutputHashFormat)(options.outputHashing);
const sassOptions = options.stylePreprocessorOptions?.sassOptions;
const lessOptions = options.stylePreprocessorOptions?.lessOptions;
const includePaths = [];
if (options?.stylePreprocessorOptions?.includePaths?.length > 0) {
options.stylePreprocessorOptions.includePaths.forEach((includePath) => includePaths.push((0, path_1.resolve)(options.root, includePath)));
}
let lessPathOptions = {};
if (includePaths.length > 0) {
lessPathOptions = {
paths: includePaths,
};
}
// Process global styles.
if (options.styles.length > 0) {
(0, normalize_entry_1.normalizeExtraEntryPoints)(options.styles, 'styles').forEach((style) => {
const resolvedPath = style.input.startsWith('.')
? style.input
: (0, path_1.resolve)(options.root, style.input);
// Add global css paths.
globalStylePaths.push(resolvedPath);
});
}
const cssModuleRules = [
{
test: /\.module\.css$/,
exclude: globalStylePaths,
use: (0, stylesheet_loaders_1.getCommonLoadersForCssModules)(options, includePaths),
},
{
test: /\.module\.(scss|sass)$/,
exclude: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForCssModules)(options, includePaths),
{
loader: require.resolve('sass-loader'),
options: {
implementation: options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
api: 'modern-compiler',
sassOptions: {
fiber: false,
precision: 8,
includePaths,
...(sassOptions ?? {}),
},
},
},
],
},
{
test: /\.module\.less$/,
exclude: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForCssModules)(options, includePaths),
{
loader: require.resolve('less-loader'),
options: {
lessOptions: {
paths: includePaths,
...(lessOptions ?? {}),
},
},
},
],
},
{
test: /\.module\.styl$/,
exclude: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForCssModules)(options, includePaths),
{
loader: (0, path_1.join)(__dirname, '../../../utils/webpack/deprecated-stylus-loader.js'),
options: {
stylusOptions: {
include: includePaths,
},
},
},
],
},
];
const globalCssRules = [
{
test: /\.css$/,
exclude: globalStylePaths,
use: (0, stylesheet_loaders_1.getCommonLoadersForGlobalCss)(options, includePaths),
},
{
test: /\.scss$|\.sass$/,
exclude: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForGlobalCss)(options, includePaths),
{
loader: require.resolve('sass-loader'),
options: {
api: 'modern-compiler',
implementation: options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
sourceMap: !!options.sourceMap,
sassOptions: {
fiber: false,
// bootstrap-sass requires a minimum precision of 8
precision: 8,
includePaths,
...(sassOptions ?? {}),
},
},
},
],
},
{
test: /\.less$/,
exclude: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForGlobalCss)(options, includePaths),
{
loader: require.resolve('less-loader'),
options: {
sourceMap: !!options.sourceMap,
lessOptions: {
javascriptEnabled: true,
...lessPathOptions,
...(lessOptions ?? {}),
},
},
},
],
},
{
test: /\.styl$/,
exclude: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForGlobalCss)(options, includePaths),
{
loader: (0, path_1.join)(__dirname, '../../../utils/webpack/deprecated-stylus-loader.js'),
options: {
sourceMap: !!options.sourceMap,
stylusOptions: {
include: includePaths,
},
},
},
],
},
];
const globalStyleRules = [
{
test: /\.css$/,
include: globalStylePaths,
use: (0, stylesheet_loaders_1.getCommonLoadersForGlobalStyle)(options, includePaths),
},
{
test: /\.scss$|\.sass$/,
include: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForGlobalStyle)(options, includePaths),
{
loader: require.resolve('sass-loader'),
options: {
api: 'modern-compiler',
implementation: options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
sourceMap: !!options.sourceMap,
sassOptions: {
fiber: false,
// bootstrap-sass requires a minimum precision of 8
precision: 8,
includePaths,
...(sassOptions ?? {}),
},
},
},
],
},
{
test: /\.less$/,
include: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForGlobalStyle)(options, includePaths),
{
loader: require.resolve('less-loader'),
options: {
sourceMap: !!options.sourceMap,
lessOptions: {
javascriptEnabled: true,
...lessPathOptions,
...(lessOptions ?? {}),
},
},
},
],
},
{
test: /\.styl$/,
include: globalStylePaths,
use: [
...(0, stylesheet_loaders_1.getCommonLoadersForGlobalStyle)(options, includePaths),
{
loader: (0, path_1.join)(__dirname, '../../../utils/webpack/deprecated-stylus-loader.js'),
options: {
sourceMap: !!options.sourceMap,
stylusOptions: {
include: includePaths,
},
},
},
],
},
];
const rules = [
{
test: /\.css$|\.scss$|\.sass$|\.less$|\.styl$/,
oneOf: [...cssModuleRules, ...globalCssRules, ...globalStyleRules],
},
];
if (options.extractCss) {
plugins.push(
// extract global css from js files into own css file
new core_1.CssExtractRspackPlugin({
filename: `[name]${hashFormat.extract}.css`,
}));
}
config.output = {
...(config.output ?? {}),
assetModuleFilename: '[name].[contenthash:16][ext]',
crossOriginLoading: 'anonymous',
};
// In case users customize their rspack config with unsupported entry.
if (typeof config.entry === 'function')
throw new Error('Entry function is not supported. Use an object.');
if (typeof config.entry === 'string')
throw new Error('Entry string is not supported. Use an object.');
if (Array.isArray(config.entry))
throw new Error('Entry array is not supported. Use an object.');
Object.entries(entries).forEach(([entryName, entryData]) => {
if (useNormalizedEntry) {
config.entry[entryName] = { import: entryData.import };
}
else {
config.entry[entryName] = entryData.import;
}
});
config.optimization = !isProd
? {}
: {
...(config.optimization ?? {}),
minimizer: [...(config.optimization?.minimizer ?? []), ...minimizer],
emitOnErrors: false,
moduleIds: 'deterministic',
runtimeChunk: options.runtimeChunk ? { name: 'runtime' } : false,
splitChunks: {
defaultSizeTypes: config.optimization?.splitChunks !== false
? config.optimization?.splitChunks?.defaultSizeTypes
: ['...'],
maxAsyncRequests: Infinity,
cacheGroups: {
default: !!options.commonChunk && {
chunks: 'async',
minChunks: 2,
priority: 10,
},
common: !!options.commonChunk && {
name: 'common',
chunks: 'async',
minChunks: 2,
enforce: true,
priority: 5,
},
vendors: false,
vendor: !!options.vendorChunk && {
name: 'vendor',
chunks: (chunk) => chunk.name === 'main',
enforce: true,
test: /[\\/]node_modules[\\/]/,
},
},
},
};
config.resolve.mainFields = ['browser', 'module', 'main'];
config.module = {
...(config.module ?? {}),
rules: [
...(config.module.rules ?? []),
// Images: Inline small images, and emit a separate file otherwise.
{
test: /\.(avif|bmp|gif|ico|jpe?g|png|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10_000, // 10 kB
},
},
},
// SVG: same as image but we need to separate it so it can be swapped for SVGR in the React plugin.
{
test: /\.svg$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10_000, // 10 kB
},
},
},
// Fonts: Emit separate file and export the URL.
{
test: /\.(eot|otf|ttf|woff|woff2)$/,
type: 'asset/resource',
},
...rules,
],
};
config.plugins ??= [];
config.plugins.push(...plugins);
}
function getClientEnvironment(mode) {
// Grab NODE_ENV and NX_PUBLIC_* environment variables and prepare them to be
// injected into the application via DefinePlugin in rspack configuration.
const nxPublicKeyRegex = /^NX_PUBLIC_/i;
const raw = Object.keys(process.env)
.filter((key) => nxPublicKeyRegex.test(key))
.reduce((env, key) => {
env[key] = process.env[key];
return env;
}, {});
// Stringify all values so we can feed into rspack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
return { stringified };
}