rspack: web.browser.legacy is built from mainModule.client, ignoring mainModule.legacy
Summary
With the Rspack bundler enabled, the web.browser.legacy program is built from meteor.mainModule.client instead of meteor.mainModule.legacy. The legacy entry is silently ignored.
For an app whose legacy entry is a small "your browser is not supported" stub, the legacy program goes from a couple of hundred KB to the size of the whole app, and the stub's content never reaches the browsers it was written for.
This is easy to miss, because meteor run and meteor test under Rspack exclude the legacy arch, and meteor build succeeds either way.
Reproduce
An app with a distinct legacy entry:
"meteor": {
"mainModule": {
"client": "client/main-modern.ts",
"server": "server/main.ts",
"legacy": "client/main-legacy.ts"
}
}
meteor build --server-only --directory /tmp/out
Observed, in our app: programs/web.browser.legacy is 18.5 MB of js, contains the modern app's modules, and does not contain any string unique to client/main-legacy.ts. Built from the same commit without Rspack it is 1.7 MB and contains only the legacy entry's output.
Expected: the legacy program is built from mainModule.legacy.
Cause
The Rspack integration only ever reads client and server:
getMeteorInitialAppEntrypoints() returns mainClient / mainServer only, and setMeteorAppEntrypoints() publishes them through METEOR_CONFIG_CLIENT / METEOR_CONFIG_SERVER. mainModule.legacy is never read anywhere in packages/rspack or npm-packages/meteor-rspack.
Setting either variable then makes the tool rebuild mainModule from scratch with exactly two keys:
mainModule: {
client: process.env.METEOR_CONFIG_CLIENT || this._config.mainModule.client,
server: process.env.METEOR_CONFIG_SERVER || this._config.mainModule.server,
},
so legacy (and cordova) are dropped from the config. _getEntryModulesByArch then maps client to web, and archinfo.mostSpecificMatch("web.browser.legacy", ["web", "os"]) resolves to web — the modern entry.
Without the env override the same app resolves correctly, because mapWhereToArches("legacy") yields web.browser.legacy, which is a more specific match than web.
Suggested fix
Preserve the keys the override does not set:
mainModule: {
...this._config.mainModule,
client: process.env.METEOR_CONFIG_CLIENT || this._config.mainModule.client,
server: process.env.METEOR_CONFIG_SERVER || this._config.mainModule.server,
},
The testModule branch immediately below has the same shape and drops extra keys in the same way, so it is worth fixing together.
That restores mainModule.legacy as a config value. Whether the Rspack integration should then also build a separate legacy bundle from it is a second question — at minimum the entry should not be silently replaced by the modern one.
Notes
The Rspack integration docs do not mention the legacy arch. meteor-bundler-optimizations.md documents modern.webArchOnly as affecting meteor run only, and says "This setting doesn't affect production; legacy builds are still included by default on Meteor apps" — so a legacy program is expected in production, which is consistent with what is produced here; it is only built from the wrong entry.
Apps that do not want a legacy program at all can add modern to .meteor/platforms, as that page describes. That is a fine workaround but a different decision from "my legacy entry is ignored".
Versions
- Meteor
[email protected] - Atmosphere
[email protected],@meteorjs/[email protected],@rspack/core/@rspack/cli1.7.11 - macOS 26.6.2 (arm64)
Source: meteor/meteor