@meteorjs/rspack 1.1.0-beta.2 → 1.1.0-beta.20

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.
@@ -0,0 +1,121 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const { cleanOmittedPaths } = require("./mergeRulesSplitOverlap.js");
4
+ const { mergeMeteorRspackFragments } = require("./meteorRspackConfigFactory.js");
5
+
6
+ // Helper function to load and process config files
7
+ async function loadAndProcessConfig(configPath, configType, Meteor, argv, disableWarnings) {
8
+ try {
9
+ // Load the config file
10
+ let config;
11
+ if (path.extname(configPath) === '.mjs') {
12
+ // For ESM modules, we need to use dynamic import
13
+ const fileUrl = `file://${configPath}`;
14
+ const module = await import(fileUrl);
15
+ config = module.default || module;
16
+ } else {
17
+ // For CommonJS modules, we can use require
18
+ config = require(configPath)?.default || require(configPath);
19
+ }
20
+
21
+ // Process the config
22
+ const rawConfig = typeof config === 'function' ? config(Meteor, argv) : config;
23
+ const resolvedConfig = await Promise.resolve(rawConfig);
24
+ const userConfig = resolvedConfig && '0' in resolvedConfig ? resolvedConfig[0] : resolvedConfig;
25
+
26
+ // Define omitted paths and warning function
27
+ const omitPaths = [
28
+ "name",
29
+ "target",
30
+ "entry",
31
+ "output.path",
32
+ "output.filename",
33
+ ...(Meteor.isServer ? ["optimization.splitChunks", "optimization.runtimeChunk"] : []),
34
+ ].filter(Boolean);
35
+
36
+ const warningFn = path => {
37
+ if (disableWarnings) return;
38
+ console.warn(
39
+ `[${configType}] Ignored custom "${path}" — reserved for Meteor-Rspack integration.`,
40
+ );
41
+ };
42
+
43
+ // Clean omitted paths and merge Meteor Rspack fragments
44
+ let nextConfig = cleanOmittedPaths(userConfig, {
45
+ omitPaths,
46
+ warningFn,
47
+ });
48
+ nextConfig = mergeMeteorRspackFragments(nextConfig);
49
+
50
+ return nextConfig;
51
+ } catch (error) {
52
+ console.error(`Error loading ${configType} from ${configPath}:`, error);
53
+ if (configType === 'rspack.config.js') {
54
+ throw error; // Only rethrow for project config
55
+ }
56
+ return null;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Loads both the user's Rspack configuration and its potential override.
62
+ *
63
+ * @param {string|undefined} projectConfigPath
64
+ * @param {object} Meteor
65
+ * @param {object} argv
66
+ * @returns {Promise<{ nextUserConfig: object|null, nextOverrideConfig: object|null }>}
67
+ */
68
+ async function loadUserAndOverrideConfig(projectConfigPath, Meteor, argv) {
69
+ let nextUserConfig = null;
70
+ let nextOverrideConfig = null;
71
+
72
+ const projectDir = process.cwd();
73
+ const isMeteorPackageConfig = projectDir.includes("/packages/rspack");
74
+
75
+ if (projectConfigPath) {
76
+ const configDir = path.dirname(projectConfigPath);
77
+ const configFileName = path.basename(projectConfigPath);
78
+ const configExt = path.extname(configFileName);
79
+ const configNameWithoutExt = configFileName.replace(configExt, '');
80
+ const configNameFull = `${configNameWithoutExt}.override${configExt}`;
81
+ const overrideConfigPath = path.join(configDir, configNameFull);
82
+
83
+ if (fs.existsSync(overrideConfigPath)) {
84
+ nextOverrideConfig = await loadAndProcessConfig(
85
+ overrideConfigPath,
86
+ configNameFull,
87
+ Meteor,
88
+ argv,
89
+ Meteor.isAngularEnabled
90
+ );
91
+ }
92
+
93
+ if (fs.existsSync(projectConfigPath) && !isMeteorPackageConfig) {
94
+ // Check if there's a .mjs or .cjs version of the config file
95
+ const mjsConfigPath = projectConfigPath.replace(/\.js$/, '.mjs');
96
+ const cjsConfigPath = projectConfigPath.replace(/\.js$/, '.cjs');
97
+
98
+ let projectConfigPathToUse = projectConfigPath;
99
+ if (fs.existsSync(mjsConfigPath)) {
100
+ projectConfigPathToUse = mjsConfigPath;
101
+ } else if (fs.existsSync(cjsConfigPath)) {
102
+ projectConfigPathToUse = cjsConfigPath;
103
+ }
104
+
105
+ nextUserConfig = await loadAndProcessConfig(
106
+ projectConfigPathToUse,
107
+ 'rspack.config.js',
108
+ Meteor,
109
+ argv,
110
+ Meteor.isAngularEnabled
111
+ );
112
+ }
113
+ }
114
+
115
+ return { nextUserConfig, nextOverrideConfig };
116
+ }
117
+
118
+ module.exports = {
119
+ loadAndProcessConfig,
120
+ loadUserAndOverrideConfig,
121
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meteorjs/rspack",
3
- "version": "1.1.0-beta.2",
3
+ "version": "1.1.0-beta.20",
4
4
  "description": "Configuration logic for using Rspack in Meteor projects",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
package/rspack.config.js CHANGED
@@ -12,7 +12,6 @@ const { RequireExternalsPlugin } = require('./plugins/RequireExtenalsPlugin.js')
12
12
  const { AssetExternalsPlugin } = require('./plugins/AssetExternalsPlugin.js');
13
13
  const { generateEagerTestFile } = require("./lib/test.js");
14
14
  const { getMeteorIgnoreEntries, createIgnoreGlobConfig } = require("./lib/ignore");
15
- const { mergeMeteorRspackFragments } = require("./lib/meteorRspackConfigFactory.js");
16
15
  const {
17
16
  compileWithMeteor,
18
17
  compileWithRspack,
@@ -22,6 +21,7 @@ const {
22
21
  makeWebNodeBuiltinsAlias,
23
22
  disablePlugins,
24
23
  } = require('./lib/meteorRspackHelpers.js');
24
+ const { loadUserAndOverrideConfig } = require('./lib/meteorRspackConfigHelpers.js');
25
25
  const { prepareMeteorRspackConfig } = require("./lib/meteorRspackConfigFactory");
26
26
 
27
27
  // Safe require that doesn't throw if the module isn't found
@@ -204,15 +204,42 @@ module.exports = async function (inMeteor = {}, argv = {}) {
204
204
  const Meteor = { ...inMeteor };
205
205
  // Convert string boolean values to actual booleans
206
206
  for (const key in Meteor) {
207
- if (Meteor[key] === 'true' || Meteor[key] === true) {
207
+ if (Meteor[key] === "true" || Meteor[key] === true) {
208
208
  Meteor[key] = true;
209
- } else if (Meteor[key] === 'false' || Meteor[key] === false) {
209
+ } else if (Meteor[key] === "false" || Meteor[key] === false) {
210
210
  Meteor[key] = false;
211
211
  }
212
212
  }
213
213
 
214
- const isProd = !!Meteor.isProduction || argv.mode === 'production';
215
- const isDev = !!Meteor.isDevelopment || !isProd;
214
+ const isTestLike = !!Meteor.isTestLike;
215
+ const swcExternalHelpers = !!Meteor.swcExternalHelpers;
216
+ const isNative = !!Meteor.isNative;
217
+
218
+ const projectDir = process.cwd();
219
+ const projectConfigPath =
220
+ Meteor.projectConfigPath || path.resolve(projectDir, "rspack.config.js");
221
+
222
+ // Load and apply project-level overrides for the selected build
223
+ const { nextUserConfig, nextOverrideConfig } =
224
+ await loadUserAndOverrideConfig(projectConfigPath, Meteor, argv);
225
+
226
+ // Determine the mode
227
+ const getModeFromConfig = () => {
228
+ if (nextOverrideConfig?.mode) return nextOverrideConfig.mode;
229
+ if (nextUserConfig?.mode) return nextUserConfig.mode;
230
+ if (argv.mode) return argv.mode;
231
+ if (Meteor.isProduction) return "production";
232
+ if (Meteor.isDevelopment) return "development";
233
+ return null;
234
+ };
235
+
236
+ const currentMode = getModeFromConfig();
237
+ const isProd = currentMode
238
+ ? currentMode === "production"
239
+ : !!Meteor.isProduction;
240
+ const isDev = currentMode
241
+ ? currentMode === "development"
242
+ : !!Meteor.isDevelopment || !isProd;
216
243
  const isTest = !!Meteor.isTest;
217
244
  const isClient = !!Meteor.isClient;
218
245
  const isServer = !!Meteor.isServer;
@@ -222,12 +249,8 @@ module.exports = async function (inMeteor = {}, argv = {}) {
222
249
  const isTestModule = !!Meteor.isTestModule;
223
250
  const isTestEager = !!Meteor.isTestEager;
224
251
  const isTestFullApp = !!Meteor.isTestFullApp;
225
- const isTestLike = !!Meteor.isTestLike;
226
- const swcExternalHelpers = !!Meteor.swcExternalHelpers;
227
- const isNative = !!Meteor.isNative;
228
- const mode = isProd ? 'production' : 'development';
229
- const projectDir = process.cwd();
230
- const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, 'rspack.config.js');
252
+
253
+ const mode = isProd ? "production" : "development";
231
254
  const configPath = Meteor.configPath;
232
255
  const testEntry = Meteor.testEntry;
233
256
 
@@ -240,28 +263,18 @@ module.exports = async function (inMeteor = {}, argv = {}) {
240
263
  const isAngularEnabled = Meteor.isAngularEnabled || false;
241
264
 
242
265
  // Determine entry points
243
- const entryPath = Meteor.entryPath;
266
+ const entryPath = Meteor.entryPath || "";
244
267
 
245
268
  // Determine output points
246
269
  const outputPath = Meteor.outputPath;
247
- const outputDir = path.dirname(Meteor.outputPath || '');
270
+ const outputDir = path.dirname(Meteor.outputPath || "");
248
271
 
249
272
  const outputFilename = Meteor.outputFilename;
250
273
 
251
- // Determine run point
252
- const runPath = Meteor.runPath;
253
-
254
- // Determine banner
255
- const bannerOutput = JSON.parse(Meteor.bannerOutput || process.env.RSPACK_BANNER || '""');
256
-
257
- // Determine output directories
258
- const clientOutputDir = path.resolve(projectDir, 'public');
259
- const serverOutputDir = path.resolve(projectDir, 'private');
260
-
261
274
  // Determine context for bundles and assets
262
- const buildContext = Meteor.buildContext || '_build';
263
- const assetsContext = Meteor.assetsContext || 'build-assets';
264
- const chunksContext = Meteor.chunksContext || 'build-chunks';
275
+ const buildContext = Meteor.buildContext || "_build";
276
+ const assetsContext = Meteor.assetsContext || "build-assets";
277
+ const chunksContext = Meteor.chunksContext || "build-chunks";
265
278
 
266
279
  // Determine build output and pass to Meteor
267
280
  const buildOutputDir = path.resolve(projectDir, buildContext, outputDir);
@@ -269,27 +282,38 @@ module.exports = async function (inMeteor = {}, argv = {}) {
269
282
 
270
283
  const cacheStrategy = createCacheStrategy(
271
284
  mode,
272
- (Meteor.isClient && 'client') || 'server',
285
+ (Meteor.isClient && "client") || "server",
273
286
  { projectConfigPath, configPath }
274
287
  );
275
288
 
289
+ // Determine run point
290
+ const runPath = Meteor.runPath || "";
291
+
292
+ // Determine banner
293
+ const bannerOutput = JSON.parse(
294
+ Meteor.bannerOutput || process.env.RSPACK_BANNER || '""'
295
+ );
296
+
297
+ // Determine output directories
298
+ const clientOutputDir = path.resolve(projectDir, "public");
299
+ const serverOutputDir = path.resolve(projectDir, "private");
300
+
276
301
  // Expose Meteor's helpers to expand Rspack configs
277
- Meteor.compileWithMeteor = deps => compileWithMeteor(deps);
302
+ Meteor.compileWithMeteor = (deps) => compileWithMeteor(deps);
278
303
  Meteor.compileWithRspack = (deps, options = {}) =>
279
304
  compileWithRspack(deps, {
280
305
  options: mergeSplitOverlap(Meteor.swcConfigOptions, options),
281
306
  });
282
- Meteor.setCache = enabled =>
283
- setCache(
284
- !!enabled,
285
- enabled === 'memory' ? undefined : cacheStrategy
286
- );
307
+ Meteor.setCache = (enabled) =>
308
+ setCache(!!enabled, enabled === "memory" ? undefined : cacheStrategy);
287
309
  Meteor.splitVendorChunk = () => splitVendorChunk();
288
- Meteor.extendSwcConfig = (customSwcConfig) => extendSwcConfig(customSwcConfig);
310
+ Meteor.extendSwcConfig = (customSwcConfig) =>
311
+ extendSwcConfig(customSwcConfig);
289
312
  Meteor.extendConfig = (...configs) => mergeSplitOverlap(...configs);
290
- Meteor.disablePlugins = matchers => prepareMeteorRspackConfig({
291
- disablePlugins: matchers,
292
- });
313
+ Meteor.disablePlugins = (matchers) =>
314
+ prepareMeteorRspackConfig({
315
+ disablePlugins: matchers,
316
+ });
293
317
 
294
318
  // Add HtmlRspackPlugin function to Meteor
295
319
  Meteor.HtmlRspackPlugin = (options = {}) => {
@@ -328,16 +352,13 @@ module.exports = async function (inMeteor = {}, argv = {}) {
328
352
  // Set default watch options
329
353
  const watchOptions = {
330
354
  ignored: [
331
- ...createIgnoreGlobConfig([
332
- ...meteorIgnoreEntries,
333
- ...additionalEntries,
334
- ]),
355
+ ...createIgnoreGlobConfig([...meteorIgnoreEntries, ...additionalEntries]),
335
356
  ],
336
357
  };
337
358
 
338
359
  if (Meteor.isDebug || Meteor.isVerbose) {
339
- console.log('[i] Rspack mode:', mode);
340
- console.log('[i] Meteor flags:', Meteor);
360
+ console.log("[i] Rspack mode:", mode);
361
+ console.log("[i] Meteor flags:", Meteor);
341
362
  }
342
363
 
343
364
  const enableSwcExternalHelpers = !isServer && swcExternalHelpers;
@@ -361,34 +382,34 @@ module.exports = async function (inMeteor = {}, argv = {}) {
361
382
  ...(isServer ? [/^bcrypt$/] : []),
362
383
  ];
363
384
  const alias = {
364
- '/': path.resolve(process.cwd()),
385
+ "/": path.resolve(process.cwd()),
365
386
  };
366
387
  const fallback = {
367
388
  ...(isClient && makeWebNodeBuiltinsAlias()),
368
389
  };
369
390
  const extensions = [
370
- '.ts',
371
- '.tsx',
372
- '.mts',
373
- '.cts',
374
- '.js',
375
- '.jsx',
376
- '.mjs',
377
- '.cjs',
378
- '.json',
379
- '.wasm',
391
+ ".ts",
392
+ ".tsx",
393
+ ".mts",
394
+ ".cts",
395
+ ".js",
396
+ ".jsx",
397
+ ".mjs",
398
+ ".cjs",
399
+ ".json",
400
+ ".wasm",
380
401
  ];
381
402
  const extraRules = [];
382
403
 
383
404
  const reactRefreshModule = isReactEnabled
384
- ? safeRequire('@rspack/plugin-react-refresh')
405
+ ? safeRequire("@rspack/plugin-react-refresh")
385
406
  : null;
386
407
 
387
408
  const requireExternalsPlugin = new RequireExternalsPlugin({
388
409
  filePath: path.join(buildContext, runPath),
389
410
  ...(Meteor.isBlazeEnabled && {
390
411
  externals: /\.html$/,
391
- isEagerImport: module => module.endsWith('.html'),
412
+ isEagerImport: (module) => module.endsWith(".html"),
392
413
  ...(isProd && {
393
414
  lastImports: [`./${outputFilename}`],
394
415
  }),
@@ -398,25 +419,26 @@ module.exports = async function (inMeteor = {}, argv = {}) {
398
419
 
399
420
  // Handle assets
400
421
  const assetExternalsPlugin = new AssetExternalsPlugin();
401
- const assetModuleFilename = _fileInfo => {
422
+ const assetModuleFilename = (_fileInfo) => {
402
423
  const filename = _fileInfo.filename;
403
- const isPublic = filename.startsWith('/') || filename.startsWith('public');
424
+ const isPublic = filename.startsWith("/") || filename.startsWith("public");
404
425
  if (isPublic) return `[name][ext][query]`;
405
426
  return `${assetsContext}/[hash][ext][query]`;
406
427
  };
407
428
 
408
429
  const rsdoctorModule = isBundleVisualizerEnabled
409
- ? safeRequire('@rsdoctor/rspack-plugin')
430
+ ? safeRequire("@rsdoctor/rspack-plugin")
410
431
  : null;
411
- const doctorPluginConfig = isRun && isBundleVisualizerEnabled && rsdoctorModule?.RsdoctorRspackPlugin
412
- ? [
413
- new rsdoctorModule.RsdoctorRspackPlugin({
414
- port: isClient
415
- ? (parseInt(Meteor.rsdoctorClientPort || '8888', 10))
416
- : (parseInt(Meteor.rsdoctorServerPort || '8889', 10)),
417
- }),
418
- ]
419
- : [];
432
+ const doctorPluginConfig =
433
+ isRun && isBundleVisualizerEnabled && rsdoctorModule?.RsdoctorRspackPlugin
434
+ ? [
435
+ new rsdoctorModule.RsdoctorRspackPlugin({
436
+ port: isClient
437
+ ? parseInt(Meteor.rsdoctorClientPort || "8888", 10)
438
+ : parseInt(Meteor.rsdoctorServerPort || "8889", 10),
439
+ }),
440
+ ]
441
+ : [];
420
442
  const bannerPluginConfig = !isBuild
421
443
  ? [
422
444
  new BannerPlugin({
@@ -452,16 +474,11 @@ module.exports = async function (inMeteor = {}, argv = {}) {
452
474
  : isClient && isTest && testEntry
453
475
  ? path.resolve(process.cwd(), testEntry)
454
476
  : path.resolve(process.cwd(), buildContext, entryPath);
455
- console.log(
456
- "--> (rspack.config.js-Line: 431)\n clientEntry: ",
457
- clientEntry,
458
- entryPath
459
- );
460
- const clientNameConfig = `[${(isTest && 'test-') || ''}client-rspack]`;
477
+ const clientNameConfig = `[${(isTest && "test-") || ""}client-rspack]`;
461
478
  // Base client config
462
479
  let clientConfig = {
463
480
  name: clientNameConfig,
464
- target: 'web',
481
+ target: "web",
465
482
  mode,
466
483
  entry: clientEntry,
467
484
  output: {
@@ -470,7 +487,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
470
487
  const chunkName = _module.chunk?.name;
471
488
  const isMainChunk = !chunkName || chunkName === "main";
472
489
  const chunkSuffix = `${chunksContext}/[id]${
473
- isProd ? '.[chunkhash]' : ''
490
+ isProd ? ".[chunkhash]" : ""
474
491
  }.js`;
475
492
  if (isDevEnvironment) {
476
493
  if (isMainChunk) return outputFilename;
@@ -479,21 +496,21 @@ module.exports = async function (inMeteor = {}, argv = {}) {
479
496
  if (isMainChunk) return `../${buildContext}/${outputPath}`;
480
497
  return chunkSuffix;
481
498
  },
482
- libraryTarget: 'commonjs2',
483
- publicPath: '/',
484
- chunkFilename: `${chunksContext}/[id]${isProd ? '.[chunkhash]' : ''}.js`,
499
+ libraryTarget: "commonjs2",
500
+ publicPath: "/",
501
+ chunkFilename: `${chunksContext}/[id]${isProd ? ".[chunkhash]" : ""}.js`,
485
502
  assetModuleFilename,
486
503
  cssFilename: `${chunksContext}/[name]${
487
- isProd ? '.[contenthash]' : ''
504
+ isProd ? ".[contenthash]" : ""
488
505
  }.css`,
489
506
  cssChunkFilename: `${chunksContext}/[id]${
490
- isProd ? '.[contenthash]' : ''
507
+ isProd ? ".[contenthash]" : ""
491
508
  }.css`,
492
509
  ...(isProd && { clean: { keep: keepOutsideBuild() } }),
493
510
  },
494
511
  optimization: {
495
512
  usedExports: true,
496
- splitChunks: { chunks: 'async' },
513
+ splitChunks: { chunks: "async" },
497
514
  },
498
515
  module: {
499
516
  rules: [
@@ -502,7 +519,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
502
519
  ? [
503
520
  {
504
521
  test: /\.html$/i,
505
- loader: 'ignore-loader',
522
+ loader: "ignore-loader",
506
523
  },
507
524
  ]
508
525
  : []),
@@ -520,33 +537,36 @@ module.exports = async function (inMeteor = {}, argv = {}) {
520
537
  assetExternalsPlugin,
521
538
  ].filter(Boolean),
522
539
  new DefinePlugin({
523
- 'Meteor.isClient': JSON.stringify(true),
524
- 'Meteor.isServer': JSON.stringify(false),
525
- 'Meteor.isTest': JSON.stringify(isTestLike && !isTestFullApp),
526
- 'Meteor.isAppTest': JSON.stringify(isTestLike && isTestFullApp),
527
- 'Meteor.isDevelopment': JSON.stringify(isDev),
528
- 'Meteor.isProduction': JSON.stringify(isProd),
540
+ "Meteor.isClient": JSON.stringify(true),
541
+ "Meteor.isServer": JSON.stringify(false),
542
+ "Meteor.isTest": JSON.stringify(isTestLike && !isTestFullApp),
543
+ "Meteor.isAppTest": JSON.stringify(isTestLike && isTestFullApp),
544
+ "Meteor.isDevelopment": JSON.stringify(isDev),
545
+ "Meteor.isProduction": JSON.stringify(isProd),
529
546
  }),
530
547
  ...bannerPluginConfig,
531
548
  Meteor.HtmlRspackPlugin(),
532
549
  ...doctorPluginConfig,
533
550
  new NormalModuleReplacementPlugin(/^node:(.*)$/, (res) => {
534
- res.request = res.request.replace(/^node:/, '');
551
+ res.request = res.request.replace(/^node:/, "");
535
552
  }),
536
553
  ],
537
554
  watchOptions,
538
- devtool: isDevEnvironment || isNative || isTest ? 'source-map' : 'hidden-source-map',
555
+ devtool:
556
+ isDevEnvironment || isNative || isTest
557
+ ? "source-map"
558
+ : "hidden-source-map",
539
559
  ...(isDevEnvironment && {
540
560
  devServer: {
541
561
  ...createRemoteDevServerConfig(),
542
- static: { directory: clientOutputDir, publicPath: '/__rspack__/' },
562
+ static: { directory: clientOutputDir, publicPath: "/__rspack__/" },
543
563
  hot: true,
544
564
  liveReload: true,
545
565
  ...(Meteor.isBlazeEnabled && { hot: false }),
546
566
  port: Meteor.devServerPort || 8080,
547
567
  devMiddleware: {
548
- writeToDisk: filePath =>
549
- /\.(html)$/.test(filePath) && !filePath.includes('.hot-update.'),
568
+ writeToDisk: (filePath) =>
569
+ /\.(html)$/.test(filePath) && !filePath.includes(".hot-update."),
550
570
  },
551
571
  },
552
572
  }),
@@ -576,23 +596,18 @@ module.exports = async function (inMeteor = {}, argv = {}) {
576
596
  : isServer && isTest && testEntry
577
597
  ? path.resolve(process.cwd(), testEntry)
578
598
  : path.resolve(projectDir, buildContext, entryPath);
579
- const serverNameConfig = `[${(isTest && 'test-') || ''}server-rspack]`;
580
- console.log(
581
- "--> (rspack.config.js-Line: 576)\n serverEntry: ",
582
- serverEntry,
583
- entryPath
584
- );
599
+ const serverNameConfig = `[${(isTest && "test-") || ""}server-rspack]`;
585
600
  // Base server config
586
601
  let serverConfig = {
587
602
  name: serverNameConfig,
588
- target: 'node',
603
+ target: "node",
589
604
  mode,
590
605
  entry: serverEntry,
591
606
  output: {
592
607
  path: serverOutputDir,
593
608
  filename: () => `../${buildContext}/${outputPath}`,
594
- libraryTarget: 'commonjs2',
595
- chunkFilename: `${chunksContext}/[id]${isProd ? '.[chunkhash]' : ''}.js`,
609
+ libraryTarget: "commonjs2",
610
+ chunkFilename: `${chunksContext}/[id]${isProd ? ".[chunkhash]" : ""}.js`,
596
611
  assetModuleFilename,
597
612
  ...(isProd && { clean: { keep: keepOutsideBuild() } }),
598
613
  },
@@ -606,15 +621,15 @@ module.exports = async function (inMeteor = {}, argv = {}) {
606
621
  parser: {
607
622
  javascript: {
608
623
  // Dynamic imports on the server are treated as bundled in the same chunk
609
- dynamicImportMode: 'eager',
624
+ dynamicImportMode: "eager",
610
625
  },
611
626
  },
612
627
  },
613
628
  resolve: {
614
629
  extensions,
615
630
  alias,
616
- modules: ['node_modules', path.resolve(projectDir)],
617
- conditionNames: ['import', 'require', 'node', 'default'],
631
+ modules: ["node_modules", path.resolve(projectDir)],
632
+ conditionNames: ["import", "require", "node", "default"],
618
633
  },
619
634
  externals,
620
635
  externalsPresets: { node: true },
@@ -622,18 +637,18 @@ module.exports = async function (inMeteor = {}, argv = {}) {
622
637
  new DefinePlugin(
623
638
  isTest && (isTestModule || isTestEager)
624
639
  ? {
625
- 'Meteor.isTest': JSON.stringify(isTest && !isTestFullApp),
626
- 'Meteor.isAppTest': JSON.stringify(isTest && isTestFullApp),
627
- 'Meteor.isDevelopment': JSON.stringify(isDev),
640
+ "Meteor.isTest": JSON.stringify(isTest && !isTestFullApp),
641
+ "Meteor.isAppTest": JSON.stringify(isTest && isTestFullApp),
642
+ "Meteor.isDevelopment": JSON.stringify(isDev),
628
643
  }
629
644
  : {
630
- 'Meteor.isClient': JSON.stringify(false),
631
- 'Meteor.isServer': JSON.stringify(true),
632
- 'Meteor.isTest': JSON.stringify(isTestLike && !isTestFullApp),
633
- 'Meteor.isAppTest': JSON.stringify(isTestLike && isTestFullApp),
634
- 'Meteor.isDevelopment': JSON.stringify(isDev),
635
- 'Meteor.isProduction': JSON.stringify(isProd),
636
- },
645
+ "Meteor.isClient": JSON.stringify(false),
646
+ "Meteor.isServer": JSON.stringify(true),
647
+ "Meteor.isTest": JSON.stringify(isTestLike && !isTestFullApp),
648
+ "Meteor.isAppTest": JSON.stringify(isTestLike && isTestFullApp),
649
+ "Meteor.isDevelopment": JSON.stringify(isDev),
650
+ "Meteor.isProduction": JSON.stringify(isProd),
651
+ }
637
652
  ),
638
653
  ...bannerPluginConfig,
639
654
  requireExternalsPlugin,
@@ -641,99 +656,15 @@ module.exports = async function (inMeteor = {}, argv = {}) {
641
656
  ...doctorPluginConfig,
642
657
  ],
643
658
  watchOptions,
644
- devtool: isDevEnvironment || isNative || isTest ? 'source-map' : 'hidden-source-map',
659
+ devtool:
660
+ isDevEnvironment || isNative || isTest
661
+ ? "source-map"
662
+ : "hidden-source-map",
645
663
  ...((isDevEnvironment || (isTest && !isTestEager) || isNative) &&
646
664
  cacheStrategy),
647
665
  ...lazyCompilationConfig,
648
666
  };
649
667
 
650
- // Helper function to load and process config files
651
- async function loadAndProcessConfig(configPath, configType, Meteor, argv, isAngularEnabled) {
652
- try {
653
- // Load the config file
654
- let config;
655
- if (path.extname(configPath) === '.mjs') {
656
- // For ESM modules, we need to use dynamic import
657
- const fileUrl = `file://${configPath}`;
658
- const module = await import(fileUrl);
659
- config = module.default || module;
660
- } else {
661
- // For CommonJS modules, we can use require
662
- config = require(configPath)?.default || require(configPath);
663
- }
664
-
665
- // Process the config
666
- const rawConfig = typeof config === 'function' ? config(Meteor, argv) : config;
667
- const resolvedConfig = await Promise.resolve(rawConfig);
668
- const userConfig = resolvedConfig && '0' in resolvedConfig ? resolvedConfig[0] : resolvedConfig;
669
-
670
- // Define omitted paths and warning function
671
- const omitPaths = [
672
- "name",
673
- "target",
674
- "entry",
675
- "output.path",
676
- "output.filename",
677
- ...(Meteor.isServer ? ["optimization.splitChunks", "optimization.runtimeChunk"] : []),
678
- ].filter(Boolean);
679
-
680
- const warningFn = path => {
681
- if (isAngularEnabled) return;
682
- console.warn(
683
- `[${configType}] Ignored custom "${path}" — reserved for Meteor-Rspack integration.`,
684
- );
685
- };
686
-
687
- // Clean omitted paths and merge Meteor Rspack fragments
688
- let nextConfig = cleanOmittedPaths(userConfig, {
689
- omitPaths,
690
- warningFn,
691
- });
692
- nextConfig = mergeMeteorRspackFragments(nextConfig);
693
-
694
- return nextConfig;
695
- } catch (error) {
696
- console.error(`Error loading ${configType} from ${configPath}:`, error);
697
- if (configType === 'rspack.config.js') {
698
- throw error; // Only rethrow for project config
699
- }
700
- return null;
701
- }
702
- }
703
-
704
- // Load and apply project-level overrides for the selected build
705
- // Check if we're in a Meteor package directory by looking at the path
706
- const isMeteorPackageConfig = projectDir.includes('/packages/rspack');
707
- if (fs.existsSync(projectConfigPath) && !isMeteorPackageConfig) {
708
- // Check if there's a .mjs or .cjs version of the config file
709
- const mjsConfigPath = projectConfigPath.replace(/\.js$/, '.mjs');
710
- const cjsConfigPath = projectConfigPath.replace(/\.js$/, '.cjs');
711
-
712
- let projectConfigPathToUse = projectConfigPath;
713
- if (fs.existsSync(mjsConfigPath)) {
714
- projectConfigPathToUse = mjsConfigPath;
715
- } else if (fs.existsSync(cjsConfigPath)) {
716
- projectConfigPathToUse = cjsConfigPath;
717
- }
718
-
719
- const nextUserConfig = await loadAndProcessConfig(
720
- projectConfigPathToUse,
721
- 'rspack.config.js',
722
- Meteor,
723
- argv,
724
- isAngularEnabled
725
- );
726
-
727
- if (nextUserConfig) {
728
- if (Meteor.isClient) {
729
- clientConfig = mergeSplitOverlap(clientConfig, nextUserConfig);
730
- }
731
- if (Meteor.isServer) {
732
- serverConfig = mergeSplitOverlap(serverConfig, nextUserConfig);
733
- }
734
- }
735
- }
736
-
737
668
  // Establish Angular overrides to ensure proper integration
738
669
  const angularExpandConfig = isAngularEnabled
739
670
  ? {
@@ -771,29 +702,12 @@ module.exports = async function (inMeteor = {}, argv = {}) {
771
702
  );
772
703
  config = mergeSplitOverlap(config, testClientExpandConfig);
773
704
 
774
- // Check for override config file (extra file to override everything)
775
- if (projectConfigPath) {
776
- const configDir = path.dirname(projectConfigPath);
777
- const configFileName = path.basename(projectConfigPath);
778
- const configExt = path.extname(configFileName);
779
- const configNameWithoutExt = configFileName.replace(configExt, '');
780
- const configNameFull = `${configNameWithoutExt}.override${configExt}`;
781
- const overrideConfigPath = path.join(configDir, configNameFull);
782
-
783
- if (fs.existsSync(overrideConfigPath)) {
784
- const nextOverrideConfig = await loadAndProcessConfig(
785
- overrideConfigPath,
786
- configNameFull,
787
- Meteor,
788
- argv,
789
- isAngularEnabled
790
- );
791
-
792
- if (nextOverrideConfig) {
793
- // Apply override config as the last step
794
- config = mergeSplitOverlap(config, nextOverrideConfig);
795
- }
796
- }
705
+ if (nextUserConfig) {
706
+ config = mergeSplitOverlap(config, nextUserConfig);
707
+ }
708
+
709
+ if (nextOverrideConfig) {
710
+ config = mergeSplitOverlap(config, nextOverrideConfig);
797
711
  }
798
712
 
799
713
  const shouldDisablePlugins = config?.disablePlugins != null;
@@ -803,15 +717,18 @@ module.exports = async function (inMeteor = {}, argv = {}) {
803
717
  }
804
718
 
805
719
  if (Meteor.isDebug || Meteor.isVerbose) {
806
- console.log('Config:', inspect(config, { depth: null, colors: true }));
720
+ console.log("Config:", inspect(config, { depth: null, colors: true }));
807
721
  }
808
722
 
809
723
  // Check if lazyCompilation is enabled and warn the user
810
- if (config.lazyCompilation === true || typeof config.lazyCompilation === 'object') {
724
+ if (
725
+ config.lazyCompilation === true ||
726
+ typeof config.lazyCompilation === "object"
727
+ ) {
811
728
  console.warn(
812
- '\n⚠️ Warning: lazyCompilation may not work correctly in the current Meteor-Rspack integration.\n' +
813
- ' This feature will be evaluated for support in future Meteor versions.\n' +
814
- ' If you encounter any issues, please disable it in your rspack config.\n',
729
+ "\n⚠️ Warning: lazyCompilation may not work correctly in the current Meteor-Rspack integration.\n" +
730
+ " This feature will be evaluated for support in future Meteor versions.\n" +
731
+ " If you encounter any issues, please disable it in your rspack config.\n"
815
732
  );
816
733
  }
817
734