@meteorjs/rspack 0.0.56 → 0.0.57
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.
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { mergeWithCustomize } = require('webpack-merge');
|
|
7
|
+
const isEqual = require('fast-deep-equal');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* File extensions to check when determining rule overlaps.
|
|
@@ -87,6 +88,10 @@ function splitOverlapRulesMerge(aRules, bRules) {
|
|
|
87
88
|
for (let i = 0; i < result.length; i++) {
|
|
88
89
|
const aRule = result[i];
|
|
89
90
|
|
|
91
|
+
|
|
92
|
+
const isMergeableRule = isEqual(aRule?.include || [], bRule?.include || []);
|
|
93
|
+
if (!isMergeableRule) continue;
|
|
94
|
+
|
|
90
95
|
// Determine which extensions each rule matches (within our catalog)
|
|
91
96
|
const aExts = EXT_CATALOG.filter(ext => ruleMatchesExt(aRule, ext));
|
|
92
97
|
const bExts = EXT_CATALOG.filter(ext => ruleMatchesExt(bRule, ext));
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// meteorRspackConfigFactory.js
|
|
2
|
+
|
|
3
|
+
const { mergeSplitOverlap } = require("./mergeRulesSplitOverlap.js");
|
|
4
|
+
|
|
5
|
+
const DEFAULT_PREFIX = "meteorRspackConfig";
|
|
6
|
+
let counter = 0;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Create a uniquely keyed Rspack config fragment.
|
|
10
|
+
* Example return: { meteorRspackConfig1: { ...customConfig } }
|
|
11
|
+
*
|
|
12
|
+
* @param {object} customConfig
|
|
13
|
+
* @param {{ key?: number|string, prefix?: string }} [opts]
|
|
14
|
+
* @returns {Record<string, object>}
|
|
15
|
+
*/
|
|
16
|
+
function prepareMeteorRspackConfig(customConfig, opts = {}) {
|
|
17
|
+
if (!customConfig || typeof customConfig !== "object") {
|
|
18
|
+
throw new TypeError("customConfig must be an object");
|
|
19
|
+
}
|
|
20
|
+
const prefix = opts.prefix || DEFAULT_PREFIX;
|
|
21
|
+
|
|
22
|
+
let name;
|
|
23
|
+
if (opts.key != null) {
|
|
24
|
+
const k = String(opts.key).trim();
|
|
25
|
+
if (/^\d+$/.test(k)) name = `${prefix}${k}`;
|
|
26
|
+
else if (k.startsWith(prefix) && /^\d+$/.test(k.slice(prefix.length)))
|
|
27
|
+
name = k;
|
|
28
|
+
else
|
|
29
|
+
throw new Error(`opts.key must be a positive integer or "${prefix}<n>"`);
|
|
30
|
+
|
|
31
|
+
const n = parseInt(name.slice(prefix.length), 10);
|
|
32
|
+
if (Number.isFinite(n) && n > counter) counter = n;
|
|
33
|
+
} else {
|
|
34
|
+
counter += 1;
|
|
35
|
+
name = `${prefix}${counter}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { [name]: customConfig };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Merge all `{prefix}<n>` fragments into `config` using `mergeSplitOverlap`,
|
|
43
|
+
* then remove those temporary keys. Mutates `config`.
|
|
44
|
+
*
|
|
45
|
+
* Order: fragments are applied in ascending numeric order (1, 2, 3, ...).
|
|
46
|
+
*
|
|
47
|
+
* @param {object} config
|
|
48
|
+
* @param {{ prefix?: string }} [opts]
|
|
49
|
+
* @returns {object} The same (mutated) config instance.
|
|
50
|
+
*/
|
|
51
|
+
function mergeMeteorRspackFragments(config, opts = {}) {
|
|
52
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
53
|
+
throw new TypeError("config must be a plain object");
|
|
54
|
+
}
|
|
55
|
+
const prefix = opts.prefix || DEFAULT_PREFIX;
|
|
56
|
+
|
|
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
|
+
|
|
64
|
+
if (tempKeys.length === 0) return config;
|
|
65
|
+
|
|
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`);
|
|
71
|
+
}
|
|
72
|
+
const merged = mergeSplitOverlap(config, fragment);
|
|
73
|
+
|
|
74
|
+
// Keep object identity: replace contents of `config` with `merged`
|
|
75
|
+
replaceObject(config, merged);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Strip the temp keys at the end
|
|
79
|
+
for (const k of tempKeys) delete config[k];
|
|
80
|
+
|
|
81
|
+
return config;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function replaceObject(target, source) {
|
|
85
|
+
for (const key of Object.keys(target)) {
|
|
86
|
+
if (!(key in source)) delete target[key];
|
|
87
|
+
}
|
|
88
|
+
for (const key of Object.keys(source)) {
|
|
89
|
+
target[key] = source[key];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = {
|
|
94
|
+
prepareMeteorRspackConfig,
|
|
95
|
+
mergeMeteorRspackFragments,
|
|
96
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const { prepareMeteorRspackConfig } = require("./meteorRspackConfigFactory");
|
|
3
|
+
const { builtinModules } = require("module");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a package directory from node resolution.
|
|
7
|
+
* @param {string} pkg
|
|
8
|
+
* @returns {string} absolute directory of the package
|
|
9
|
+
*/
|
|
10
|
+
function pkgDir(pkg) {
|
|
11
|
+
const resolved = require.resolve(`${pkg}/package.json`, {
|
|
12
|
+
paths: [process.cwd()],
|
|
13
|
+
});
|
|
14
|
+
return path.dirname(resolved);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Wrap externals for Meteor runtime (marks deps as externals).
|
|
19
|
+
* Usage: compileWithMeteor(["sharp", "vimeo", "fs"])
|
|
20
|
+
*
|
|
21
|
+
* @param {string[]} deps - package names or module IDs
|
|
22
|
+
* @returns {Record<string, object>} `{ meteorRspackConfigX: { externals: [...] } }`
|
|
23
|
+
*/
|
|
24
|
+
function compileWithMeteor(deps) {
|
|
25
|
+
const flat = deps.flat().filter(Boolean);
|
|
26
|
+
return prepareMeteorRspackConfig({
|
|
27
|
+
externals: flat,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Add SWC transpilation rules limited to specific deps (monorepo-friendly).
|
|
33
|
+
* Usage: compileWithRspack(["@org/lib-a", "zod"])
|
|
34
|
+
*
|
|
35
|
+
* Requires global `Meteor.swcConfigOptions` (as in your setup).
|
|
36
|
+
*
|
|
37
|
+
* @param {string[]} deps - package names to include in SWC loader
|
|
38
|
+
* @returns {Record<string, object>} `{ meteorRspackConfigX: { module: { rules: [...] } } }`
|
|
39
|
+
*/
|
|
40
|
+
function compileWithRspack(deps, { options = {} } = {}) {
|
|
41
|
+
const includeDirs = deps.flat().filter(Boolean).map(pkgDir);
|
|
42
|
+
|
|
43
|
+
return prepareMeteorRspackConfig({
|
|
44
|
+
module: {
|
|
45
|
+
rules: [
|
|
46
|
+
{
|
|
47
|
+
test: /\.(?:[mc]?js|jsx|[mc]?ts|tsx)$/i,
|
|
48
|
+
include: includeDirs,
|
|
49
|
+
loader: "builtin:swc-loader",
|
|
50
|
+
options,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build an alias map that disables ALL Node core modules in a web build.
|
|
59
|
+
* - Includes both 'fs' and 'node:fs' keys
|
|
60
|
+
* - Optional extras let you block non-core modules too
|
|
61
|
+
*/
|
|
62
|
+
function makeWebNodeBuiltinsAlias(extras = []) {
|
|
63
|
+
// Strip potential 'node:' prefixes then add both forms
|
|
64
|
+
const core = new Set(builtinModules.map((m) => m.replace(/^node:/, "")));
|
|
65
|
+
|
|
66
|
+
const names = new Set();
|
|
67
|
+
for (const m of core) {
|
|
68
|
+
names.add(m); // e.g. 'fs'
|
|
69
|
+
names.add(`node:${m}`); // e.g. 'node:fs'
|
|
70
|
+
}
|
|
71
|
+
for (const x of extras) names.add(x);
|
|
72
|
+
|
|
73
|
+
// Map every name to false (causes hard error if imported)
|
|
74
|
+
return Object.fromEntries([...names].map((m) => [m, false]));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
compileWithMeteor,
|
|
79
|
+
compileWithRspack,
|
|
80
|
+
makeWebNodeBuiltinsAlias,
|
|
81
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meteorjs/rspack",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.57",
|
|
4
4
|
"description": "Configuration logic for using Rspack in Meteor projects",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"author": "",
|
|
8
8
|
"license": "ISC",
|
|
9
9
|
"dependencies": {
|
|
10
|
+
"fast-deep-equal": "^3.1.3",
|
|
10
11
|
"ignore-loader": "^0.1.2",
|
|
11
12
|
"webpack-merge": "^6.0.1"
|
|
12
13
|
},
|
package/rspack.config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { DefinePlugin, BannerPlugin } = require('@rspack/core');
|
|
1
|
+
const { DefinePlugin, BannerPlugin, NormalModuleReplacementPlugin } = require('@rspack/core');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const { inspect } = require('node:util');
|
|
4
4
|
const path = require('path');
|
|
@@ -10,6 +10,12 @@ const HtmlRspackPlugin = require('./plugins/HtmlRspackPlugin.js');
|
|
|
10
10
|
const { RequireExternalsPlugin } = require('./plugins/RequireExtenalsPlugin.js');
|
|
11
11
|
const { generateEagerTestFile } = require("./lib/test.js");
|
|
12
12
|
const { getMeteorIgnoreEntries, createIgnoreGlobConfig } = require("./lib/ignore");
|
|
13
|
+
const { mergeMeteorRspackFragments } = require("./lib/meteorRspackConfigFactory.js");
|
|
14
|
+
const {
|
|
15
|
+
compileWithMeteor,
|
|
16
|
+
compileWithRspack,
|
|
17
|
+
makeWebNodeBuiltinsAlias,
|
|
18
|
+
} = require('./lib/meteorRspackHelpers.js');
|
|
13
19
|
|
|
14
20
|
// Safe require that doesn't throw if the module isn't found
|
|
15
21
|
function safeRequire(moduleName) {
|
|
@@ -27,17 +33,52 @@ function safeRequire(moduleName) {
|
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
// Persistent filesystem cache strategy
|
|
30
|
-
function createCacheStrategy(mode, side) {
|
|
36
|
+
function createCacheStrategy(mode, side, { projectConfigPath, configPath } = {}) {
|
|
37
|
+
// Check for configuration files
|
|
38
|
+
const tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
|
|
39
|
+
const hasTsconfig = fs.existsSync(tsconfigPath);
|
|
40
|
+
const babelRcConfig = path.join(process.cwd(), '.babelrc');
|
|
41
|
+
const hasBabelRcConfig = fs.existsSync(babelRcConfig);
|
|
42
|
+
const babelJsConfig = path.join(process.cwd(), 'babel.config.js');
|
|
43
|
+
const hasBabelJsConfig = fs.existsSync(babelJsConfig);
|
|
44
|
+
const swcrcPath = path.join(process.cwd(), '.swcrc');
|
|
45
|
+
const hasSwcrcConfig = fs.existsSync(swcrcPath);
|
|
46
|
+
const swcJsPath = path.join(process.cwd(), 'swc.config.js');
|
|
47
|
+
const hasSwcJsConfig = fs.existsSync(swcJsPath);
|
|
48
|
+
const postcssConfigPath = path.join(process.cwd(), 'postcss.config.js');
|
|
49
|
+
const hasPostcssConfig = fs.existsSync(postcssConfigPath);
|
|
50
|
+
const packageLockPath = path.join(process.cwd(), 'package-lock.json');
|
|
51
|
+
const hasPackageLock = fs.existsSync(packageLockPath);
|
|
52
|
+
const yarnLockPath = path.join(process.cwd(), 'yarn.lock');
|
|
53
|
+
const hasYarnLock = fs.existsSync(yarnLockPath);
|
|
54
|
+
|
|
55
|
+
// Build dependencies array
|
|
56
|
+
const buildDependencies = [
|
|
57
|
+
...(projectConfigPath ? [projectConfigPath] : []),
|
|
58
|
+
...(configPath ? [configPath] : []),
|
|
59
|
+
...(hasTsconfig ? [tsconfigPath] : []),
|
|
60
|
+
...(hasBabelRcConfig ? [babelRcConfig] : []),
|
|
61
|
+
...(hasBabelJsConfig ? [babelJsConfig] : []),
|
|
62
|
+
...(hasSwcrcConfig ? [swcrcPath] : []),
|
|
63
|
+
...(hasSwcJsConfig ? [swcJsPath] : []),
|
|
64
|
+
...(hasPostcssConfig ? [postcssConfigPath] : []),
|
|
65
|
+
...(hasPackageLock ? [packageLockPath] : []),
|
|
66
|
+
...(hasYarnLock ? [yarnLockPath] : []),
|
|
67
|
+
].filter(Boolean);
|
|
68
|
+
|
|
31
69
|
return {
|
|
32
70
|
cache: true,
|
|
33
71
|
experiments: {
|
|
34
72
|
cache: {
|
|
35
|
-
version: `
|
|
73
|
+
version: `cache-${mode}${(side && `-${side}`) || ""}`,
|
|
36
74
|
type: "persistent",
|
|
37
75
|
storage: {
|
|
38
76
|
type: "filesystem",
|
|
39
77
|
directory: `node_modules/.cache/rspack${(side && `/${side}`) || ""}`,
|
|
40
78
|
},
|
|
79
|
+
...(buildDependencies.length > 0 && {
|
|
80
|
+
buildDependencies: buildDependencies,
|
|
81
|
+
})
|
|
41
82
|
},
|
|
42
83
|
},
|
|
43
84
|
};
|
|
@@ -140,6 +181,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
140
181
|
const mode = isProd ? 'production' : 'development';
|
|
141
182
|
const projectDir = process.cwd();
|
|
142
183
|
const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, 'rspack.config.js');
|
|
184
|
+
const configPath = Meteor.configPath;
|
|
143
185
|
|
|
144
186
|
const isTypescriptEnabled = Meteor.isTypescriptEnabled || false;
|
|
145
187
|
const isJsxEnabled =
|
|
@@ -176,6 +218,13 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
176
218
|
const buildOutputDir = path.resolve(projectDir, buildContext, outputDir);
|
|
177
219
|
Meteor.buildOutputDir = buildOutputDir;
|
|
178
220
|
|
|
221
|
+
// Expose Meteor's helpers to expand Rspack configs
|
|
222
|
+
Meteor.compileWithMeteor = deps => compileWithMeteor(deps);
|
|
223
|
+
Meteor.compileWithRspack = deps =>
|
|
224
|
+
compileWithRspack(deps, {
|
|
225
|
+
options: Meteor.swcConfigOptions,
|
|
226
|
+
});
|
|
227
|
+
|
|
179
228
|
// Add HtmlRspackPlugin function to Meteor
|
|
180
229
|
Meteor.HtmlRspackPlugin = (options = {}) => {
|
|
181
230
|
return new HtmlRspackPlugin({
|
|
@@ -225,7 +274,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
225
274
|
console.log('[i] Meteor flags:', Meteor);
|
|
226
275
|
}
|
|
227
276
|
|
|
228
|
-
const enableSwcExternalHelpers =
|
|
277
|
+
const enableSwcExternalHelpers = !isServer && swcExternalHelpers;
|
|
229
278
|
const isDevEnvironment = isRun && isDev && !isTest && !isNative;
|
|
230
279
|
const swcConfigRule = createSwcConfig({
|
|
231
280
|
isTypescriptEnabled,
|
|
@@ -247,6 +296,9 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
247
296
|
const alias = {
|
|
248
297
|
'/': path.resolve(process.cwd()),
|
|
249
298
|
};
|
|
299
|
+
const fallback = {
|
|
300
|
+
...(isClient && makeWebNodeBuiltinsAlias()),
|
|
301
|
+
};
|
|
250
302
|
const extensions = [
|
|
251
303
|
'.ts',
|
|
252
304
|
'.tsx',
|
|
@@ -341,7 +393,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
341
393
|
...extraRules,
|
|
342
394
|
],
|
|
343
395
|
},
|
|
344
|
-
resolve: { extensions, alias },
|
|
396
|
+
resolve: { extensions, alias, fallback },
|
|
345
397
|
externals,
|
|
346
398
|
plugins: [
|
|
347
399
|
...[
|
|
@@ -361,6 +413,9 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
361
413
|
...bannerPluginConfig,
|
|
362
414
|
Meteor.HtmlRspackPlugin(),
|
|
363
415
|
...doctorPluginConfig,
|
|
416
|
+
new NormalModuleReplacementPlugin(/^node:(.*)$/, (res) => {
|
|
417
|
+
res.request = res.request.replace(/^node:/, '');
|
|
418
|
+
}),
|
|
364
419
|
],
|
|
365
420
|
watchOptions,
|
|
366
421
|
devtool: isDevEnvironment || isNative || isTest ? 'source-map' : 'hidden-source-map',
|
|
@@ -377,7 +432,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
377
432
|
},
|
|
378
433
|
},
|
|
379
434
|
}),
|
|
380
|
-
...merge(createCacheStrategy(mode, "client"), { experiments: { css: true } })
|
|
435
|
+
...merge(createCacheStrategy(mode, "client", { projectConfigPath, configPath }), { experiments: { css: true } })
|
|
381
436
|
};
|
|
382
437
|
|
|
383
438
|
|
|
@@ -431,6 +486,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
431
486
|
conditionNames: ['import', 'require', 'node', 'default'],
|
|
432
487
|
},
|
|
433
488
|
externals,
|
|
489
|
+
externalsPresets: { node: true },
|
|
434
490
|
plugins: [
|
|
435
491
|
new DefinePlugin(
|
|
436
492
|
isTest && (isTestModule || isTestEager)
|
|
@@ -455,7 +511,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
455
511
|
watchOptions,
|
|
456
512
|
devtool: isDevEnvironment || isNative || isTest ? 'source-map' : 'hidden-source-map',
|
|
457
513
|
...((isDevEnvironment || (isTest && !isTestEager) || isNative) &&
|
|
458
|
-
createCacheStrategy(mode, "server")),
|
|
514
|
+
createCacheStrategy(mode, "server", { projectConfigPath, configPath })),
|
|
459
515
|
};
|
|
460
516
|
|
|
461
517
|
// Load and apply project-level overrides for the selected build
|
|
@@ -523,7 +579,8 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
523
579
|
}
|
|
524
580
|
}
|
|
525
581
|
|
|
526
|
-
const
|
|
582
|
+
const sideConfig = isClient ? clientConfig : serverConfig;
|
|
583
|
+
const config = mergeMeteorRspackFragments(sideConfig);
|
|
527
584
|
|
|
528
585
|
if (Meteor.isDebug || Meteor.isVerbose) {
|
|
529
586
|
console.log('Config:', inspect(config, { depth: null, colors: true }));
|