-
Notifications
You must be signed in to change notification settings - Fork 373
Update to Shakapacker 9.1.0 and migrate to Rspack #680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
a951f4c
879d171
087ec70
5d85f15
3fe61f0
fbc5781
76921b8
012b0b7
1685fb4
28014b2
3da3dfc
71b934a
752919b
4c761bb
431a8ee
2e03f56
5f92988
0ab9eac
a32ebff
84311cc
660aab3
2bdc624
2af9d6f
4d9d19e
13449f0
b7171e5
8bae4aa
ab8bd51
921844d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // Default locale and messages for i18n | ||
| export const defaultLocale = 'en'; | ||
|
|
||
| export const defaultMessages = { | ||
| 'app.name': 'React Webpack Rails Tutorial', | ||
| 'comment.form.name_label': 'Name', | ||
| 'comment.form.text_label': 'Text', | ||
| 'comment.form.submit': 'Submit', | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| // Translation messages for different locales | ||
| export const translations = { | ||
| en: { | ||
| 'app.name': 'React Webpack Rails Tutorial', | ||
| 'comment.form.name_label': 'Name', | ||
| 'comment.form.text_label': 'Text', | ||
| 'comment.form.submit': 'Submit', | ||
| }, | ||
| es: { | ||
| 'app.name': 'Tutorial de React Webpack Rails', | ||
| 'comment.form.name_label': 'Nombre', | ||
| 'comment.form.text_label': 'Texto', | ||
| 'comment.form.submit': 'Enviar', | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| const { resolve } = require('path'); | ||
|
|
||
| module.exports = { | ||
| resolve: { | ||
| alias: { | ||
| Assets: resolve(__dirname, '..', '..', 'client', 'app', 'assets'), | ||
| }, | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| const rspack = require('@rspack/core'); | ||
| const commonRspackConfig = require('./commonRspackConfig'); | ||
|
|
||
| const configureClient = () => { | ||
| const clientConfig = commonRspackConfig(); | ||
|
|
||
| clientConfig.plugins.push( | ||
| new rspack.ProvidePlugin({ | ||
| $: 'jquery', | ||
| jQuery: 'jquery', | ||
| ActionCable: '@rails/actioncable', | ||
| }), | ||
| ); | ||
|
|
||
| // server-bundle is special and should ONLY be built by the serverConfig | ||
| // In case this entry is not deleted, a very strange "window" not found | ||
| // error shows referring to window["webpackJsonp"]. That is because the | ||
| // client config is going to try to load chunks. | ||
| delete clientConfig.entry['server-bundle']; | ||
|
|
||
| return clientConfig; | ||
| }; | ||
|
|
||
| module.exports = configureClient; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| // Common configuration applying to client and server configuration | ||
| const { generateWebpackConfig, merge } = require('shakapacker'); | ||
|
|
||
| const baseClientRspackConfig = generateWebpackConfig(); | ||
| const commonOptions = { | ||
| resolve: { | ||
| extensions: ['.css', '.ts', '.tsx'], | ||
| }, | ||
| }; | ||
|
|
||
| // add sass resource loader | ||
| const sassLoaderConfig = { | ||
| loader: 'sass-resources-loader', | ||
| options: { | ||
| resources: './client/app/assets/styles/app-variables.scss', | ||
| }, | ||
| }; | ||
|
|
||
| const ignoreWarningsConfig = { | ||
| ignoreWarnings: [/Module not found: Error: Can't resolve 'react-dom\/client'/], | ||
| }; | ||
|
|
||
| const scssConfigIndex = baseClientRspackConfig.module.rules.findIndex((config) => | ||
| '.scss'.match(config.test) && config.use, | ||
| ); | ||
|
|
||
| if (scssConfigIndex === -1) { | ||
| console.warn('No SCSS rule with use array found in rspack config'); | ||
| } else { | ||
| // Configure sass-loader to use the modern API | ||
| const scssRule = baseClientRspackConfig.module.rules[scssConfigIndex]; | ||
| const sassLoaderIndex = scssRule.use.findIndex((loader) => { | ||
| if (typeof loader === 'string') { | ||
| return loader.includes('sass-loader'); | ||
| } | ||
| return loader.loader && loader.loader.includes('sass-loader'); | ||
| }); | ||
|
|
||
| if (sassLoaderIndex !== -1) { | ||
| const sassLoader = scssRule.use[sassLoaderIndex]; | ||
| if (typeof sassLoader === 'string') { | ||
| scssRule.use[sassLoaderIndex] = { | ||
| loader: sassLoader, | ||
| options: { | ||
| api: 'modern' | ||
| } | ||
| }; | ||
| } else { | ||
| sassLoader.options = sassLoader.options || {}; | ||
| sassLoader.options.api = 'modern'; | ||
| } | ||
| } | ||
|
|
||
| // Fix css-loader configuration for CSS modules if namedExport is enabled | ||
| // When namedExport is true, exportLocalsConvention must be camelCaseOnly or dashesOnly | ||
| const cssLoader = scssRule.use.find((loader) => { | ||
| const loaderName = typeof loader === 'string' ? loader : loader?.loader; | ||
| return loaderName?.includes('css-loader'); | ||
| }); | ||
|
|
||
| if (cssLoader?.options?.modules?.namedExport) { | ||
| cssLoader.options.modules.exportLocalsConvention = 'camelCaseOnly'; | ||
| } | ||
|
|
||
| baseClientRspackConfig.module.rules[scssConfigIndex].use.push(sassLoaderConfig); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix Prettier violations to unblock lint.
🧰 Tools🪛 ESLint[error] 23-24: Replace (prettier/prettier) [error] 45-45: Insert (prettier/prettier) [error] 46-46: Insert (prettier/prettier) 🤖 Prompt for AI Agents |
||
|
|
||
| // Copy the object using merge b/c the baseClientRspackConfig and commonOptions are mutable globals | ||
| const commonRspackConfig = () => merge({}, baseClientRspackConfig, commonOptions, ignoreWarningsConfig); | ||
|
|
||
| module.exports = commonRspackConfig; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| process.env.NODE_ENV = process.env.NODE_ENV || 'development'; | ||
|
|
||
| const { devServer, inliningCss } = require('shakapacker'); | ||
|
|
||
| const rspackConfig = require('./rspackConfig'); | ||
|
|
||
| const developmentEnvOnly = (clientRspackConfig, _serverRspackConfig) => { | ||
| // plugins | ||
| if (inliningCss) { | ||
| // Note, when this is run, we're building the server and client bundles in separate processes. | ||
| // Thus, this plugin is not applied to the server bundle. | ||
|
|
||
| // eslint-disable-next-line global-require | ||
| const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); | ||
| clientRspackConfig.plugins.push( | ||
| new ReactRefreshWebpackPlugin({ | ||
| overlay: { | ||
| sockPort: devServer.port, | ||
| }, | ||
| }), | ||
| ); | ||
| } | ||
| }; | ||
|
|
||
| module.exports = rspackConfig(developmentEnvOnly); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| process.env.NODE_ENV = process.env.NODE_ENV || 'production'; | ||
|
|
||
| const rspackConfig = require('./rspackConfig'); | ||
|
|
||
| const productionEnvOnly = (_clientRspackConfig, _serverRspackConfig) => { | ||
| // place any code here that is for production only | ||
| }; | ||
|
|
||
| module.exports = rspackConfig(productionEnvOnly); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| const { env, generateWebpackConfig } = require('shakapacker'); | ||
| const { existsSync } = require('fs'); | ||
| const { resolve } = require('path'); | ||
|
|
||
| const envSpecificConfig = () => { | ||
| const path = resolve(__dirname, `${env.nodeEnv}.js`); | ||
| if (existsSync(path)) { | ||
| console.log(`Loading ENV specific rspack configuration file ${path}`); | ||
| return require(path); | ||
| } | ||
|
|
||
| return generateWebpackConfig(); | ||
| }; | ||
|
|
||
| module.exports = envSpecificConfig(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| const clientRspackConfig = require('./clientRspackConfig'); | ||
| const serverRspackConfig = require('./serverRspackConfig'); | ||
|
|
||
| const rspackConfig = (envSpecific) => { | ||
| const clientConfig = clientRspackConfig(); | ||
| const serverConfig = serverRspackConfig(); | ||
|
|
||
| if (envSpecific) { | ||
| envSpecific(clientConfig, serverConfig); | ||
| } | ||
|
|
||
| let result; | ||
| // For HMR, need to separate the the client and server rspack configurations | ||
| if (process.env.WEBPACK_SERVE || process.env.CLIENT_BUNDLE_ONLY) { | ||
| // eslint-disable-next-line no-console | ||
| console.log('[React on Rails] Creating only the client bundles.'); | ||
| result = clientConfig; | ||
| } else if (process.env.SERVER_BUNDLE_ONLY) { | ||
| // eslint-disable-next-line no-console | ||
| console.log('[React on Rails] Creating only the server bundle.'); | ||
| result = serverConfig; | ||
| } else { | ||
| // default is the standard client and server build | ||
| // eslint-disable-next-line no-console | ||
| console.log('[React on Rails] Creating both client and server bundles.'); | ||
| result = [clientConfig, serverConfig]; | ||
| } | ||
|
|
||
| // To debug, uncomment next line and inspect "result" | ||
| // debugger | ||
| return result; | ||
| }; | ||
|
|
||
| module.exports = rspackConfig; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const path = require('path'); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const { config } = require('shakapacker'); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const commonRspackConfig = require('./commonRspackConfig'); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const rspack = require('@rspack/core'); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix import order (ESLint import/order). Place external packages before local modules. Apply this diff: const path = require('path');
const { config } = require('shakapacker');
-const commonRspackConfig = require('./commonRspackConfig');
-
-const rspack = require('@rspack/core');
+const rspack = require('@rspack/core');
+const commonRspackConfig = require('./commonRspackConfig');
const configureServer = () => {📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 5-5: (import/order) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| const configureServer = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // We need to use "merge" because the clientConfigObject, EVEN after running | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // toRspackConfig() is a mutable GLOBAL. Thus any changes, like modifying the | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // entry value will result in changing the client config! | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Using merge into an empty object avoids this issue. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const serverRspackConfig = commonRspackConfig(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // We just want the single server bundle entry | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const serverEntry = { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| 'server-bundle': serverRspackConfig.entry['server-bundle'], | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!serverEntry['server-bundle']) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "Create a pack with the file name 'server-bundle.js' containing all the server rendering files", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| serverRspackConfig.entry = serverEntry; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Remove the mini-css-extract-plugin from the style loaders because | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // the client build will handle exporting CSS. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // replace file-loader with null-loader | ||||||||||||||||||||||||||||||||||||||||||||||||||
| serverRspackConfig.module.rules.forEach((loader) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (loader.use && loader.use.filter) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| loader.use = loader.use.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| (item) => !(typeof item === 'string' && item.match(/mini-css-extract-plugin/)), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Remove duplicate mini-css-extract-plugin filtering. This filtering logic is duplicated at lines 67-107, where it's handled more comprehensively along with other SSR-specific loader adjustments. The duplicate code reduces maintainability without adding value. Apply this diff: serverRspackConfig.entry = serverEntry;
- // Remove the mini-css-extract-plugin from the style loaders because
- // the client build will handle exporting CSS.
- // replace file-loader with null-loader
- serverRspackConfig.module.rules.forEach((loader) => {
- if (loader.use && loader.use.filter) {
- loader.use = loader.use.filter(
- (item) => !(typeof item === 'string' && item.match(/mini-css-extract-plugin/)),
- );
- }
- });
-
// No splitting of chunks for a server bundle📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 32-32: Assignment to property of function parameter 'loader'. (no-param-reassign) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // No splitting of chunks for a server bundle | ||||||||||||||||||||||||||||||||||||||||||||||||||
| serverRspackConfig.optimization = { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| minimize: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| serverRspackConfig.plugins.unshift(new rspack.optimize.LimitChunkCountPlugin({ maxChunks: 1 })); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Custom output for the server-bundle that matches the config in | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // config/initializers/react_on_rails.rb | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Output to a private directory for SSR bundles (not in public/) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Using the default React on Rails path: ssr-generated | ||||||||||||||||||||||||||||||||||||||||||||||||||
| serverRspackConfig.output = { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| filename: 'server-bundle.js', | ||||||||||||||||||||||||||||||||||||||||||||||||||
| globalObject: 'this', | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // If using the React on Rails Pro node server renderer, uncomment the next line | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // libraryTarget: 'commonjs2', | ||||||||||||||||||||||||||||||||||||||||||||||||||
| path: path.resolve(__dirname, '../../ssr-generated'), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| publicPath: config.publicPath, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // https://rspack.dev/config/output#outputglobalobject | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Don't hash the server bundle b/c would conflict with the client manifest | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // And no need for the MiniCssExtractPlugin | ||||||||||||||||||||||||||||||||||||||||||||||||||
| serverRspackConfig.plugins = serverRspackConfig.plugins.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| (plugin) => | ||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.constructor.name !== 'WebpackAssetsManifest' && | ||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.constructor.name !== 'MiniCssExtractPlugin' && | ||||||||||||||||||||||||||||||||||||||||||||||||||
| plugin.constructor.name !== 'ForkTsCheckerWebpackPlugin', | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Configure loader rules for SSR | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Remove the mini-css-extract-plugin from the style loaders because | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // the client build will handle exporting CSS. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // replace file-loader with null-loader | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const rules = serverRspackConfig.module.rules; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| rules.forEach((rule) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Array.isArray(rule.use)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // remove the mini-css-extract-plugin and style-loader | ||||||||||||||||||||||||||||||||||||||||||||||||||
| rule.use = rule.use.filter((item) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let testValue; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof item === 'string') { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| testValue = item; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (typeof item.loader === 'string') { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| testValue = item.loader; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return !(testValue.match(/mini-css-extract-plugin/) || testValue === 'style-loader'); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const cssLoader = rule.use.find((item) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let testValue; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof item === 'string') { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| testValue = item; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (typeof item.loader === 'string') { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| testValue = item.loader; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return testValue.includes('css-loader'); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (cssLoader && cssLoader.options && cssLoader.options.modules) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against undefined when detecting css-loader. Avoid potential TypeError if testValue is not set. Apply this diff: - return testValue.includes('css-loader');
+ return typeof testValue === 'string' && testValue.includes('css-loader');📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Preserve existing modules config but add exportOnlyLocals for SSR | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cssLoader.options.modules = { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ...cssLoader.options.modules, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exportOnlyLocals: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Skip writing image files during SSR by setting emitFile to false | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (rule.use && (rule.use.loader === 'url-loader' || rule.use.loader === 'file-loader')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| rule.use.options.emitFile = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add null safety check for rule.use.options. When Apply this diff: // Skip writing image files during SSR by setting emitFile to false
} else if (rule.use && (rule.use.loader === 'url-loader' || rule.use.loader === 'file-loader')) {
- rule.use.options.emitFile = false;
+ if (rule.use.options) {
+ rule.use.options.emitFile = false;
+ }
}📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 105-105: Assignment to property of function parameter 'rule'. (no-param-reassign) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // eval works well for the SSR bundle because it's the fastest and shows | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // lines in the server bundle which is good for debugging SSR | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // The default of cheap-module-source-map is slow and provides poor info. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| serverRspackConfig.devtool = 'eval'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // If using the default 'web', then libraries like Emotion and loadable-components | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // break with SSR. The fix is to use a node renderer and change the target. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // If using the React on Rails Pro node server renderer, uncomment the next line | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // serverRspackConfig.target = 'node' | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return serverRspackConfig; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| module.exports = configureServer; | ||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Eliminate code duplication with
defaultMessages.The English translations here duplicate
defaultMessagesfromclient/app/libs/i18n/default.js. This violates the DRY principle and creates a maintenance burden—updating English text requires changes in two places.Apply this refactor to import and reuse
defaultMessages:📝 Committable suggestion
🤖 Prompt for AI Agents