@meteorjs/rspack 0.0.59 → 0.0.60

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.
@@ -42,11 +42,17 @@ function prepareMeteorRspackConfig(customConfig, opts = {}) {
42
42
  * Merge all `{prefix}<n>` fragments into `config` using `mergeSplitOverlap`,
43
43
  * then remove those temporary keys. Mutates `config`.
44
44
  *
45
- * Order: fragments are applied in ascending numeric order (1, 2, 3, ...).
45
+ * Position-aware merge:
46
+ * Walk the config in insertion order and fold:
47
+ * - for a fragment key: out = mergeSplitOverlap(out, fragment)
48
+ * - for a normal key: out = mergeSplitOverlap(out, { [key]: value })
49
+ *
50
+ * Result: fragments behave like spreads at their exact position;
51
+ * later inline keys override earlier ones (including fragments).
46
52
  *
47
53
  * @param {object} config
48
54
  * @param {{ prefix?: string }} [opts]
49
- * @returns {object} The same (mutated) config instance.
55
+ * @returns {object} same (mutated) config
50
56
  */
51
57
  function mergeMeteorRspackFragments(config, opts = {}) {
52
58
  if (!config || typeof config !== "object" || Array.isArray(config)) {
@@ -54,39 +60,36 @@ function mergeMeteorRspackFragments(config, opts = {}) {
54
60
  }
55
61
  const prefix = opts.prefix || DEFAULT_PREFIX;
56
62
 
57
- // Collect fragment keys like "meteorRspackConfig12"
58
- const tempKeys = Object.keys(config)
59
- .filter((k) => k.startsWith(prefix) && /^\d+$/.test(k.slice(prefix.length)))
60
- .map((k) => [k, parseInt(k.slice(prefix.length), 10)])
61
- .sort((a, b) => a[1] - b[1])
62
- .map(([k]) => k);
63
+ let out = {};
64
+ for (const key of Object.keys(config)) {
65
+ const val = config[key];
63
66
 
64
- if (tempKeys.length === 0) return config;
67
+ const isFragment =
68
+ typeof key === "string" &&
69
+ key.startsWith(prefix) &&
70
+ /^\d+$/.test(key.slice(prefix.length));
65
71
 
66
- // Apply each fragment with your merge policy
67
- for (const k of tempKeys) {
68
- const fragment = config[k];
69
- if (!fragment || typeof fragment !== "object" || Array.isArray(fragment)) {
70
- throw new Error(`Fragment "${k}" must be a plain object`);
72
+ if (isFragment) {
73
+ if (!val || typeof val !== "object" || Array.isArray(val)) {
74
+ throw new Error(`Fragment "${key}" must be a plain object`);
75
+ }
76
+ out = mergeSplitOverlap(out, val);
77
+ } else {
78
+ out = mergeSplitOverlap(out, { [key]: val });
71
79
  }
72
- const merged = mergeSplitOverlap(config, fragment);
73
-
74
- // Keep object identity: replace contents of `config` with `merged`
75
- replaceObject(config, merged);
76
80
  }
77
81
 
78
- // Strip the temp keys at the end
79
- for (const k of tempKeys) delete config[k];
80
-
82
+ // keep object identity; fragments disappear because `out` doesn't include them
83
+ replaceObject(config, out);
81
84
  return config;
82
85
  }
83
86
 
84
87
  function replaceObject(target, source) {
85
- for (const key of Object.keys(target)) {
86
- if (!(key in source)) delete target[key];
88
+ for (const k of Object.keys(target)) {
89
+ if (!(k in source)) delete target[k];
87
90
  }
88
- for (const key of Object.keys(source)) {
89
- target[key] = source[key];
91
+ for (const k of Object.keys(source)) {
92
+ target[k] = source[k];
90
93
  }
91
94
  }
92
95
 
@@ -54,6 +54,30 @@ function compileWithRspack(deps, { options = {} } = {}) {
54
54
  });
55
55
  }
56
56
 
57
+ /**
58
+ * Enable or disable Rspack cache config
59
+ * Usage: setCache(false)
60
+ *
61
+ * @param {boolean} enabled
62
+ * @param {Record<string, object>} cacheConfig
63
+ * @returns {Record<string, object>} `{ meteorRspackConfigX: { cache: {} } }`
64
+ */
65
+ function setCache(
66
+ enabled,
67
+ cacheConfig = { cache: true, experiments: { cache: true } },
68
+ ) {
69
+ return prepareMeteorRspackConfig(
70
+ enabled
71
+ ? cacheConfig
72
+ : {
73
+ cache: false, // disable cache
74
+ experiments: {
75
+ cache: false, // disable persistent cache (experimental flag)
76
+ },
77
+ },
78
+ );
79
+ }
80
+
57
81
  /**
58
82
  * Build an alias map that disables ALL Node core modules in a web build.
59
83
  * - Includes both 'fs' and 'node:fs' keys
@@ -77,5 +101,6 @@ function makeWebNodeBuiltinsAlias(extras = []) {
77
101
  module.exports = {
78
102
  compileWithMeteor,
79
103
  compileWithRspack,
104
+ setCache,
80
105
  makeWebNodeBuiltinsAlias,
81
106
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meteorjs/rspack",
3
- "version": "0.0.59",
3
+ "version": "0.0.60",
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
@@ -14,6 +14,7 @@ const { mergeMeteorRspackFragments } = require("./lib/meteorRspackConfigFactory.
14
14
  const {
15
15
  compileWithMeteor,
16
16
  compileWithRspack,
17
+ setCache,
17
18
  makeWebNodeBuiltinsAlias,
18
19
  } = require('./lib/meteorRspackHelpers.js');
19
20
 
@@ -137,6 +138,43 @@ function createSwcConfig({
137
138
  };
138
139
  }
139
140
 
141
+ function createRemoteDevServerConfig() {
142
+ const rootUrl = process.env.ROOT_URL;
143
+ let hostname;
144
+ let protocol;
145
+ let port;
146
+
147
+ if (rootUrl) {
148
+ try {
149
+ const url = new URL(rootUrl);
150
+ // Detect if it's remote (not localhost or 127.x)
151
+ const isLocal =
152
+ url.hostname.includes('localhost') ||
153
+ url.hostname.startsWith('127.') ||
154
+ url.hostname.endsWith('.local');
155
+ if (!isLocal) {
156
+ hostname = url.hostname;
157
+ protocol = url.protocol === 'https:' ? 'wss' : 'ws';
158
+ port = url.port ? Number(url.port) : (url.protocol === 'https:' ? 443 : 80);
159
+
160
+ return {
161
+ client: {
162
+ webSocketURL: {
163
+ hostname,
164
+ port,
165
+ protocol,
166
+ },
167
+ },
168
+ };
169
+ }
170
+ } catch (err) {
171
+ console.warn(`Invalid ROOT_URL "${rootUrl}", falling back to localhost`);
172
+ }
173
+ }
174
+
175
+ // If local doesn't provide any extra config
176
+ return {};
177
+ }
140
178
 
141
179
  // Keep files outside of build folders
142
180
  function keepOutsideBuild() {
@@ -218,12 +256,23 @@ module.exports = async function (inMeteor = {}, argv = {}) {
218
256
  const buildOutputDir = path.resolve(projectDir, buildContext, outputDir);
219
257
  Meteor.buildOutputDir = buildOutputDir;
220
258
 
259
+ const cacheStrategy = createCacheStrategy(
260
+ mode,
261
+ (Meteor.isClient && 'client') || 'server',
262
+ { projectConfigPath, configPath }
263
+ );
264
+
221
265
  // Expose Meteor's helpers to expand Rspack configs
222
266
  Meteor.compileWithMeteor = deps => compileWithMeteor(deps);
223
267
  Meteor.compileWithRspack = deps =>
224
268
  compileWithRspack(deps, {
225
269
  options: Meteor.swcConfigOptions,
226
270
  });
271
+ Meteor.setCache = enabled =>
272
+ setCache(
273
+ !!enabled,
274
+ enabled === 'memory' ? undefined : cacheStrategy
275
+ );
227
276
 
228
277
  // Add HtmlRspackPlugin function to Meteor
229
278
  Meteor.HtmlRspackPlugin = (options = {}) => {
@@ -432,6 +481,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
432
481
  devtool: isDevEnvironment || isNative || isTest ? 'source-map' : 'hidden-source-map',
433
482
  ...(isDevEnvironment && {
434
483
  devServer: {
484
+ ...createRemoteDevServerConfig(),
435
485
  static: { directory: clientOutputDir, publicPath: '/__rspack__/' },
436
486
  hot: true,
437
487
  liveReload: true,
@@ -443,7 +493,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
443
493
  },
444
494
  },
445
495
  }),
446
- ...merge(createCacheStrategy(mode, "client", { projectConfigPath, configPath }), { experiments: { css: true } })
496
+ ...merge(cacheStrategy, { experiments: { css: true } })
447
497
  };
448
498
 
449
499
 
@@ -526,7 +576,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
526
576
  watchOptions,
527
577
  devtool: isDevEnvironment || isNative || isTest ? 'source-map' : 'hidden-source-map',
528
578
  ...((isDevEnvironment || (isTest && !isTestEager) || isNative) &&
529
- createCacheStrategy(mode, "server", { projectConfigPath, configPath })),
579
+ cacheStrategy),
530
580
  };
531
581
 
532
582
  // Load and apply project-level overrides for the selected build
@@ -583,22 +633,27 @@ module.exports = async function (inMeteor = {}, argv = {}) {
583
633
  );
584
634
  };
585
635
 
636
+ let nextUserConfig = cleanOmittedPaths(userConfig, {
637
+ omitPaths,
638
+ warningFn,
639
+ });
640
+ nextUserConfig = mergeMeteorRspackFragments(nextUserConfig);
641
+
586
642
  if (Meteor.isClient) {
587
643
  clientConfig = mergeSplitOverlap(
588
644
  clientConfig,
589
- cleanOmittedPaths(userConfig, { omitPaths, warningFn }),
645
+ nextUserConfig
590
646
  );
591
647
  }
592
648
  if (Meteor.isServer) {
593
649
  serverConfig = mergeSplitOverlap(
594
650
  serverConfig,
595
- cleanOmittedPaths(userConfig, { omitPaths, warningFn }),
651
+ nextUserConfig
596
652
  );
597
653
  }
598
654
  }
599
655
 
600
- const sideConfig = isClient ? clientConfig : serverConfig;
601
- const config = mergeMeteorRspackFragments(sideConfig);
656
+ const config = isClient ? clientConfig : serverConfig;
602
657
 
603
658
  if (Meteor.isDebug || Meteor.isVerbose) {
604
659
  console.log('Config:', inspect(config, { depth: null, colors: true }));