@meteorjs/rspack 0.0.13 → 0.0.14
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.
- package/lib/mergeRulesSplitOverlap.js +143 -0
- package/package.json +1 -1
- package/rspack.config.js +6 -3
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for merging webpack/rspack configurations with special handling for
|
|
3
|
+
* overlapping file extensions in module rules.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { mergeWithCustomize } from 'webpack-merge';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* File extensions to check when determining rule overlaps.
|
|
10
|
+
*/
|
|
11
|
+
export const EXT_CATALOG = [
|
|
12
|
+
'.tsx', '.ts', '.mts', '.cts',
|
|
13
|
+
'.jsx', '.js', '.mjs', '.cjs',
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Converts rule.test to predicate functions.
|
|
18
|
+
* @param {Object} rule - Rule object
|
|
19
|
+
* @returns {Function[]} Predicate functions
|
|
20
|
+
*/
|
|
21
|
+
function testsFrom(rule) {
|
|
22
|
+
const t = rule.test;
|
|
23
|
+
if (!t) return [() => true]; // no test means match all; you can tighten if you want
|
|
24
|
+
const arr = Array.isArray(t) ? t : [t];
|
|
25
|
+
return arr.map(el => {
|
|
26
|
+
if (el instanceof RegExp) return (s) => el.test(s);
|
|
27
|
+
if (typeof el === 'function') return el;
|
|
28
|
+
if (typeof el === 'string') {
|
|
29
|
+
// Webpack allows string match; treat as substring
|
|
30
|
+
return (s) => s.includes(el);
|
|
31
|
+
}
|
|
32
|
+
return () => false;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Checks if rule matches a file extension.
|
|
38
|
+
* @param {Object} rule - Rule object
|
|
39
|
+
* @param {string} ext - File extension
|
|
40
|
+
* @returns {boolean} True if matches
|
|
41
|
+
*/
|
|
42
|
+
function ruleMatchesExt(rule, ext) {
|
|
43
|
+
// simulate a filename to test against
|
|
44
|
+
const filename = `x${ext}`;
|
|
45
|
+
const preds = testsFrom(rule);
|
|
46
|
+
return preds.some(fn => {
|
|
47
|
+
try { return !!fn(filename); } catch { return false; }
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates regex for matching file extensions.
|
|
53
|
+
* @param {string[]} exts - File extensions
|
|
54
|
+
* @returns {RegExp} Regex like /\.(js|jsx)$/
|
|
55
|
+
*/
|
|
56
|
+
function regexFromExts(exts) {
|
|
57
|
+
const body = exts.map(e => e.replace(/^\./, '')).join('|');
|
|
58
|
+
return new RegExp(`\\.(${body})$`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Clones rule with new test property.
|
|
63
|
+
* @param {Object} rule - Rule to clone
|
|
64
|
+
* @param {RegExp|Function|string} newTest - New test value
|
|
65
|
+
* @returns {Object} Cloned rule
|
|
66
|
+
*/
|
|
67
|
+
function cloneWithTest(rule, newTest) {
|
|
68
|
+
return { ...rule, test: newTest };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Merges rules with special handling for overlapping extensions.
|
|
73
|
+
* - Replaces overlapping parts with B rules
|
|
74
|
+
* - Preserves non-overlapping parts from A rules
|
|
75
|
+
*
|
|
76
|
+
* @param {Array} aRules - Base rules
|
|
77
|
+
* @param {Array} bRules - Rules to merge in
|
|
78
|
+
* @returns {Array} Merged rules
|
|
79
|
+
*/
|
|
80
|
+
function splitOverlapRulesMerge(aRules, bRules) {
|
|
81
|
+
const result = [...aRules];
|
|
82
|
+
|
|
83
|
+
for (const bRule of bRules) {
|
|
84
|
+
// Try to find an A rule that overlaps B by extensions
|
|
85
|
+
let replaced = false;
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < result.length; i++) {
|
|
88
|
+
const aRule = result[i];
|
|
89
|
+
|
|
90
|
+
// Determine which extensions each rule matches (within our catalog)
|
|
91
|
+
const aExts = EXT_CATALOG.filter(ext => ruleMatchesExt(aRule, ext));
|
|
92
|
+
const bExts = EXT_CATALOG.filter(ext => ruleMatchesExt(bRule, ext));
|
|
93
|
+
|
|
94
|
+
if (aExts.length === 0 || bExts.length === 0) {
|
|
95
|
+
continue; // nothing meaningful to compare in our catalog
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const overlap = aExts.filter(e => bExts.includes(e));
|
|
99
|
+
if (overlap.length === 0) continue;
|
|
100
|
+
|
|
101
|
+
// 1) Replace the overlapping A rule with B
|
|
102
|
+
result[i] = bRule;
|
|
103
|
+
|
|
104
|
+
// 2) Add a "residual" A rule for the non-overlapping extensions
|
|
105
|
+
const residual = aExts.filter(e => !overlap.includes(e));
|
|
106
|
+
if (residual.length > 0) {
|
|
107
|
+
const residualRule = cloneWithTest(aRule, regexFromExts(residual));
|
|
108
|
+
result.splice(i, 0, residualRule); // keep residual before B, or after—your choice
|
|
109
|
+
i++; // skip over the newly inserted residual
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
replaced = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// If we didn’t overlap with any A rule, just add B
|
|
117
|
+
if (!replaced) {
|
|
118
|
+
result.push(bRule);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Merges webpack/rspack configs with smart handling of overlapping rules.
|
|
127
|
+
*
|
|
128
|
+
* @param {...Object} configs - Configs to merge
|
|
129
|
+
* @returns {Object} Merged config
|
|
130
|
+
*/
|
|
131
|
+
export function mergeSplitOverlap(...configs) {
|
|
132
|
+
return mergeWithCustomize({
|
|
133
|
+
customizeArray(a, b, key) {
|
|
134
|
+
if (key === 'module.rules') {
|
|
135
|
+
const aRules = Array.isArray(a) ? a : [];
|
|
136
|
+
const bRules = Array.isArray(b) ? b : [];
|
|
137
|
+
return splitOverlapRulesMerge(aRules, bRules);
|
|
138
|
+
}
|
|
139
|
+
// fall through to default merging
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
})(...configs);
|
|
143
|
+
}
|
package/package.json
CHANGED
package/rspack.config.js
CHANGED
|
@@ -7,6 +7,7 @@ import { inspect } from "node:util";
|
|
|
7
7
|
|
|
8
8
|
import { RequireExternalsPlugin } from './plugins/RequireExtenalsPlugin.js';
|
|
9
9
|
import { getMeteorAppSwcConfig } from "./lib/swc.js";
|
|
10
|
+
import { mergeSplitOverlap } from './lib/mergeRulesSplitOverlap.js';
|
|
10
11
|
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
|
|
@@ -66,7 +67,7 @@ function createSwcConfig({ isTypescriptEnabled, isJsxEnabled, isTsxEnabled, exte
|
|
|
66
67
|
const customConfig = getMeteorAppSwcConfig() || {};
|
|
67
68
|
const swcConfig = merge(defaultConfig, customConfig);
|
|
68
69
|
return {
|
|
69
|
-
test: /\.[
|
|
70
|
+
test: /\.(?:[mc]?js|jsx|[mc]?ts|tsx)$/i,
|
|
70
71
|
exclude: /node_modules|\.meteor\/local/,
|
|
71
72
|
loader: 'builtin:swc-loader',
|
|
72
73
|
options: swcConfig,
|
|
@@ -175,6 +176,8 @@ export default function (inMeteor = {}, argv = {}) {
|
|
|
175
176
|
...(isCoffeescriptEnabled ? ['.coffee'] : []),
|
|
176
177
|
'.ts',
|
|
177
178
|
'.tsx',
|
|
179
|
+
'.mts',
|
|
180
|
+
'.cts',
|
|
178
181
|
'.js',
|
|
179
182
|
'.jsx',
|
|
180
183
|
'.mjs',
|
|
@@ -340,10 +343,10 @@ export default function (inMeteor = {}, argv = {}) {
|
|
|
340
343
|
: projectConfig;
|
|
341
344
|
|
|
342
345
|
if (Meteor.isClient) {
|
|
343
|
-
clientConfig =
|
|
346
|
+
clientConfig = mergeSplitOverlap(clientConfig, userConfig);
|
|
344
347
|
}
|
|
345
348
|
if (Meteor.isServer) {
|
|
346
|
-
serverConfig =
|
|
349
|
+
serverConfig = mergeSplitOverlap(serverConfig, userConfig);
|
|
347
350
|
}
|
|
348
351
|
}
|
|
349
352
|
|