@meteorjs/rspack 0.2.53 → 0.3.50
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/index.d.ts +9 -0
- package/lib/meteorRspackHelpers.js +54 -0
- package/lib/test.js +10 -11
- package/package.json +1 -1
- package/plugins/AssetExternalsPlugin.js +40 -0
- package/rspack.config.js +25 -2
package/index.d.ts
CHANGED
|
@@ -66,6 +66,15 @@ type MeteorEnv = Record<string, any> & {
|
|
|
66
66
|
* @returns A config object with merged configs
|
|
67
67
|
*/
|
|
68
68
|
extendConfig: (...configs: Record<string, object>[]) => Record<string, object>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Remove plugins from a Rspack config by name, RegExp, predicate, or array of them.
|
|
72
|
+
* @param matchers - String, RegExp, function, or array of them to match plugin names
|
|
73
|
+
* @returns The modified config object
|
|
74
|
+
*/
|
|
75
|
+
disablePlugins: (
|
|
76
|
+
matchers: string | RegExp | ((plugin: any, index: number) => boolean) | Array<string | RegExp | ((plugin: any, index: number) => boolean)>
|
|
77
|
+
) => Record<string, any>;
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
export type ConfigFactory = (
|
|
@@ -152,6 +152,59 @@ function extendSwcConfig(swcConfig) {
|
|
|
152
152
|
});
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Remove plugins from a Rspack config by name, RegExp, predicate, or array of them.
|
|
157
|
+
* When using a function predicate, it receives both the plugin and its index in the plugins array.
|
|
158
|
+
*
|
|
159
|
+
* @param {object} config Rspack config object
|
|
160
|
+
* @param {string | RegExp | ((plugin: any, index: number) => boolean) | Array<string|RegExp|Function>} matchers
|
|
161
|
+
* @returns {object} The modified config object
|
|
162
|
+
*/
|
|
163
|
+
function disablePlugins(config, matchers) {
|
|
164
|
+
if (!config || typeof config !== "object") {
|
|
165
|
+
throw new TypeError("disablePlugins: `config` must be an object");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const plugins = Array.isArray(config.plugins) ? config.plugins : [];
|
|
169
|
+
const kept = [];
|
|
170
|
+
|
|
171
|
+
const list = Array.isArray(matchers) ? matchers : [matchers];
|
|
172
|
+
|
|
173
|
+
const getPluginName = (p) => {
|
|
174
|
+
if (!p) return "";
|
|
175
|
+
return (
|
|
176
|
+
(p.constructor && typeof p.constructor.name === "string" && p.constructor.name) ||
|
|
177
|
+
(typeof p.name === "string" && p.name) ||
|
|
178
|
+
(typeof p.pluginName === "string" && p.pluginName) ||
|
|
179
|
+
(typeof p.__pluginName === "string" && p.__pluginName) ||
|
|
180
|
+
""
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const predicates = list.map((m) => {
|
|
185
|
+
if (typeof m === "function") return m;
|
|
186
|
+
if (m instanceof RegExp) {
|
|
187
|
+
return (p) => m.test(getPluginName(p));
|
|
188
|
+
}
|
|
189
|
+
if (typeof m === "string") {
|
|
190
|
+
return (p) => getPluginName(p) === m;
|
|
191
|
+
}
|
|
192
|
+
throw new TypeError(
|
|
193
|
+
"disablePlugins: matchers must be string, RegExp, function, or array of them"
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
plugins.forEach((p, index) => {
|
|
198
|
+
const matches = predicates.some((fn) => fn(p, index));
|
|
199
|
+
if (!matches) {
|
|
200
|
+
kept.push(p);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
config.plugins = kept;
|
|
205
|
+
return config;
|
|
206
|
+
}
|
|
207
|
+
|
|
155
208
|
module.exports = {
|
|
156
209
|
compileWithMeteor,
|
|
157
210
|
compileWithRspack,
|
|
@@ -159,4 +212,5 @@ module.exports = {
|
|
|
159
212
|
splitVendorChunk,
|
|
160
213
|
extendSwcConfig,
|
|
161
214
|
makeWebNodeBuiltinsAlias,
|
|
215
|
+
disablePlugins,
|
|
162
216
|
};
|
package/lib/test.js
CHANGED
|
@@ -13,13 +13,13 @@ const { createIgnoreRegex, createIgnoreGlobConfig } = require("./ignore.js");
|
|
|
13
13
|
* @returns {string} The path to the generated file
|
|
14
14
|
*/
|
|
15
15
|
const generateEagerTestFile = ({
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}) => {
|
|
16
|
+
isAppTest,
|
|
17
|
+
projectDir,
|
|
18
|
+
buildContext,
|
|
19
|
+
ignoreEntries: inIgnoreEntries = [],
|
|
20
|
+
prefix: inPrefix = '',
|
|
21
|
+
extraEntry,
|
|
22
|
+
}) => {
|
|
23
23
|
const distDir = path.resolve(projectDir, ".meteor/local/test");
|
|
24
24
|
if (!fs.existsSync(distDir)) {
|
|
25
25
|
fs.mkdirSync(distDir, { recursive: true });
|
|
@@ -49,8 +49,7 @@ const generateEagerTestFile = ({
|
|
|
49
49
|
? "/\\.app-(?:test|spec)s?\\.[^.]+$/"
|
|
50
50
|
: "/\\.(?:test|spec)s?\\.[^.]+$/";
|
|
51
51
|
|
|
52
|
-
const content =
|
|
53
|
-
{
|
|
52
|
+
const content = `{
|
|
54
53
|
const ctx = import.meta.webpackContext('/', {
|
|
55
54
|
recursive: true,
|
|
56
55
|
regExp: ${regExp},
|
|
@@ -61,8 +60,8 @@ const generateEagerTestFile = ({
|
|
|
61
60
|
${
|
|
62
61
|
extraEntry
|
|
63
62
|
? `const extra = import.meta.webpackContext('${path.dirname(
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
extraEntry
|
|
64
|
+
)}', {
|
|
66
65
|
recursive: false,
|
|
67
66
|
regExp: ${new RegExp(`${path.basename(extraEntry)}$`).toString()},
|
|
68
67
|
mode: 'eager',
|
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// AssetExternalsPlugin.js
|
|
2
|
+
//
|
|
3
|
+
// This plugin externalizes assets within CSS/SCSS and other files.
|
|
4
|
+
// It prevents Rspack from bundling assets referenced in CSS url() and similar contexts,
|
|
5
|
+
// allowing them to be served directly from the public directory.
|
|
6
|
+
|
|
7
|
+
// Regular expression to match CSS, SCSS, and other style files
|
|
8
|
+
const CSS_EXT_REGEX = /\.(css|scss|sass|less|styl)$/;
|
|
9
|
+
|
|
10
|
+
class AssetExternalsPlugin {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.pluginName = 'AssetExternalsPlugin';
|
|
13
|
+
this.options = options;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
apply(compiler) {
|
|
17
|
+
// Add the externals function to handle asset URLs in CSS files
|
|
18
|
+
compiler.options.externals = [
|
|
19
|
+
...compiler.options.externals || [],
|
|
20
|
+
(data, callback) => {
|
|
21
|
+
const req = data.request;
|
|
22
|
+
|
|
23
|
+
// Webpack provides dependencyType === "url" for CSS url() deps.
|
|
24
|
+
// Rspack is webpack-compatible here, but keep this tolerant.
|
|
25
|
+
const isUrlDep = data.dependencyType === 'url';
|
|
26
|
+
const issuer = data.contextInfo?.issuer || '';
|
|
27
|
+
const fromCss = CSS_EXT_REGEX.test(issuer);
|
|
28
|
+
|
|
29
|
+
if (req && req.startsWith('/') && (isUrlDep || fromCss)) {
|
|
30
|
+
// Keep the URL as-is (served by your server from /public)
|
|
31
|
+
return callback(null, `asset ${req}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
callback();
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { AssetExternalsPlugin };
|
package/rspack.config.js
CHANGED
|
@@ -9,6 +9,7 @@ const { cleanOmittedPaths, mergeSplitOverlap } = require("./lib/mergeRulesSplitO
|
|
|
9
9
|
const { getMeteorAppSwcConfig } = require('./lib/swc.js');
|
|
10
10
|
const HtmlRspackPlugin = require('./plugins/HtmlRspackPlugin.js');
|
|
11
11
|
const { RequireExternalsPlugin } = require('./plugins/RequireExtenalsPlugin.js');
|
|
12
|
+
const { AssetExternalsPlugin } = require('./plugins/AssetExternalsPlugin.js');
|
|
12
13
|
const { generateEagerTestFile } = require("./lib/test.js");
|
|
13
14
|
const { getMeteorIgnoreEntries, createIgnoreGlobConfig } = require("./lib/ignore");
|
|
14
15
|
const { mergeMeteorRspackFragments } = require("./lib/meteorRspackConfigFactory.js");
|
|
@@ -19,7 +20,9 @@ const {
|
|
|
19
20
|
splitVendorChunk,
|
|
20
21
|
extendSwcConfig,
|
|
21
22
|
makeWebNodeBuiltinsAlias,
|
|
23
|
+
disablePlugins,
|
|
22
24
|
} = require('./lib/meteorRspackHelpers.js');
|
|
25
|
+
const { prepareMeteorRspackConfig } = require("./lib/meteorRspackConfigFactory");
|
|
23
26
|
|
|
24
27
|
// Safe require that doesn't throw if the module isn't found
|
|
25
28
|
function safeRequire(moduleName) {
|
|
@@ -286,6 +289,9 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
286
289
|
Meteor.splitVendorChunk = () => splitVendorChunk();
|
|
287
290
|
Meteor.extendSwcConfig = (customSwcConfig) => extendSwcConfig(customSwcConfig);
|
|
288
291
|
Meteor.extendConfig = (...configs) => mergeSplitOverlap(...configs);
|
|
292
|
+
Meteor.disablePlugins = matchers => prepareMeteorRspackConfig({
|
|
293
|
+
disablePlugins: matchers,
|
|
294
|
+
});
|
|
289
295
|
|
|
290
296
|
// Add HtmlRspackPlugin function to Meteor
|
|
291
297
|
Meteor.HtmlRspackPlugin = (options = {}) => {
|
|
@@ -392,6 +398,15 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
392
398
|
enableGlobalPolyfill: isDevEnvironment && !isServer,
|
|
393
399
|
});
|
|
394
400
|
|
|
401
|
+
// Handle assets
|
|
402
|
+
const assetExternalsPlugin = new AssetExternalsPlugin();
|
|
403
|
+
const assetModuleFilename = _fileInfo => {
|
|
404
|
+
const filename = _fileInfo.filename;
|
|
405
|
+
const isPublic = filename.startsWith('/') || filename.startsWith('public');
|
|
406
|
+
if (isPublic) return `[name][ext][query]`;
|
|
407
|
+
return `${assetsContext}/[hash][ext][query]`;
|
|
408
|
+
};
|
|
409
|
+
|
|
395
410
|
const rsdoctorModule = isBundleVisualizerEnabled
|
|
396
411
|
? safeRequire('@rsdoctor/rspack-plugin')
|
|
397
412
|
: null;
|
|
@@ -462,7 +477,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
462
477
|
libraryTarget: 'commonjs2',
|
|
463
478
|
publicPath: '/',
|
|
464
479
|
chunkFilename: `${chunksContext}/[id]${isProd ? '.[chunkhash]' : ''}.js`,
|
|
465
|
-
assetModuleFilename
|
|
480
|
+
assetModuleFilename,
|
|
466
481
|
cssFilename: `${chunksContext}/[name]${
|
|
467
482
|
isProd ? '.[contenthash]' : ''
|
|
468
483
|
}.css`,
|
|
@@ -497,6 +512,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
497
512
|
? [new reactRefreshModule()]
|
|
498
513
|
: []),
|
|
499
514
|
requireExternalsPlugin,
|
|
515
|
+
assetExternalsPlugin,
|
|
500
516
|
].filter(Boolean),
|
|
501
517
|
new DefinePlugin({
|
|
502
518
|
'Meteor.isClient': JSON.stringify(true),
|
|
@@ -566,7 +582,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
566
582
|
filename: () => `../${buildContext}/${outputPath}`,
|
|
567
583
|
libraryTarget: 'commonjs2',
|
|
568
584
|
chunkFilename: `${chunksContext}/[id]${isProd ? '.[chunkhash]' : ''}.js`,
|
|
569
|
-
assetModuleFilename
|
|
585
|
+
assetModuleFilename,
|
|
570
586
|
...(isProd && { clean: { keep: keepOutsideBuild() } }),
|
|
571
587
|
},
|
|
572
588
|
optimization: {
|
|
@@ -610,6 +626,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
610
626
|
),
|
|
611
627
|
...bannerPluginConfig,
|
|
612
628
|
requireExternalsPlugin,
|
|
629
|
+
assetExternalsPlugin,
|
|
613
630
|
...doctorPluginConfig,
|
|
614
631
|
],
|
|
615
632
|
watchOptions,
|
|
@@ -768,6 +785,12 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
768
785
|
}
|
|
769
786
|
}
|
|
770
787
|
|
|
788
|
+
const shouldDisablePlugins = config?.disablePlugins != null;
|
|
789
|
+
if (shouldDisablePlugins) {
|
|
790
|
+
config = disablePlugins(config, config.disablePlugins);
|
|
791
|
+
delete config.disablePlugins;
|
|
792
|
+
}
|
|
793
|
+
|
|
771
794
|
if (Meteor.isDebug || Meteor.isVerbose) {
|
|
772
795
|
console.log('Config:', inspect(config, { depth: null, colors: true }));
|
|
773
796
|
}
|