@modern-js/app-tools 3.9.2 → 3.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/cjs/commands/build.js +1 -2
  2. package/dist/cjs/commands/deploy.js +1 -3
  3. package/dist/cjs/commands/index.js +4 -0
  4. package/dist/cjs/index.js +15 -39
  5. package/dist/cjs/plugins/analyze/index.js +2 -2
  6. package/dist/cjs/plugins/analyze/utils.js +2 -3
  7. package/dist/cjs/plugins/initialize/index.js +3 -4
  8. package/dist/esm/commands/build.mjs +1 -2
  9. package/dist/esm/commands/deploy.mjs +1 -3
  10. package/dist/esm/commands/index.mjs +4 -0
  11. package/dist/esm/index.mjs +2 -10
  12. package/dist/esm/plugins/analyze/index.mjs +2 -2
  13. package/dist/esm/plugins/analyze/utils.mjs +2 -3
  14. package/dist/esm/plugins/initialize/index.mjs +3 -4
  15. package/dist/esm-node/commands/build.mjs +1 -2
  16. package/dist/esm-node/commands/deploy.mjs +1 -3
  17. package/dist/esm-node/commands/index.mjs +4 -0
  18. package/dist/esm-node/index.mjs +2 -10
  19. package/dist/esm-node/plugins/analyze/index.mjs +2 -2
  20. package/dist/esm-node/plugins/analyze/utils.mjs +2 -3
  21. package/dist/esm-node/plugins/initialize/index.mjs +3 -4
  22. package/dist/types/index.d.ts +1 -3
  23. package/dist/types/plugins/analyze/utils.d.ts +1 -1
  24. package/docs/configure/app/output/disable-svgr.md +4 -0
  25. package/docs/configure/app/output/svg-default-export.md +4 -0
  26. package/docs/configure/app/tools/less.md +94 -19
  27. package/docs/configure/app/tools/sass.md +87 -18
  28. package/docs/configure/app/tools/svgr.md +89 -0
  29. package/docs/guides/basic-features/static-assets/svg-assets.md +29 -17
  30. package/docs/llms.txt +3 -2
  31. package/docs/plugin/introduction.md +10 -10
  32. package/package.json +16 -16
@@ -98,10 +98,9 @@ const build = async (api, options)=>{
98
98
  utils_namespaceObject.logger.info('Starting production build...');
99
99
  if (!appContext.builder) throw new Error('Expect the Builder to have been initialized, But the appContext.builder received `undefined`');
100
100
  await appContext.builder.onAfterBuild(async ()=>copyEnvFiles(appContext.appDirectory, appContext.distDirectory));
101
- const buildResult = await appContext.builder.build({
101
+ await appContext.builder.build({
102
102
  watch: options?.watch
103
103
  });
104
- if (!options?.watch) await buildResult?.close();
105
104
  };
106
105
  exports.build = __webpack_exports__.build;
107
106
  for(var __rspack_i in __webpack_exports__)if (-1 === [
@@ -31,12 +31,10 @@ __webpack_require__.d(__webpack_exports__, {
31
31
  deploy: ()=>deploy
32
32
  });
33
33
  const loadPlugins_js_namespaceObject = require("../utils/loadPlugins.js");
34
- const external_build_js_namespaceObject = require("./build.js");
35
34
  const deploy = async (api, options = {})=>{
36
35
  const hooks = api.getHooks();
37
36
  const { metaName } = api.getAppContext();
38
- if (options.skipBuild) await (0, loadPlugins_js_namespaceObject.getServerPlugins)(api, metaName);
39
- else await (0, external_build_js_namespaceObject.build)(api);
37
+ await (0, loadPlugins_js_namespaceObject.getServerPlugins)(api, metaName);
40
38
  await hooks.onBeforeDeploy.call(options);
41
39
  await hooks.deploy.call();
42
40
  await hooks.onAfterDeploy.call(options);
@@ -56,6 +56,10 @@ const serverCommand = (program, api)=>{
56
56
  };
57
57
  const deployCommand = (program, api)=>{
58
58
  program.command('deploy').usage('[options]').option('-c --config <config>', index_js_namespaceObject.i18n.t(index_js_namespaceObject.localeKeys.command.shared.config)).option('-s --skip-build', index_js_namespaceObject.i18n.t(index_js_namespaceObject.localeKeys.command.shared.skipBuild)).description(index_js_namespaceObject.i18n.t(index_js_namespaceObject.localeKeys.command.deploy.describe)).action(async (options)=>{
59
+ if (!options.skipBuild) {
60
+ const { build } = await import("./build.js");
61
+ await build(api);
62
+ }
59
63
  const { deploy } = await import("./deploy.js");
60
64
  await deploy(api, options);
61
65
  process.exit(0);
package/dist/cjs/index.js CHANGED
@@ -1,11 +1,5 @@
1
1
  "use strict";
2
2
  var __webpack_modules__ = {
3
- "./commands/build" (module) {
4
- module.exports = require("./commands/build.js");
5
- },
6
- "./commands/deploy" (module) {
7
- module.exports = require("./commands/deploy.js");
8
- },
9
3
  "./commands/dev" (module) {
10
4
  module.exports = require("./commands/dev.js");
11
5
  },
@@ -155,21 +149,17 @@ var __webpack_exports__ = {};
155
149
  "serve",
156
150
  "generateWatchFiles",
157
151
  "initAppContext",
158
- "deploy",
159
152
  "defineConfig",
160
- "build",
161
153
  "closeServer"
162
154
  ].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_defineConfig__rspack_import_16[__rspack_import_key];
163
155
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
164
- var _commands_build__rspack_import_17 = __webpack_require__("./commands/build");
165
- var _commands_deploy__rspack_import_18 = __webpack_require__("./commands/deploy");
166
- var _commands_dev__rspack_import_19 = __webpack_require__("./commands/dev");
167
- var _commands_serve__rspack_import_20 = __webpack_require__("./commands/serve");
168
- var _utils_createServer__rspack_import_21 = __webpack_require__("./utils/createServer");
169
- var _rsbuild__rspack_import_22 = __webpack_require__("./rsbuild");
170
- var _types__rspack_import_23 = __webpack_require__("./types?6efa");
156
+ var _commands_dev__rspack_import_17 = __webpack_require__("./commands/dev");
157
+ var _commands_serve__rspack_import_18 = __webpack_require__("./commands/serve");
158
+ var _utils_createServer__rspack_import_19 = __webpack_require__("./utils/createServer");
159
+ var _rsbuild__rspack_import_20 = __webpack_require__("./rsbuild");
160
+ var _types__rspack_import_21 = __webpack_require__("./types?6efa");
171
161
  var __rspack_reexport = {};
172
- for(const __rspack_import_key in _types__rspack_import_23)if ([
162
+ for(const __rspack_import_key in _types__rspack_import_21)if ([
173
163
  "dev",
174
164
  "resolveModernRsbuildConfig",
175
165
  "mergeConfig",
@@ -178,13 +168,11 @@ var __webpack_exports__ = {};
178
168
  "serve",
179
169
  "generateWatchFiles",
180
170
  "initAppContext",
181
- "deploy",
182
171
  "defineConfig",
183
- "build",
184
172
  "closeServer"
185
- ].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_types__rspack_import_23[__rspack_import_key];
173
+ ].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_types__rspack_import_21[__rspack_import_key];
186
174
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
187
- var _modern_js_plugin_cli__rspack_import_24 = __webpack_require__("@modern-js/plugin/cli");
175
+ var _modern_js_plugin_cli__rspack_import_22 = __webpack_require__("@modern-js/plugin/cli");
188
176
  const appTools = ()=>({
189
177
  name: '@modern-js/app-tools',
190
178
  usePlugins: [
@@ -244,18 +232,12 @@ var __webpack_exports__ = {};
244
232
  ].some((tag)=>(0, _modern_js_utils__rspack_import_4.getArgv)().includes(tag));
245
233
  if (isSkipBuild) return;
246
234
  }
247
- const { command: contextCommand } = api.getAppContext();
248
- const shouldClean = [
235
+ if ([
249
236
  'dev',
250
237
  'start',
251
238
  'build',
252
239
  'deploy'
253
- ].includes(command) || [
254
- 'dev',
255
- 'start',
256
- 'build'
257
- ].includes(contextCommand);
258
- if (shouldClean) {
240
+ ].includes(command)) {
259
241
  const resolvedConfig = api.getNormalizedConfig();
260
242
  if (resolvedConfig.output.cleanDistPath) {
261
243
  const appContext = api.getAppContext();
@@ -295,27 +277,23 @@ var __webpack_exports__ = {};
295
277
  });
296
278
  const __rspack_default_export = appTools;
297
279
  __webpack_require__.d(__webpack_exports__, {
298
- build: ()=>_commands_build__rspack_import_17.build,
299
- closeServer: ()=>_utils_createServer__rspack_import_21.closeServer,
280
+ closeServer: ()=>_utils_createServer__rspack_import_19.closeServer,
300
281
  defineConfig: ()=>_defineConfig__rspack_import_16.defineConfig,
301
- deploy: ()=>_commands_deploy__rspack_import_18.deploy,
302
- dev: ()=>_commands_dev__rspack_import_19.dev,
282
+ dev: ()=>_commands_dev__rspack_import_17.dev,
303
283
  generateWatchFiles: ()=>_utils_generateWatchFiles__rspack_import_13.generateWatchFiles,
304
284
  initAppContext: ()=>_utils_initAppContext__rspack_import_14.initAppContext,
305
- mergeConfig: ()=>_modern_js_plugin_cli__rspack_import_24.mergeConfig,
306
- resolveModernRsbuildConfig: ()=>_rsbuild__rspack_import_22.resolveModernRsbuildConfig,
307
- serve: ()=>_commands_serve__rspack_import_20.serve
285
+ mergeConfig: ()=>_modern_js_plugin_cli__rspack_import_22.mergeConfig,
286
+ resolveModernRsbuildConfig: ()=>_rsbuild__rspack_import_20.resolveModernRsbuildConfig,
287
+ serve: ()=>_commands_serve__rspack_import_18.serve
308
288
  }, {
309
289
  appTools: appTools,
310
290
  default: __rspack_default_export
311
291
  });
312
292
  })();
313
293
  exports.appTools = __webpack_exports__.appTools;
314
- exports.build = __webpack_exports__.build;
315
294
  exports.closeServer = __webpack_exports__.closeServer;
316
295
  exports["default"] = __webpack_exports__["default"];
317
296
  exports.defineConfig = __webpack_exports__.defineConfig;
318
- exports.deploy = __webpack_exports__.deploy;
319
297
  exports.dev = __webpack_exports__.dev;
320
298
  exports.generateWatchFiles = __webpack_exports__.generateWatchFiles;
321
299
  exports.initAppContext = __webpack_exports__.initAppContext;
@@ -324,11 +302,9 @@ exports.resolveModernRsbuildConfig = __webpack_exports__.resolveModernRsbuildCon
324
302
  exports.serve = __webpack_exports__.serve;
325
303
  for(var __rspack_i in __webpack_exports__)if (-1 === [
326
304
  "appTools",
327
- "build",
328
305
  "closeServer",
329
306
  "default",
330
307
  "defineConfig",
331
- "deploy",
332
308
  "dev",
333
309
  "generateWatchFiles",
334
310
  "initAppContext",
@@ -55,7 +55,7 @@ const analyze = ()=>({
55
55
  const resolvedConfig = api.getNormalizedConfig();
56
56
  const hooks = api.getHooks();
57
57
  try {
58
- if ((0, external_utils_js_namespaceObject.checkIsBuildCommands)(appContext.command)) utils_namespaceObject.fs.emptydirSync(appContext.internalDirectory);
58
+ if ((0, external_utils_js_namespaceObject.checkIsBuildCommands)()) utils_namespaceObject.fs.emptydirSync(appContext.internalDirectory);
59
59
  } catch {}
60
60
  const apiOnly = await (0, utils_namespaceObject.isApiOnly)(appContext.appDirectory, resolvedConfig.source?.entriesDir, appContext.apiDirectory);
61
61
  const [{ getProdServerRoutes }] = await Promise.all([
@@ -149,7 +149,7 @@ const analyze = ()=>({
149
149
  htmlTemplates
150
150
  };
151
151
  api.updateAppContext(appContext);
152
- if ((0, external_utils_js_namespaceObject.checkIsBuildCommands)(appContext.command)) {
152
+ if ((0, external_utils_js_namespaceObject.checkIsBuildCommands)()) {
153
153
  await hooks.generateEntryCode.call({
154
154
  entrypoints
155
155
  });
@@ -101,7 +101,7 @@ const parseModule = async ({ source, filename })=>{
101
101
  return await (0, external_es_module_lexer_namespaceObject.parse)(content);
102
102
  };
103
103
  const getServerCombinedModuleFile = (internalDirectory, entryName)=>external_path_default().join(internalDirectory, entryName, 'server-loader-combined.js');
104
- const checkIsBuildCommands = (contextCommand)=>{
104
+ const checkIsBuildCommands = ()=>{
105
105
  const buildCommands = [
106
106
  'dev',
107
107
  'start',
@@ -111,8 +111,7 @@ const checkIsBuildCommands = (contextCommand)=>{
111
111
  'dev-worker'
112
112
  ];
113
113
  const command = (0, utils_namespaceObject.getCommand)();
114
- if (buildCommands.includes(command)) return true;
115
- return 'dev' === contextCommand || 'start' === contextCommand || 'build' === contextCommand || 'deploy' === contextCommand;
114
+ return buildCommands.includes(command);
116
115
  };
117
116
  const checkIsServeCommand = ()=>{
118
117
  const command = (0, utils_namespaceObject.getCommand)();
@@ -57,7 +57,7 @@ const initialize = ()=>({
57
57
  api.modifyResolvedConfig(async (resolved)=>{
58
58
  let appContext = api.getAppContext();
59
59
  const userConfig = api.getConfig();
60
- const port = await getServerPort(resolved, appContext.command);
60
+ const port = await getServerPort(resolved);
61
61
  appContext = {
62
62
  ...appContext,
63
63
  port,
@@ -92,10 +92,9 @@ function stabilizeConfig(resolve, config, keys) {
92
92
  resolve[key] = config[key] || {};
93
93
  });
94
94
  }
95
- async function getServerPort(config, contextCommand) {
95
+ async function getServerPort(config) {
96
96
  const prodPort = Number(process.env.PORT) || config.server.port || 8080;
97
- const isProgrammaticDev = 'dev' === contextCommand || 'start' === contextCommand;
98
- if ((0, utils_namespaceObject.isDev)() && ((0, utils_namespaceObject.isDevCommand)() || isProgrammaticDev)) return (0, utils_namespaceObject.getPort)(Number(process.env.PORT) || prodPort);
97
+ if ((0, utils_namespaceObject.isDev)() && (0, utils_namespaceObject.isDevCommand)()) return (0, utils_namespaceObject.getPort)(Number(process.env.PORT) || prodPort);
99
98
  return prodPort;
100
99
  }
101
100
  exports["default"] = __webpack_exports__["default"];
@@ -56,9 +56,8 @@ const build = async (api, options)=>{
56
56
  logger.info('Starting production build...');
57
57
  if (!appContext.builder) throw new Error('Expect the Builder to have been initialized, But the appContext.builder received `undefined`');
58
58
  await appContext.builder.onAfterBuild(async ()=>copyEnvFiles(appContext.appDirectory, appContext.distDirectory));
59
- const buildResult = await appContext.builder.build({
59
+ await appContext.builder.build({
60
60
  watch: options?.watch
61
61
  });
62
- if (!options?.watch) await buildResult?.close();
63
62
  };
64
63
  export { build };
@@ -1,10 +1,8 @@
1
1
  import { getServerPlugins } from "../utils/loadPlugins.mjs";
2
- import { build } from "./build.mjs";
3
2
  const deploy = async (api, options = {})=>{
4
3
  const hooks = api.getHooks();
5
4
  const { metaName } = api.getAppContext();
6
- if (options.skipBuild) await getServerPlugins(api, metaName);
7
- else await build(api);
5
+ await getServerPlugins(api, metaName);
8
6
  await hooks.onBeforeDeploy.call(options);
9
7
  await hooks.deploy.call();
10
8
  await hooks.onAfterDeploy.call(options);
@@ -19,6 +19,10 @@ const serverCommand = (program, api)=>{
19
19
  };
20
20
  const deployCommand = (program, api)=>{
21
21
  program.command('deploy').usage('[options]').option('-c --config <config>', i18n.t(localeKeys.command.shared.config)).option('-s --skip-build', i18n.t(localeKeys.command.shared.skipBuild)).description(i18n.t(localeKeys.command.deploy.describe)).action(async (options)=>{
22
+ if (!options.skipBuild) {
23
+ const { build } = await import("./build.mjs");
24
+ await build(api);
25
+ }
22
26
  const { deploy } = await import("./deploy.mjs");
23
27
  await deploy(api, options);
24
28
  process.exit(0);
@@ -75,18 +75,12 @@ const appTools = ()=>({
75
75
  ].some((tag)=>getArgv().includes(tag));
76
76
  if (isSkipBuild) return;
77
77
  }
78
- const { command: contextCommand } = api.getAppContext();
79
- const shouldClean = [
78
+ if ([
80
79
  'dev',
81
80
  'start',
82
81
  'build',
83
82
  'deploy'
84
- ].includes(command) || [
85
- 'dev',
86
- 'start',
87
- 'build'
88
- ].includes(contextCommand);
89
- if (shouldClean) {
83
+ ].includes(command)) {
90
84
  const resolvedConfig = api.getNormalizedConfig();
91
85
  if (resolvedConfig.output.cleanDistPath) {
92
86
  const appContext = api.getAppContext();
@@ -126,8 +120,6 @@ const appTools = ()=>({
126
120
  });
127
121
  const src = appTools;
128
122
  export { defineConfig } from "./defineConfig.mjs";
129
- export { build } from "./commands/build.mjs";
130
- export { deploy } from "./commands/deploy.mjs";
131
123
  export { dev } from "./commands/dev.mjs";
132
124
  export { serve } from "./commands/serve.mjs";
133
125
  export { closeServer } from "./utils/createServer.mjs";
@@ -23,7 +23,7 @@ const analyze = ()=>({
23
23
  const resolvedConfig = api.getNormalizedConfig();
24
24
  const hooks = api.getHooks();
25
25
  try {
26
- if (checkIsBuildCommands(appContext.command)) fs.emptydirSync(appContext.internalDirectory);
26
+ if (checkIsBuildCommands()) fs.emptydirSync(appContext.internalDirectory);
27
27
  } catch {}
28
28
  const apiOnly = await isApiOnly(appContext.appDirectory, resolvedConfig.source?.entriesDir, appContext.apiDirectory);
29
29
  const [{ getProdServerRoutes }] = await Promise.all([
@@ -117,7 +117,7 @@ const analyze = ()=>({
117
117
  htmlTemplates
118
118
  };
119
119
  api.updateAppContext(appContext);
120
- if (checkIsBuildCommands(appContext.command)) {
120
+ if (checkIsBuildCommands()) {
121
121
  await hooks.generateEntryCode.call({
122
122
  entrypoints
123
123
  });
@@ -52,7 +52,7 @@ const parseModule = async ({ source, filename })=>{
52
52
  return await parse(content);
53
53
  };
54
54
  const getServerCombinedModuleFile = (internalDirectory, entryName)=>path.join(internalDirectory, entryName, 'server-loader-combined.js');
55
- const checkIsBuildCommands = (contextCommand)=>{
55
+ const checkIsBuildCommands = ()=>{
56
56
  const buildCommands = [
57
57
  'dev',
58
58
  'start',
@@ -62,8 +62,7 @@ const checkIsBuildCommands = (contextCommand)=>{
62
62
  'dev-worker'
63
63
  ];
64
64
  const command = getCommand();
65
- if (buildCommands.includes(command)) return true;
66
- return 'dev' === contextCommand || 'start' === contextCommand || 'build' === contextCommand || 'deploy' === contextCommand;
65
+ return buildCommands.includes(command);
67
66
  };
68
67
  const checkIsServeCommand = ()=>{
69
68
  const command = getCommand();
@@ -25,7 +25,7 @@ const initialize = ()=>({
25
25
  api.modifyResolvedConfig(async (resolved)=>{
26
26
  let appContext = api.getAppContext();
27
27
  const userConfig = api.getConfig();
28
- const port = await getServerPort(resolved, appContext.command);
28
+ const port = await getServerPort(resolved);
29
29
  appContext = {
30
30
  ...appContext,
31
31
  port,
@@ -60,10 +60,9 @@ function stabilizeConfig(resolve, config, keys) {
60
60
  resolve[key] = config[key] || {};
61
61
  });
62
62
  }
63
- async function getServerPort(config, contextCommand) {
63
+ async function getServerPort(config) {
64
64
  const prodPort = Number(process.env.PORT) || config.server.port || 8080;
65
- const isProgrammaticDev = 'dev' === contextCommand || 'start' === contextCommand;
66
- if (isDev() && (isDevCommand() || isProgrammaticDev)) return getPort(Number(process.env.PORT) || prodPort);
65
+ if (isDev() && isDevCommand()) return getPort(Number(process.env.PORT) || prodPort);
67
66
  return prodPort;
68
67
  }
69
68
  export default initialize;
@@ -57,9 +57,8 @@ const build = async (api, options)=>{
57
57
  logger.info('Starting production build...');
58
58
  if (!appContext.builder) throw new Error('Expect the Builder to have been initialized, But the appContext.builder received `undefined`');
59
59
  await appContext.builder.onAfterBuild(async ()=>copyEnvFiles(appContext.appDirectory, appContext.distDirectory));
60
- const buildResult = await appContext.builder.build({
60
+ await appContext.builder.build({
61
61
  watch: options?.watch
62
62
  });
63
- if (!options?.watch) await buildResult?.close();
64
63
  };
65
64
  export { build };
@@ -1,11 +1,9 @@
1
1
  import "node:module";
2
2
  import { getServerPlugins } from "../utils/loadPlugins.mjs";
3
- import { build } from "./build.mjs";
4
3
  const deploy = async (api, options = {})=>{
5
4
  const hooks = api.getHooks();
6
5
  const { metaName } = api.getAppContext();
7
- if (options.skipBuild) await getServerPlugins(api, metaName);
8
- else await build(api);
6
+ await getServerPlugins(api, metaName);
9
7
  await hooks.onBeforeDeploy.call(options);
10
8
  await hooks.deploy.call();
11
9
  await hooks.onAfterDeploy.call(options);
@@ -20,6 +20,10 @@ const serverCommand = (program, api)=>{
20
20
  };
21
21
  const deployCommand = (program, api)=>{
22
22
  program.command('deploy').usage('[options]').option('-c --config <config>', i18n.t(localeKeys.command.shared.config)).option('-s --skip-build', i18n.t(localeKeys.command.shared.skipBuild)).description(i18n.t(localeKeys.command.deploy.describe)).action(async (options)=>{
23
+ if (!options.skipBuild) {
24
+ const { build } = await import("./build.mjs");
25
+ await build(api);
26
+ }
23
27
  const { deploy } = await import("./deploy.mjs");
24
28
  await deploy(api, options);
25
29
  process.exit(0);
@@ -77,18 +77,12 @@ const appTools = ()=>({
77
77
  ].some((tag)=>getArgv().includes(tag));
78
78
  if (isSkipBuild) return;
79
79
  }
80
- const { command: contextCommand } = api.getAppContext();
81
- const shouldClean = [
80
+ if ([
82
81
  'dev',
83
82
  'start',
84
83
  'build',
85
84
  'deploy'
86
- ].includes(command) || [
87
- 'dev',
88
- 'start',
89
- 'build'
90
- ].includes(contextCommand);
91
- if (shouldClean) {
85
+ ].includes(command)) {
92
86
  const resolvedConfig = api.getNormalizedConfig();
93
87
  if (resolvedConfig.output.cleanDistPath) {
94
88
  const appContext = api.getAppContext();
@@ -128,8 +122,6 @@ const appTools = ()=>({
128
122
  });
129
123
  const src = appTools;
130
124
  export { defineConfig } from "./defineConfig.mjs";
131
- export { build } from "./commands/build.mjs";
132
- export { deploy } from "./commands/deploy.mjs";
133
125
  export { dev } from "./commands/dev.mjs";
134
126
  export { serve } from "./commands/serve.mjs";
135
127
  export { closeServer } from "./utils/createServer.mjs";
@@ -24,7 +24,7 @@ const analyze = ()=>({
24
24
  const resolvedConfig = api.getNormalizedConfig();
25
25
  const hooks = api.getHooks();
26
26
  try {
27
- if (checkIsBuildCommands(appContext.command)) fs.emptydirSync(appContext.internalDirectory);
27
+ if (checkIsBuildCommands()) fs.emptydirSync(appContext.internalDirectory);
28
28
  } catch {}
29
29
  const apiOnly = await isApiOnly(appContext.appDirectory, resolvedConfig.source?.entriesDir, appContext.apiDirectory);
30
30
  const [{ getProdServerRoutes }] = await Promise.all([
@@ -118,7 +118,7 @@ const analyze = ()=>({
118
118
  htmlTemplates
119
119
  };
120
120
  api.updateAppContext(appContext);
121
- if (checkIsBuildCommands(appContext.command)) {
121
+ if (checkIsBuildCommands()) {
122
122
  await hooks.generateEntryCode.call({
123
123
  entrypoints
124
124
  });
@@ -53,7 +53,7 @@ const parseModule = async ({ source, filename })=>{
53
53
  return await parse(content);
54
54
  };
55
55
  const getServerCombinedModuleFile = (internalDirectory, entryName)=>path.join(internalDirectory, entryName, 'server-loader-combined.js');
56
- const checkIsBuildCommands = (contextCommand)=>{
56
+ const checkIsBuildCommands = ()=>{
57
57
  const buildCommands = [
58
58
  'dev',
59
59
  'start',
@@ -63,8 +63,7 @@ const checkIsBuildCommands = (contextCommand)=>{
63
63
  'dev-worker'
64
64
  ];
65
65
  const command = getCommand();
66
- if (buildCommands.includes(command)) return true;
67
- return 'dev' === contextCommand || 'start' === contextCommand || 'build' === contextCommand || 'deploy' === contextCommand;
66
+ return buildCommands.includes(command);
68
67
  };
69
68
  const checkIsServeCommand = ()=>{
70
69
  const command = getCommand();
@@ -26,7 +26,7 @@ const initialize = ()=>({
26
26
  api.modifyResolvedConfig(async (resolved)=>{
27
27
  let appContext = api.getAppContext();
28
28
  const userConfig = api.getConfig();
29
- const port = await getServerPort(resolved, appContext.command);
29
+ const port = await getServerPort(resolved);
30
30
  appContext = {
31
31
  ...appContext,
32
32
  port,
@@ -61,10 +61,9 @@ function stabilizeConfig(resolve, config, keys) {
61
61
  resolve[key] = config[key] || {};
62
62
  });
63
63
  }
64
- async function getServerPort(config, contextCommand) {
64
+ async function getServerPort(config) {
65
65
  const prodPort = Number(process.env.PORT) || config.server.port || 8080;
66
- const isProgrammaticDev = 'dev' === contextCommand || 'start' === contextCommand;
67
- if (isDev() && (isDevCommand() || isProgrammaticDev)) return getPort(Number(process.env.PORT) || prodPort);
66
+ if (isDev() && isDevCommand()) return getPort(Number(process.env.PORT) || prodPort);
68
67
  return prodPort;
69
68
  }
70
69
  export default initialize;
@@ -3,12 +3,10 @@ import { initAppContext } from './utils/initAppContext.js';
3
3
  export * from './defineConfig.js';
4
4
  export declare const appTools: () => CliPlugin<AppTools>;
5
5
  export { defineConfig } from './defineConfig.js';
6
- export { build } from './commands/build.js';
7
- export { deploy } from './commands/deploy.js';
8
6
  export { dev } from './commands/dev.js';
9
7
  export { serve } from './commands/serve.js';
10
8
  export { closeServer } from './utils/createServer.js';
11
- export type { BuildOptions, DeployOptions, DevOptions } from './utils/types.js';
9
+ export type { DeployOptions, DevOptions } from './utils/types.js';
12
10
  export { generateWatchFiles } from './utils/generateWatchFiles.js';
13
11
  export { resolveModernRsbuildConfig, type ResolveModernRsbuildConfigOptions, } from './rsbuild.js';
14
12
  export * from './types/index.js';
@@ -5,6 +5,6 @@ export declare const parseModule: ({ source, filename, }: {
5
5
  filename: string;
6
6
  }) => Promise<readonly [imports: readonly import("es-module-lexer").ImportSpecifier[], exports: readonly import("es-module-lexer").ExportSpecifier[], facade: boolean, hasModuleSyntax: boolean]>;
7
7
  export declare const getServerCombinedModuleFile: (internalDirectory: string, entryName: string) => string;
8
- export declare const checkIsBuildCommands: (contextCommand?: string) => boolean;
8
+ export declare const checkIsBuildCommands: () => boolean;
9
9
  export declare const checkIsServeCommand: () => boolean;
10
10
  export declare const isSubDirOrEqual: (parent: string, child: string) => boolean;
@@ -3,6 +3,10 @@
3
3
  - **Type:** `boolean`
4
4
  - **Default:** `false`
5
5
 
6
+ :::warning Deprecated
7
+ `output.disableSvgr` is deprecated and will be removed in the next major version. Replace `output.disableSvgr: true` with [tools.svgr](/configure/app/tools/svgr.md) set to `false`; `output.disableSvgr: false` is the default behavior and can simply be removed. When both are set, `tools.svgr` wins.
8
+ :::
9
+
6
10
  Whether to transform SVGs into React components. If true, will treat all .svg files as assets.
7
11
 
8
12
  By default, when an SVG resource is referenced in a JS file, Modern.js will call SVGR to convert the SVG into a React component. If you are sure that all SVG resources in your project are not being used as React components, you can turn off this conversion by setting `disableSvgr` to true to improve build performance.
@@ -3,6 +3,10 @@
3
3
  - **Type:** `'url' | 'component'`
4
4
  - **Default:** `'url'`
5
5
 
6
+ :::warning Deprecated
7
+ `output.svgDefaultExport` is deprecated and will be removed in the next major version. Use `svgrOptions.exportType` of [tools.svgr](/configure/app/tools/svgr.md) instead: `'component'` maps to `exportType: 'default'`, `'url'` maps to `exportType: 'named'`. When both are set, `tools.svgr` wins.
8
+ :::
9
+
6
10
  `output.svgDefaultExport` is used to configure the default export type of SVG files.
7
11
 
8
12
  When `output.svgDefaultExport` is set to `url` , the default export of SVG files is the URL of the file. For example:
@@ -5,49 +5,78 @@
5
5
 
6
6
  ```js
7
7
  const defaultOptions = {
8
- lessOptions: {
9
- javascriptEnabled: true,
8
+ lessLoaderOptions: {
9
+ lessOptions: {
10
+ javascriptEnabled: true,
11
+ },
12
+ // CSS Source Map enabled by default in development environment
13
+ sourceMap: isDev,
10
14
  },
11
- // CSS Source Map enabled by default in development environment
12
- sourceMap: isDev,
13
15
  };
14
16
  ```
15
17
 
16
- You can modify the config of [less-loader](https://github.com/webpack-contrib/less-loader) via `tools.less`.
18
+ `tools.less` modifies the options of [@rsbuild/plugin-less](https://rsbuild.rs/plugins/list/plugin-less). It accepts the full plugin options:
19
+
20
+ | Option | Description |
21
+ | ------------------- | --------------------------------------------------------------------------------------------------- |
22
+ | `lessLoaderOptions` | Options passed to [less-loader](https://github.com/webpack-contrib/less-loader), object or function |
23
+ | `include` | Files handled by less-loader, defaults to `/\.less$/` |
24
+ | `exclude` | Files that less-loader should skip |
25
+ | `parallel` | Whether to compile Less modules in worker threads, defaults to `false` |
26
+
27
+ ### Modifying less-loader options
17
28
 
18
- ### Object Type
29
+ When `lessLoaderOptions` is an `Object`, it is merged with the default config through Object.assign in a shallow way. It should be noted that `lessOptions` is merged through deepMerge in a deep way. For example:
30
+
31
+ ```js
32
+ export default {
33
+ tools: {
34
+ less: {
35
+ lessLoaderOptions: {
36
+ lessOptions: {
37
+ javascriptEnabled: false,
38
+ },
39
+ },
40
+ },
41
+ },
42
+ };
43
+ ```
19
44
 
20
- When `tools.less` is configured as `Object` type, it is merged with the default config through Object.assign in a shallow way. It should be noted that `lessOptions` is merged through deepMerge in a deep way. For example:
45
+ When `lessLoaderOptions` is a `Function`, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For example:
21
46
 
22
47
  ```js
23
48
  export default {
24
49
  tools: {
25
50
  less: {
26
- lessOptions: {
27
- javascriptEnabled: false,
51
+ lessLoaderOptions(config) {
52
+ // Modify the config of lessOptions
53
+ config.lessOptions = {
54
+ javascriptEnabled: false,
55
+ };
28
56
  },
29
57
  },
30
58
  },
31
59
  };
32
60
  ```
33
61
 
34
- ### Function Type
62
+ ### Parallel compilation
35
63
 
36
- When `tools.less` is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For example:
64
+ Less compilation is pure JavaScript and runs on the Node.js main thread by default. With `parallel` enabled, Less modules are compiled in a pool of worker threads, which shortens the build when a project has many Less files.
37
65
 
38
66
  ```js
39
67
  export default {
40
68
  tools: {
41
- less(config) {
42
- // Modify the config of lessOptions
43
- config.lessOptions = {
44
- javascriptEnabled: false,
45
- };
69
+ less: {
70
+ parallel: true,
46
71
  },
47
72
  },
48
73
  };
49
74
  ```
50
75
 
76
+ :::tip
77
+ Options sent to worker threads must satisfy the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). With `parallel` enabled, `lessLoaderOptions` therefore cannot contain functions, such as an `additionalData` function or a custom `implementation`.
78
+ :::
79
+
51
80
  ### Modifying Less Version
52
81
 
53
82
  In some scenarios, if you need to use a specific version of Less instead of the built-in Less v4 in Modern.js, you can install the desired Less version in your project and set it up using the `implementation` option of the `less-loader`.
@@ -56,7 +85,9 @@ In some scenarios, if you need to use a specific version of Less instead of the
56
85
  export default {
57
86
  tools: {
58
87
  less: {
59
- implementation: require('less'),
88
+ lessLoaderOptions: {
89
+ implementation: require('less'),
90
+ },
60
91
  },
61
92
  },
62
93
  };
@@ -73,8 +104,52 @@ Used to specify which files `less-loader` does not compile, You can pass in one
73
104
  ```js
74
105
  export default {
75
106
  tools: {
76
- less(config, { addExcludes }) {
77
- addExcludes(/node_modules/);
107
+ less: {
108
+ lessLoaderOptions(config, { addExcludes }) {
109
+ addExcludes(/node_modules/);
110
+ },
111
+ },
112
+ },
113
+ };
114
+ ```
115
+
116
+ The plugin's `exclude` option is the recommended equivalent:
117
+
118
+ ```js
119
+ export default {
120
+ tools: {
121
+ less: {
122
+ exclude: /node_modules/,
123
+ },
124
+ },
125
+ };
126
+ ```
127
+
128
+ ### Legacy form
129
+
130
+ :::warning Do not mix the two layers
131
+ As soon as an object contains a plugin-level key (`lessLoaderOptions`, `include`, `exclude`, ...), the whole object is parsed as plugin options and any loader option in it has no effect. For example, `lessOptions` in `tools.less: { parallel: true, lessOptions: { ... } }` is ignored and must be moved under `lessLoaderOptions`; a hint is printed in development.
132
+ :::
133
+
134
+ In earlier versions, `tools.less` took the less-loader options directly, e.g. `tools.less: { lessOptions: {} }` or `tools.less(config, { addExcludes }) {}`. This form still works: Modern.js wraps it into `lessLoaderOptions` and produces exactly the same config as before. A migration hint is printed in development, and the legacy form will be removed in the next major version.
135
+
136
+ ```js
137
+ // legacy
138
+ export default {
139
+ tools: {
140
+ less: {
141
+ lessOptions: { javascriptEnabled: false },
142
+ },
143
+ },
144
+ };
145
+
146
+ // current
147
+ export default {
148
+ tools: {
149
+ less: {
150
+ lessLoaderOptions: {
151
+ lessOptions: { javascriptEnabled: false },
152
+ },
78
153
  },
79
154
  },
80
155
  };
@@ -5,41 +5,64 @@
5
5
 
6
6
  ```js
7
7
  const defaultOptions = {
8
- // CSS Source Map enabled by default in development environment
9
- sourceMap: isDev,
8
+ sassLoaderOptions: {
9
+ // CSS Source Map enabled by default in development environment
10
+ sourceMap: isDev,
11
+ },
10
12
  };
11
13
  ```
12
14
 
13
- You can modify the config of [sass-loader](https://github.com/webpack-contrib/sass-loader) via `tools.sass`.
15
+ `tools.sass` modifies the options of [@rsbuild/plugin-sass](https://rsbuild.rs/plugins/list/plugin-sass). It accepts the full plugin options:
14
16
 
15
- ### Object Type
17
+ | Option | Description | |
18
+ | ------------------- | --------------------------------------------------------------------------------------------------- | -------- |
19
+ | `sassLoaderOptions` | Options passed to [sass-loader](https://github.com/webpack-contrib/sass-loader), object or function | |
20
+ | `include` | Files handled by sass-loader, defaults to \`/.s(?:a | c)ss$/\` |
21
+ | `exclude` | Files that sass-loader should skip | |
22
+ | `rewriteUrls` | Whether to rewrite relative URLs in Sass files with resolve-url-loader, defaults to `true` | |
16
23
 
17
- When `tools.sass` is `Object` type, it is merged with the default config through Object.assign. It should be noted that `sassOptions` is merged through deepMerge in a deep way.
24
+ ### Modifying sass-loader options
18
25
 
19
- For example:
26
+ When `sassLoaderOptions` is an `Object`, it is merged with the default config through Object.assign in a shallow way. It should be noted that `sassOptions` is merged through deepMerge in a deep way. For example:
20
27
 
21
28
  ```js
22
29
  export default {
23
30
  tools: {
24
31
  sass: {
25
- sourceMap: true,
32
+ sassLoaderOptions: {
33
+ sourceMap: true,
34
+ },
35
+ },
36
+ },
37
+ };
38
+ ```
39
+
40
+ When `sassLoaderOptions` is a `Function`, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For example:
41
+
42
+ ```js
43
+ export default {
44
+ tools: {
45
+ sass: {
46
+ sassLoaderOptions(config) {
47
+ // Modify the additionalData config
48
+ config.additionalData = async (content, loaderContext) => {
49
+ // ...
50
+ };
51
+ },
26
52
  },
27
53
  },
28
54
  };
29
55
  ```
30
56
 
31
- ### Function Type
57
+ ### Disabling URL rewriting
32
58
 
33
- When `tools.sass` is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For Example:
59
+ By default, relative URLs in Sass files are rewritten by resolve-url-loader so that they resolve relative to the source file. If your project does not rely on this, turn it off to drop one loader from the chain:
34
60
 
35
61
  ```js
36
62
  export default {
37
63
  tools: {
38
- sass(config) {
39
- // Modify sourceMap config
40
- config.additionalData = async (content, loaderContext) => {
41
- // ...
42
- };
64
+ sass: {
65
+ rewriteUrls: false,
43
66
  },
44
67
  },
45
68
  };
@@ -53,13 +76,15 @@ In some scenarios, if you need to use a specific version of Sass instead of the
53
76
  export default {
54
77
  tools: {
55
78
  sass: {
56
- implementation: require('sass'),
79
+ sassLoaderOptions: {
80
+ implementation: require('sass'),
81
+ },
57
82
  },
58
83
  },
59
84
  };
60
85
  ```
61
86
 
62
- ### Utility Function
87
+ ### Util Function
63
88
 
64
89
  #### addExcludes
65
90
 
@@ -70,8 +95,52 @@ Used to specify which files `sass-loader` does not compile, You can pass in one
70
95
  ```js
71
96
  export default {
72
97
  tools: {
73
- sass(config, { addExcludes }) {
74
- addExcludes(/node_modules/);
98
+ sass: {
99
+ sassLoaderOptions(config, { addExcludes }) {
100
+ addExcludes(/node_modules/);
101
+ },
102
+ },
103
+ },
104
+ };
105
+ ```
106
+
107
+ The plugin's `exclude` option is the recommended equivalent:
108
+
109
+ ```js
110
+ export default {
111
+ tools: {
112
+ sass: {
113
+ exclude: /node_modules/,
114
+ },
115
+ },
116
+ };
117
+ ```
118
+
119
+ ### Legacy form
120
+
121
+ :::warning Do not mix the two layers
122
+ As soon as an object contains a plugin-level key (`sassLoaderOptions`, `include`, `exclude`, ...), the whole object is parsed as plugin options and any loader option in it has no effect. For example, `sassOptions` in `tools.sass: { rewriteUrls: false, sassOptions: { ... } }` is ignored and must be moved under `sassLoaderOptions`; a hint is printed in development.
123
+ :::
124
+
125
+ In earlier versions, `tools.sass` took the sass-loader options directly, e.g. `tools.sass: { sassOptions: {} }` or `tools.sass(config, { addExcludes }) {}`. This form still works: Modern.js wraps it into `sassLoaderOptions` and produces exactly the same config as before. A migration hint is printed in development, and the legacy form will be removed in the next major version.
126
+
127
+ ```js
128
+ // legacy
129
+ export default {
130
+ tools: {
131
+ sass: {
132
+ sourceMap: true,
133
+ },
134
+ },
135
+ };
136
+
137
+ // current
138
+ export default {
139
+ tools: {
140
+ sass: {
141
+ sassLoaderOptions: {
142
+ sourceMap: true,
143
+ },
75
144
  },
76
145
  },
77
146
  };
@@ -0,0 +1,89 @@
1
+ # tools.svgr
2
+
3
+ - **Type:** `Object | Function | false`
4
+ - **Default:**
5
+
6
+ ```js
7
+ const defaultOptions = {
8
+ mixedImport: true,
9
+ svgrOptions: {
10
+ exportType: 'named',
11
+ },
12
+ };
13
+ ```
14
+
15
+ `tools.svgr` modifies the options of [@rsbuild/plugin-svgr](https://rsbuild.rs/plugins/list/plugin-svgr). It accepts the full plugin options. Set it to `false` to skip registering the SVGR plugin; all `.svg` files are then treated as static assets.
16
+
17
+ | Option | Description |
18
+ | ----------------- | ---------------------------------------------------------------------------------------------------------- |
19
+ | `svgrOptions` | Options passed to [SVGR](https://react-svgr.com/docs/options/), such as `exportType`, `icon`, `svgoConfig` |
20
+ | `parallel` | Whether to transform SVG files in worker threads, defaults to `false` |
21
+ | `exclude` | SVG files that SVGR should skip |
22
+ | `excludeImporter` | Importer files whose SVG imports should skip SVGR |
23
+ | `query` | Query that triggers the SVGR transform, defaults to `/react/` |
24
+ | `mixedImport` | Whether a module may import both the URL and the React component, defaults to `true` |
25
+
26
+ ### Merge rules
27
+
28
+ When `tools.svgr` is an `Object`, its top-level fields are merged with the defaults through Object.assign in a shallow way, and `svgrOptions` is merged one level deeper, so setting only `svgrOptions.icon` keeps the default `exportType`.
29
+
30
+ When `tools.svgr` is a `Function`, the default config is passed as the first parameter, which can be directly modified or returned as the final result. An array of objects and functions is also accepted and applied in order.
31
+
32
+ ### Parallel transform
33
+
34
+ The SVGR transform is pure JavaScript and runs on the Node.js main thread by default. With many SVG files, enable `parallel` to move the transform into a pool of worker threads:
35
+
36
+ ```js
37
+ export default {
38
+ tools: {
39
+ svgr: {
40
+ parallel: true,
41
+ },
42
+ },
43
+ };
44
+ ```
45
+
46
+ :::tip
47
+ Options sent to worker threads must satisfy the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). With `parallel` enabled, `svgrOptions` therefore cannot contain functions.
48
+ :::
49
+
50
+ ### Changing the default export
51
+
52
+ `svgrOptions.exportType` controls what an SVG file exports by default: `'named'` exports the file URL by default and the React component as the named export `ReactComponent`; `'default'` exports the React component by default.
53
+
54
+ ```js
55
+ export default {
56
+ tools: {
57
+ svgr: {
58
+ svgrOptions: {
59
+ exportType: 'default',
60
+ },
61
+ },
62
+ },
63
+ };
64
+ ```
65
+
66
+ ### Disabling SVGR
67
+
68
+ If you are sure that no SVG asset in your project is used as a React component, disable SVGR to improve build performance:
69
+
70
+ ```js
71
+ export default {
72
+ tools: {
73
+ svgr: false,
74
+ },
75
+ };
76
+ ```
77
+
78
+ ### Relation to the legacy options
79
+
80
+ `tools.svgr` replaces the two deprecated options below. They still work today and will be removed in the next major version:
81
+
82
+ | Legacy option | Replacement |
83
+ | -------------------------------------- | -------------------------------------------------------- |
84
+ | `output.svgDefaultExport: 'component'` | `tools.svgr: { svgrOptions: { exportType: 'default' } }` |
85
+ | `output.svgDefaultExport: 'url'` | `tools.svgr: { svgrOptions: { exportType: 'named' } }` |
86
+ | `output.disableSvgr: true` | `tools.svgr: false` |
87
+ | `output.disableSvgr: false` | Default behavior, just remove it |
88
+
89
+ When both are set, `tools.svgr` wins: once `tools.svgr` is set explicitly, `output.disableSvgr` no longer applies, and `tools.svgr.svgrOptions.exportType` overrides the value derived from `output.svgDefaultExport`.
@@ -38,16 +38,22 @@ export default () => <Logo />;
38
38
 
39
39
  ## Modify the Default Export
40
40
 
41
- You can modify the default export of SVG files through the [output.svgDefaultExport](/configure/app/output/svg-default-export.md) config. For example, set the default export as a React component:
41
+ You can modify the default export of SVG files through `svgrOptions.exportType` of [tools.svgr](/configure/app/tools/svgr.md). For example, set the default export as a React component:
42
42
 
43
43
  ```ts
44
44
  export default {
45
- output: {
46
- svgDefaultExport: 'component',
45
+ tools: {
46
+ svgr: {
47
+ svgrOptions: {
48
+ exportType: 'default',
49
+ },
50
+ },
47
51
  },
48
52
  };
49
53
  ```
50
54
 
55
+ The legacy [output.svgDefaultExport](/configure/app/output/svg-default-export.md) config still works but is deprecated.
56
+
51
57
  Then import the SVG, you'll get a React component instead of a URL:
52
58
 
53
59
  ```tsx title="src/component/Logo.tsx"
@@ -74,12 +80,12 @@ Please read the [Import Static Assets](/guides/basic-features/static-assets.md)
74
80
 
75
81
  ## Disable SVGR Processing
76
82
 
77
- By default, when an SVG resource is referenced in a JS file, Modern.js will call SVGR to convert the SVG into a React component. If you are sure that all SVG resources in your project are not being used as React components, you can turn off this conversion by setting [disableSvgr](/configure/app/output/disable-svgr.md) to true to improve build performance.
83
+ By default, when an SVG resource is referenced in a JS file, Modern.js will call SVGR to convert the SVG into a React component. If you are sure that all SVG resources in your project are not being used as React components, you can turn off this conversion by setting [tools.svgr](/configure/app/tools/svgr.md) to `false` to improve build performance.
78
84
 
79
85
  ```js
80
86
  export default {
81
- output: {
82
- disableSvgr: true,
87
+ tools: {
88
+ svgr: false,
83
89
  },
84
90
  };
85
91
  ```
@@ -147,21 +153,27 @@ When SVGR is enabled, its default configuration is as follows:
147
153
  }
148
154
  ```
149
155
 
150
- If you need to modify the SVGR configuration, you can do the following:
156
+ To modify the SVGR configuration, use `svgrOptions` of [tools.svgr](/configure/app/tools/svgr.md). The `svgoConfig.plugins` you pass are deep merged with the defaults by plugin name, so only the changed part needs to be written:
151
157
 
152
158
  ```js
153
159
  export default {
154
160
  tools: {
155
- bundlerChain: (chain, { CHAIN_ID }) => {
156
- chain.module
157
- .rule(CHAIN_ID.RULE.SVG)
158
- .oneOf(CHAIN_ID.ONE_OF.SVG)
159
- .use(CHAIN_ID.USE.SVGR)
160
- .tap(options => {
161
- // modify svgoConfig
162
- options.svgoConfig.plugins[0].params.overrides.removeUselessDefs = false;
163
- return options;
164
- });
161
+ svgr: {
162
+ svgrOptions: {
163
+ svgoConfig: {
164
+ plugins: [
165
+ {
166
+ name: 'preset-default',
167
+ params: {
168
+ overrides: {
169
+ // add one more override on top of the default preset-default
170
+ removeUselessDefs: false,
171
+ },
172
+ },
173
+ },
174
+ ],
175
+ },
176
+ },
165
177
  },
166
178
  },
167
179
  };
package/docs/llms.txt CHANGED
@@ -128,13 +128,14 @@
128
128
  - [cssLoader](/configure/app/tools/css-loader.md): Type: Object | FunctionDefault: The config of css-loader can be modified through tools.cssLoader.
129
129
  - [devServer](/configure/app/tools/dev-server.md): Type: ObjectDefault: {} The config of DevServer can be modified through tools.devServer. Options compress Type: booleanDefault: true Whether to enable gzip compression for served static assets. If you want to disable the gzip compression, you can set compress to false: headers Type: Record<string, string>Default: undefined Adds headers to all responses. historyApiFallback Type: boolean | ConnectHistoryApiFallbackOptionsDefault: false The index.html page will likely have to be served in place of any 404 responses. Enable devServer.historyApiFallback by setting it to true: For more options and information, see the connect-history-api-fallback documentation. proxy Type: ProxyOptions[] | Record<string, string | ProxyOptions>Default: undefined Configure proxy rules for the dev server, and forward requests to the specified service. watch Type: booleanDefault: true Whether to watch files change in directories such as mock/, server/, api/.
130
130
  - [htmlPlugin](/configure/app/tools/html-plugin.md): Type: boolean | Object | FunctionDefault: The configs of html-rspack-plugin can be modified through tools.htmlPlugin.
131
- - [less](/configure/app/tools/less.md): Type: Object | FunctionDefault: You can modify the config of less-loader via tools.less. Object Type When tools.less is configured as Object type, it is merged with the default config through Object.assign in a shallow way. It should be noted that lessOptions is merged through deepMerge in a deep way. For example: Function Type When tools.less is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For example: Modifying Less Version In some scenarios, if you need to use a specific version of Less instead of the built-in Less v4 in Modern.js, you can install the desired Less version in your project and set it up using the implementation option of the less-loader. Util Function addExcludes Type: (excludes: RegExp | RegExp[]) => void Used to specify which files less-loader does not compile, You can pass in one or more regular expressions to match the path of less files, for example:
131
+ - [less](/configure/app/tools/less.md): Type: Object | FunctionDefault: tools.less modifies the options of @rsbuild/plugin-less. It accepts the full plugin options: Modifying less-loader options When lessLoaderOptions is an Object, it is merged with the default config through Object.assign in a shallow way. It should be noted that lessOptions is merged through deepMerge in a deep way. For example: When lessLoaderOptions is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For example: Parallel compilation Less compilation is pure JavaScript and runs on the Node.js main thread by default. With parallel enabled, Less modules are compiled in a pool of worker threads, which shortens the build when a project has many Less files. Modifying Less Version In some scenarios, if you need to use a specific version of Less instead of the built-in Less v4 in Modern.js, you can install the desired Less version in your project and set it up using the implementation option of the less-loader. Util Function addExcludes Type: (excludes: RegExp | RegExp[]) => void Used to specify which files less-loader does not compile, You can pass in one or more regular expressions to match the path of less files, for example: The plugin's exclude option is the recommended equivalent: Legacy form In earlier versions, tools.less took the less-loader options directly, e.g. tools.less: { lessOptions: {} } or tools.less(config, { addExcludes }) {}. This form still works: Modern.js wraps it into lessLoaderOptions and produces exactly the same config as before. A migration hint is printed in development, and the legacy form will be removed in the next major version.
132
132
  - [lightningcssLoader](/configure/app/tools/lightningcss-loader.md): Type: Rspack.LightningcssLoaderOptions | Function | booleanDefault: Rspack.LightningcssLoaderOptions | Function | boolean You can configure builtin:lightningcss-loader through tools.lightningcssLoader.
133
133
  - [minifyCss](/configure/app/tools/minify-css.md): Type: Object | Function | undefinedDefault: When building for production, Modern.js will minimize the CSS code through css-minimizer-webpack-plugin. The config of css-minimizer-webpack-plugin can be modified via tools.minifyCss. Object Type When tools.minifyCss is Object type, it will be merged with the default config via Object.assign. For example, modify the preset config of cssnano: Function Type When tools.minifyCss is Function type, the default config is passed in as the first parameter, the config object can be modified directly, or a value can be returned as the final result.
134
134
  - [postcss](/configure/app/tools/postcss.md): Type: Object | FunctionDefault: Modern.js integrates PostCSS by default, you can configure postcss-loader through tools.postcss. It should be noted that when you enable the tools.lightningcss configuration, PostCSS will be disabled by default, including postcss-loader and its default plugins.
135
135
  - [rspack](/configure/app/tools/rspack.md): Type: Rspack.Configuration | Function | undefinedDefault: undefined tools.rspack is used to configure Rspack.
136
- - [sass](/configure/app/tools/sass.md): Type: Object | FunctionDefault: You can modify the config of sass-loader via tools.sass. Object Type When tools.sass is Object type, it is merged with the default config through Object.assign. It should be noted that sassOptions is merged through deepMerge in a deep way. For example: Function Type When tools.sass is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For Example: Modifying Sass Version In some scenarios, if you need to use a specific version of Sass instead of the built-in Dart Sass v1 in Modern.js, you can install the desired Sass version in your project and set it up using the implementation option of the sass-loader. Utility Function addExcludes Type: (excludes: RegExp | RegExp[]) => void Used to specify which files sass-loader does not compile, You can pass in one or more regular expressions to match the path of sass files, for example:
136
+ - [sass](/configure/app/tools/sass.md): Type: Object | FunctionDefault: tools.sass modifies the options of @rsbuild/plugin-sass. It accepts the full plugin options: Modifying sass-loader options When sassLoaderOptions is an Object, it is merged with the default config through Object.assign in a shallow way. It should be noted that sassOptions is merged through deepMerge in a deep way. For example: When sassLoaderOptions is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. The second parameter provides some utility functions that can be called directly. For example: Disabling URL rewriting By default, relative URLs in Sass files are rewritten by resolve-url-loader so that they resolve relative to the source file. If your project does not rely on this, turn it off to drop one loader from the chain: Modifying Sass Version In some scenarios, if you need to use a specific version of Sass instead of the built-in Dart Sass v1 in Modern.js, you can install the desired Sass version in your project and set it up using the implementation option of the sass-loader. Util Function addExcludes Type: (excludes: RegExp | RegExp[]) => void Used to specify which files sass-loader does not compile, You can pass in one or more regular expressions to match the path of sass files, for example: The plugin's exclude option is the recommended equivalent: Legacy form In earlier versions, tools.sass took the sass-loader options directly, e.g. tools.sass: { sassOptions: {} } or tools.sass(config, { addExcludes }) {}. This form still works: Modern.js wraps it into sassLoaderOptions and produces exactly the same config as before. A migration hint is printed in development, and the legacy form will be removed in the next major version.
137
137
  - [styleLoader](/configure/app/tools/style-loader.md): Type: Object | FunctionDefault: {} The config of style-loader can be set through tools.styleLoader.
138
+ - [svgr](/configure/app/tools/svgr.md): Type: Object | Function | falseDefault: tools.svgr modifies the options of @rsbuild/plugin-svgr. It accepts the full plugin options. Set it to false to skip registering the SVGR plugin; all .svg files are then treated as static assets. Merge rules When tools.svgr is an Object, its top-level fields are merged with the defaults through Object.assign in a shallow way, and svgrOptions is merged one level deeper, so setting only svgrOptions.icon keeps the default exportType. When tools.svgr is a Function, the default config is passed as the first parameter, which can be directly modified or returned as the final result. An array of objects and functions is also accepted and applied in order. Parallel transform The SVGR transform is pure JavaScript and runs on the Node.js main thread by default. With many SVG files, enable parallel to move the transform into a pool of worker threads: Changing the default export svgrOptions.exportType controls what an SVG file exports by default: 'named' exports the file URL by default and the React component as the named export ReactComponent; 'default' exports the React component by default. Disabling SVGR If you are sure that no SVG asset in your project is used as a React component, disable SVGR to improve build performance: Relation to the legacy options tools.svgr replaces the two deprecated options below. They still work today and will be removed in the next major version: When both are set, tools.svgr wins: once tools.svgr is set explicitly, output.disableSvgr no longer applies, and tools.svgr.svgrOptions.exportType overrides the value derived from output.svgDefaultExport.
138
139
  - [swc](/configure/app/tools/swc.md): Type: Object | FunctionDefault: undefined
139
140
  - [tsChecker](/configure/app/tools/ts-checker.md): Type: Object | FunctionDefault: By default, the @rsbuild/plugin-type-check is enabled for type checking. You can use output.disableTsChecker config to disable it.
140
141
  - [aliasStrategy](/configure/app/source/alias-strategy.md): Type: 'prefer-tsconfig' | 'prefer-alias'Default: 'prefer-tsconfig' source.aliasStrategy is used to control the priority between the paths option in tsconfig.json and the alias option in the bundler.
@@ -126,16 +126,16 @@ You can read [Rsbuild Official Website - Plugins](https://v2.rsbuild.rs/plugins/
126
126
 
127
127
  The following are official Rsbuild plugins that are already built into Modern.js. They can be enabled without installation:
128
128
 
129
- | Plugin | Description | Modern.js Link |
130
- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
131
- | [React Plugin](https://v2.rsbuild.rs/plugins/list/plugin-react) | Provides support for React | - |
132
- | [SVGR Plugin](https://v2.rsbuild.rs/plugins/list/plugin-svgr) | Supports converting SVG images into React components | [output.disableSvgr](/configure/app/output/disable-svgr.md)<br />[output.svgDefaultExport](/configure/app/output/svg-default-export.md) |
133
- | [Assets Retry Plugin](https://github.com/rstackjs/rsbuild-plugin-assets-retry) | Automatically retries requests when static asset loading fails | [output.assetsRetry](/configure/app/output/assets-retry.md) |
134
- | [Type Check Plugin](https://github.com/rstackjs/rsbuild-plugin-type-check) | Runs TypeScript type checking in a separate process | [output.disableTsChecker](/configure/app/output/disable-ts-checker.md)<br />[tools.tsChecker](/configure/app/tools/ts-checker.md) |
135
- | [Source Build Plugin](https://github.com/rstackjs/rsbuild-plugin-source-build) | For monorepo scenarios, supports referencing source code from other subdirectories and completing builds and hot updates | [experiments.sourceBuild](/configure/app/experiments/source-build.md) |
136
- | [Check Syntax Plugin](https://github.com/rstackjs/rsbuild-plugin-check-syntax) | Analyzes the syntax compatibility of the build artifacts to determine if there are any advanced syntax features that cause compatibility issues | [security.checkSyntax](/configure/app/security/check-syntax.md) |
137
- | [CSS Minimizer Plugin](https://github.com/rstackjs/rsbuild-plugin-css-minimizer) | Used to customize the CSS compression tool, switch to [cssnano](https://cssnano.co/) or other tools for CSS compression | [tools.minifyCss](/configure/app/tools/minify-css.md) |
138
- | [Rem Plugin](https://github.com/rstackjs/rsbuild-plugin-rem) | Implements rem adaptive layout for mobile pages | [output.convertToRem](/configure/app/output/convert-to-rem.md) |
129
+ | Plugin | Description | Modern.js Link |
130
+ | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
131
+ | [React Plugin](https://v2.rsbuild.rs/plugins/list/plugin-react) | Provides support for React | - |
132
+ | [SVGR Plugin](https://v2.rsbuild.rs/plugins/list/plugin-svgr) | Supports converting SVG images into React components | [tools.svgr](/configure/app/tools/svgr.md) |
133
+ | [Assets Retry Plugin](https://github.com/rstackjs/rsbuild-plugin-assets-retry) | Automatically retries requests when static asset loading fails | [output.assetsRetry](/configure/app/output/assets-retry.md) |
134
+ | [Type Check Plugin](https://github.com/rstackjs/rsbuild-plugin-type-check) | Runs TypeScript type checking in a separate process | [output.disableTsChecker](/configure/app/output/disable-ts-checker.md)<br />[tools.tsChecker](/configure/app/tools/ts-checker.md) |
135
+ | [Source Build Plugin](https://github.com/rstackjs/rsbuild-plugin-source-build) | For monorepo scenarios, supports referencing source code from other subdirectories and completing builds and hot updates | [experiments.sourceBuild](/configure/app/experiments/source-build.md) |
136
+ | [Check Syntax Plugin](https://github.com/rstackjs/rsbuild-plugin-check-syntax) | Analyzes the syntax compatibility of the build artifacts to determine if there are any advanced syntax features that cause compatibility issues | [security.checkSyntax](/configure/app/security/check-syntax.md) |
137
+ | [CSS Minimizer Plugin](https://github.com/rstackjs/rsbuild-plugin-css-minimizer) | Used to customize the CSS compression tool, switch to [cssnano](https://cssnano.co/) or other tools for CSS compression | [tools.minifyCss](/configure/app/tools/minify-css.md) |
138
+ | [Rem Plugin](https://github.com/rstackjs/rsbuild-plugin-rem) | Implements rem adaptive layout for mobile pages | [output.convertToRem](/configure/app/output/convert-to-rem.md) |
139
139
 
140
140
  #### Plugins Not Built-in
141
141
 
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "modern",
16
16
  "modern.js"
17
17
  ],
18
- "version": "3.9.2",
18
+ "version": "3.9.3",
19
19
  "types": "./dist/types/index.d.ts",
20
20
  "main": "./dist/cjs/index.js",
21
21
  "exports": {
@@ -113,10 +113,10 @@
113
113
  }
114
114
  },
115
115
  "dependencies": {
116
- "@babel/parser": "^7.29.7",
117
- "@babel/traverse": "^7.29.7",
118
- "@babel/types": "^7.29.7",
119
- "@rsbuild/core": "2.2.7",
116
+ "@babel/parser": "^7.29.9",
117
+ "@babel/traverse": "^7.29.8",
118
+ "@babel/types": "^7.29.8",
119
+ "@rsbuild/core": "2.2.9",
120
120
  "@swc/core": "1.15.41",
121
121
  "@swc/helpers": "^0.5.17",
122
122
  "es-module-lexer": "^1.7.0",
@@ -126,19 +126,19 @@
126
126
  "ndepe": "^0.1.13",
127
127
  "pkg-types": "^1.3.1",
128
128
  "std-env": "^3.10.0",
129
- "@modern-js/builder": "3.9.2",
130
- "@modern-js/i18n-utils": "3.9.2",
131
- "@modern-js/plugin": "3.9.2",
132
- "@modern-js/plugin-data-loader": "3.9.2",
133
- "@modern-js/prod-server": "3.9.2",
134
- "@modern-js/server": "3.9.2",
135
- "@modern-js/server-core": "3.9.2",
136
- "@modern-js/server-utils": "3.9.2",
137
- "@modern-js/types": "3.9.2",
138
- "@modern-js/utils": "3.9.2"
129
+ "@modern-js/builder": "3.9.3",
130
+ "@modern-js/i18n-utils": "3.9.3",
131
+ "@modern-js/plugin": "3.9.3",
132
+ "@modern-js/plugin-data-loader": "3.9.3",
133
+ "@modern-js/prod-server": "3.9.3",
134
+ "@modern-js/server": "3.9.3",
135
+ "@modern-js/server-utils": "3.9.3",
136
+ "@modern-js/server-core": "3.9.3",
137
+ "@modern-js/types": "3.9.3",
138
+ "@modern-js/utils": "3.9.3"
139
139
  },
140
140
  "devDependencies": {
141
- "@rslib/core": "1.0.0",
141
+ "@rslib/core": "1.0.1",
142
142
  "@types/babel__traverse": "7.28.0",
143
143
  "@types/node": "^20",
144
144
  "ts-node": "^10.9.2",