UNPKG

@nx/rspack

Version:

The Nx Plugin for Rspack contains executors and generators that support building applications using Rspack.

414 lines (403 loc) • 19.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = addSvgrToRspackConfig; const tslib_1 = require("tslib"); const internal_1 = require("@nx/devkit/internal"); const devkit_1 = require("@nx/devkit"); const tsquery_1 = require("@phenomnomnominal/tsquery"); const ts = tslib_1.__importStar(require("typescript")); const withSvgrFunctionForWithReact = ` // SVGR support function (migrated from svgr option in withReact/NxReactRspackPlugin) function withSvgr(svgrOptions = {}) { const defaultOptions = { svgo: false, titleProp: true, ref: true, }; const options = { ...defaultOptions, ...svgrOptions }; return function configure(config) { // Remove existing SVG loader if present const svgLoaderIdx = config.module.rules.findIndex( (rule) => typeof rule === 'object' && typeof rule.test !== 'undefined' && rule.test.toString().includes('svg') ); if (svgLoaderIdx !== -1) { config.module.rules.splice(svgLoaderIdx, 1); } config.module.rules.push( { test: /\\.svg$/i, type: 'asset', resourceQuery: /url/, // *.svg?url }, { test: /\\.svg$/i, issuer: /\\.[jt]sx?$/, resourceQuery: { not: [/url/] }, use: [{ loader: '@svgr/webpack', options }], } ); return config; }; } `; const withSvgrFunctionForNxReactRspackPlugin = ` // SVGR support function (migrated from svgr option in withReact/NxReactRspackPlugin) function withSvgr(svgrOptions = {}) { const defaultOptions = { svgo: false, titleProp: true, ref: true, }; const options = { ...defaultOptions, ...svgrOptions }; return (config) => { config.plugins.push({ apply: (compiler) => { // Remove ALL existing SVG loaders compiler.options.module.rules = compiler.options.module.rules.filter( (rule) => !( rule && typeof rule === 'object' && rule.test && rule.test.toString().includes('svg') ) ); compiler.options.module.rules.push( { test: /\\.svg$/i, type: 'asset', resourceQuery: /url/, }, { test: /\\.svg$/i, issuer: /\\.[jt]sx?$/, resourceQuery: { not: [/url/] }, use: [{ loader: '@svgr/webpack', options }], } ); }, }); return config; }; } `; async function addSvgrToRspackConfig(tree) { const projects = new Map(); // Find all React rspack projects using either withReact OR NxReactRspackPlugin (0, internal_1.forEachExecutorOptions)(tree, '@nx/rspack:rspack', (options, project, target) => { if (!options.rspackConfig) return; const rspackConfigPath = options.rspackConfig; if (!tree.exists(rspackConfigPath)) return; const content = tree.read(rspackConfigPath, 'utf-8'); const sourceFile = (0, tsquery_1.ast)(content); // Check if this is a withReact setup if (content.includes('withReact')) { const withReactCalls = (0, tsquery_1.query)(sourceFile, 'CallExpression[expression.name=withReact]'); if (withReactCalls.length > 0) { const callExpr = withReactCalls[0]; if (callExpr.arguments.length === 0) return; const arg = callExpr.arguments[0]; if (!ts.isObjectLiteralExpression(arg)) return; const svgrProp = arg.properties.find((prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'svgr'); if (svgrProp) { let svgrValue; if (ts.isObjectLiteralExpression(svgrProp.initializer)) { svgrValue = {}; for (const prop of svgrProp.initializer.properties) { if (!ts.isPropertyAssignment(prop)) continue; if (!ts.isIdentifier(prop.name)) continue; const key = prop.name.text; if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) { svgrValue[key] = true; } else if (prop.initializer.kind === ts.SyntaxKind.FalseKeyword) { svgrValue[key] = false; } } } else { svgrValue = svgrProp.initializer.kind === ts.SyntaxKind.TrueKeyword; } projects.set(rspackConfigPath, { svgrOptions: svgrValue, isWithReact: true, }); } } } // Otherwise check if this is NxReactRspackPlugin setup else if (content.includes('NxReactRspackPlugin')) { const pluginCalls = (0, tsquery_1.query)(sourceFile, 'NewExpression[expression.name=NxReactRspackPlugin]'); if (pluginCalls.length > 0) { const newExpr = pluginCalls[0]; if (!newExpr.arguments || newExpr.arguments.length === 0) return; const arg = newExpr.arguments[0]; if (!ts.isObjectLiteralExpression(arg)) return; const svgrProp = arg.properties.find((prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'svgr'); if (svgrProp) { let svgrValue; if (ts.isObjectLiteralExpression(svgrProp.initializer)) { svgrValue = {}; for (const prop of svgrProp.initializer.properties) { if (!ts.isPropertyAssignment(prop)) continue; if (!ts.isIdentifier(prop.name)) continue; const key = prop.name.text; if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) { svgrValue[key] = true; } else if (prop.initializer.kind === ts.SyntaxKind.FalseKeyword) { svgrValue[key] = false; } } } else if (svgrProp.initializer.kind === ts.SyntaxKind.TrueKeyword) { svgrValue = true; } else if (svgrProp.initializer.kind === ts.SyntaxKind.FalseKeyword) { svgrValue = false; } // Add to projects if svgr is explicitly set if (svgrValue !== undefined) { projects.set(rspackConfigPath, { svgrOptions: svgrValue, isWithReact: false, }); } } } } }); if (projects.size === 0) return; // Update rspack configs to add withSvgr function inline for (const [rspackConfigPath, config] of projects.entries()) { let content = tree.read(rspackConfigPath, 'utf-8'); const sourceFile = (0, tsquery_1.ast)(content); const changes = []; // Build the svgr options for this specific config let svgrOptionsStr = ''; if (config.svgrOptions) { const importStatements = (0, tsquery_1.query)(sourceFile, 'ImportDeclaration'); const requireStatements = (0, tsquery_1.query)(sourceFile, 'VariableStatement:has(CallExpression[expression.name=require])'); const allImportRequires = [ ...importStatements, ...requireStatements, ].sort((a, b) => a.getEnd() - b.getEnd()); const lastImportOrRequire = allImportRequires[allImportRequires.length - 1]; if (config.svgrOptions === true || config.svgrOptions === undefined) { svgrOptionsStr = ''; } else if (typeof config.svgrOptions === 'object') { const options = Object.entries(config.svgrOptions) .map(([key, value]) => ` ${key}: ${value}`) .join(',\n'); svgrOptionsStr = `{\n${options}\n}`; } if (lastImportOrRequire) { changes.push({ type: devkit_1.ChangeType.Insert, index: lastImportOrRequire.getEnd(), text: config.isWithReact ? withSvgrFunctionForWithReact : withSvgrFunctionForNxReactRspackPlugin, }); } else { changes.push({ type: devkit_1.ChangeType.Insert, index: 0, text: (config.isWithReact ? withSvgrFunctionForWithReact : withSvgrFunctionForNxReactRspackPlugin) + '\n', }); } } // Remove svgr option based on the style (withReact OR NxReactRspackPlugin) if (config.isWithReact) { const withReactCalls = (0, tsquery_1.query)(sourceFile, 'CallExpression[expression.name=withReact]'); if (withReactCalls.length > 0) { const callExpr = withReactCalls[0]; if (callExpr.arguments.length > 0) { const arg = callExpr.arguments[0]; if (ts.isObjectLiteralExpression(arg)) { const svgrProp = arg.properties.find((prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'svgr'); if (svgrProp) { const hasOnlySvgrProperty = arg.properties.length === 1; if (hasOnlySvgrProperty) { changes.push({ type: devkit_1.ChangeType.Delete, start: arg.getStart(), length: arg.getEnd() - arg.getStart(), }); } else { const propIndex = arg.properties.indexOf(svgrProp); const isLastProp = propIndex === arg.properties.length - 1; const isFirstProp = propIndex === 0; let removeStart = svgrProp.getFullStart(); let removeEnd = svgrProp.getEnd(); if (!isLastProp) { const nextProp = arg.properties[propIndex + 1]; removeEnd = nextProp.getFullStart(); } else if (!isFirstProp) { const prevProp = arg.properties[propIndex - 1]; const textBetween = content.substring(prevProp.getEnd(), svgrProp.getFullStart()); const commaIndex = textBetween.indexOf(','); if (commaIndex !== -1) { removeStart = prevProp.getEnd() + commaIndex; } } changes.push({ type: devkit_1.ChangeType.Delete, start: removeStart, length: removeEnd - removeStart, }); } if (config.svgrOptions) { const composePluginsCalls = (0, tsquery_1.query)(sourceFile, 'CallExpression[expression.name=composePlugins]'); if (composePluginsCalls.length > 0) { const composeCall = composePluginsCalls[0]; let svgrCallStr = ''; if (config.svgrOptions === true || config.svgrOptions === undefined) { svgrCallStr = 'withSvgr()'; } else if (typeof config.svgrOptions === 'object') { svgrCallStr = `withSvgr(${svgrOptionsStr})`; } const withReactIdx = composeCall.arguments.findIndex((arg) => arg.getText().includes('withReact')); // Insert withSvgr as the last argument before the closing paren const argToInsertAfter = composeCall.arguments[withReactIdx]; if (argToInsertAfter) { changes.push({ type: devkit_1.ChangeType.Insert, index: argToInsertAfter.getEnd(), text: `, ${svgrCallStr}`, }); } } } } } } } } else { // Remove svgr option from first NxReactRspackPlugin call const pluginCalls = (0, tsquery_1.query)(sourceFile, 'NewExpression[expression.name=NxReactRspackPlugin]'); if (pluginCalls.length > 0) { const newExpr = pluginCalls[0]; if (newExpr.arguments && newExpr.arguments.length > 0) { const arg = newExpr.arguments[0]; if (ts.isObjectLiteralExpression(arg)) { const svgrProp = arg.properties.find((prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'svgr'); if (svgrProp) { const hasOnlySvgrProperty = arg.properties.length === 1; if (hasOnlySvgrProperty) { changes.push({ type: devkit_1.ChangeType.Delete, start: arg.getStart(), length: arg.getEnd() - arg.getStart(), }); } else { const propIndex = arg.properties.indexOf(svgrProp); const isLastProp = propIndex === arg.properties.length - 1; const isFirstProp = propIndex === 0; let removeStart = svgrProp.getFullStart(); let removeEnd = svgrProp.getEnd(); if (!isLastProp) { const nextProp = arg.properties[propIndex + 1]; removeEnd = nextProp.getFullStart(); } else if (!isFirstProp) { const prevProp = arg.properties[propIndex - 1]; const textBetween = content.substring(prevProp.getEnd(), svgrProp.getFullStart()); const commaIndex = textBetween.indexOf(','); if (commaIndex !== -1) { removeStart = prevProp.getEnd() + commaIndex; } } changes.push({ type: devkit_1.ChangeType.Delete, start: removeStart, length: removeEnd - removeStart, }); } } } } } // For NxReactRspackPlugin style, wrap the entire module.exports or export default with withSvgr if (config.svgrOptions) { const allAssignments = (0, tsquery_1.query)(sourceFile, 'BinaryExpression'); const moduleExportsAssignment = allAssignments.find((node) => { const binaryExpr = node; const left = binaryExpr.left; return (ts.isPropertyAccessExpression(left) && ts.isIdentifier(left.expression) && left.expression.text === 'module' && ts.isIdentifier(left.name) && left.name.text === 'exports'); }); const exportDefaultStatements = (0, tsquery_1.query)(sourceFile, 'ExportAssignment'); const exportDefaultStatement = exportDefaultStatements[0]; let exportValue; if (moduleExportsAssignment) { exportValue = moduleExportsAssignment.right; } else if (exportDefaultStatement) { exportValue = exportDefaultStatement.expression; } if (exportValue) { let svgrCallStr = ''; if (config.svgrOptions === true || config.svgrOptions === undefined) { svgrCallStr = 'withSvgr()'; } else if (typeof config.svgrOptions === 'object') { const options = Object.entries(config.svgrOptions) .map(([key, value]) => ` ${key}: ${value}`) .join(',\n'); svgrCallStr = `withSvgr({\n${options}\n})`; } changes.push({ type: devkit_1.ChangeType.Insert, index: exportValue.getStart(), text: `${svgrCallStr}(`, }); changes.push({ type: devkit_1.ChangeType.Insert, index: exportValue.getEnd(), text: ')', }); } } } content = (0, devkit_1.applyChangesToString)(content, changes); tree.write(rspackConfigPath, content); } await (0, devkit_1.formatFiles)(tree); }