Meteor-RSPack Integration: A Modern Bundler Meets Meteor 3.4

To speed up test and make use of all the cores and RAM on the build server, we partition tests with METEOR_IGNORE to only include certain *.tests.* files and then run test processes in parallel on the same circleci “machine”. Each instance gets a unique port and results file.

    "test:xunit-first-half": "parallel --xapply --tagstring '{= $_=3000+3*seq() =}' --linebuffer --header : 'INCLUDE_RANGE={RANGE} PORT={= $_=3000+3*seq() =} MOCHA_OUTPUT_FILE=result{#} yarn test:xunit' ::: RANGE a-c d-j k-n",
"test:xunit": "unset MONGO_URL && bash -c 'TEST_CONSOLE_LOGGING=true TYPESCRIPT_FAIL_ON_COMPILATION_ERRORS=1 METEOR_IGNORE=$(server/buildmeteorignore $INCLUDE_RANGE) TEST_CLIENT=0 MOCHA_REPORTER=xunit MOCHA_TIMEOUT=60000 SERVER_MOCHA_OUTPUT=$PWD/mocha/${MOCHA_OUTPUT_FILE:-results}.xml meteor test --once --driver-package meteortesting:mocha --exclude-archs web.browser.legacy --port \"[::]:${PORT:-3000}\"'",    

with buildmeteorignore being:

#!/usr/bin/env bash
# builds contents for METEOR_IGNORE to include only test files that start with
# the character range provided as arg1
# OR with invert as arg2 to EXCLUDE that character range
# TEST_PREFIX is set to "app-" when running ui tests since then the test files are named XX.XX.app-tests.ts
echo "# Generated METEOR_IGNORE contents"

if [[ -z $1 ]]; then
    # no argument => no contents
    exit
fi

RANGE=${1:-a-z}
if [[ $2 == invert ]]; then
    PREFIX=""
else
    echo "*.${TEST_PREFIX}tests.ts"
    PREFIX="!"
fi

# for debugging
# echo >&2 RANGE=$RANGE PREFIX=$PREFIX

echo $PREFIX[$RANGE]*.${TEST_PREFIX}tests.ts

/me checking the Meteor Forums every day, to see if the new 3.4 RSPack release has shipped yet.

4 Likes

Same! :joy: It’s in my muscle memory now first thing in the morning I type “for” and this thread on the forums autocompletes in my chrome address bar.

Definitely anxious for an RC to try with my projects!

2 Likes

Thanks to everyone who voted. Most projects already use the expected package.json entrypoints in Meteor’s config.

For those who don’t but know how to adapt them, that’s great. For those who don’t and need help, please reach me privately via DM in the forums. Over the coming weeks, while the beta is running, I’ll assist on adapting projects. I’ll also work to understand how to add entrypoints to projects without them by the approach mentioned above. @vikr00001 let’s move this to private messages, I’ll DM you when available.

For tests, projects still use both eager runs and full-app mode. I’ve updated the Rspack integration to support all test setups, and results look fine. Whether you use testModule with separate configs for client and server, a single config, no testModule at all with the eager approach, or run --full-app, everything pass as expected. :white_check_mark:


@permb your approach to partitioning tests with Meteor ignore is solid. Since Meteor was the bundler in charge, that worked well. With the new Rspack integration, METEOR_IGNORE now applies broadly to source code (including tests), and delegation goes to Rspack. To achieve a similar setup you’ll need to use rspack.config.js at your project root with the Rspack IgnorePlugin, for example:

import path from "path";
import { IgnorePlugin } from "@rspack/core";
import { defineConfig } from "@meteorjs/rspack";

export default defineConfig(Meteor => {
  const range = process.env.TEST_RANGE || "a-z";         // e.g. "a-m"
  const invert = process.env.TEST_INVERT === "true";     // "true" to exclude range
  const testPrefix = Meteor.isTestApp ? "app-" : "";     // matches ".app-tests.ts" vs ".tests.ts"
  const testSuffix = `.${testPrefix}tests.ts`;
  const rangeRe = new RegExp(`^[${range}]`);

  return {
    plugins: [
      ...(Meteor.isTest || Meteor.isTestApp
        ? [
            new IgnorePlugin({
              checkResource(resource) {
                const filename = path.basename(resource);
                // Only act on test files with the expected suffix
                if (!filename.endsWith(testSuffix)) return false;
                const inRange = rangeRe.test(filename);
                return invert ? inRange : !inRange;
              },
            }),
          ]
        : []),
    ],
  };
});

This is just an example to show how rspack.config.js is introduced in Meteor projects. The code snippet above is not tested, but should work similarly in your env once you adopt Rspack.

The rspack.config.js file lets you customize your Rspack setup with plugins and configurations, acting directly on the modern bundler process. Meteor now only handles Atmosphere packages and linking app code with the Meteor core.

Keep in mind Rspack is optional, but if you opt in, you’ll need to adjust parts of your current setup, at least for more complex projects.

3 Likes

I’m really excited about all the attention this integration is getting. Like many of you, I can’t wait to see it evolve. This will go through several betas before a stable RC, but it’s worth it. The benefits are huge for Meteor users: better performance, smaller bundles, and new features. What’s even better is how much ground a single integration already covers, solving long-standing bundler issues that would have been nearly impossible to address one by one with a small team.

A quick update on the state: our initial goal was to ship the first beta in August. But after the releases of 3.3.1, 3.3.2, and 2.16.2, plus some uncovered Rspack integration details (like full support for Meteor test options), the timeline has slipped a bit.

The good news is we now support all official Meteor skeleton projects: React, TypeScript, Blaze, CoffeeScript, Solid, Svelte, and Vue. Angular experiments may follow after the first beta. Some setups need small migration steps, but they’re straightforward and already covered in the Rspack docs with clear rspack.config.js examples.

To ensure stability and avoid regressions, we’ve built a new modern test suite. It runs across all skeletons and checks key things: server responses, page rendering, dev/prod builds, and multiple configurations. Every skeleton now passes these tests as shown in the picture.

The suite also goes beyond skeleton coverage. It validates critical features and past Meteor limitations: HMR, dynamic imports, custom aliases with file and node_module redirects, SWC/Babel configs, React compiler, ESM imports (like react-router v7), style compilers (Less, SCSS, Tailwind), and more. This gives us a strong base to add tests/features without breaking existing ones. Before, skeletons had no tests at all, some even broken over time.

What’s left now is documentation and final cleanup before the PR. I expect to try an internal beta next week. Public beta will take longer, since I want to ensure published version works as from checkout tests, deployment on Galaxy and docs readiness. Realistically, that means at least one more week if everything goes well. In the meantime, I’ll share more details in this post about migration steps and integration benefits so you can start preparing.

13 Likes

Today I will introduce the last major prerequisite to adopt Rspack in your Meteor app: transforming nested imports into a standard style.

Nested imports are a Meteor-specific bundler feature not supported by standard tools. They come from the reify package and allow you to place import statements inside nested blocks (ifs, function bodies, etc.), deferring evaluation of the imported code (why nested imports?). In standard JavaScript you should instead use require or dynamic import (which can also split your bundle).

Example of a nested import:

// import { a as b } from "./c"; // root import

if (condition) {
  import { a as b } from "./c"; // nested import
  console.log(b);
}

Don’t confuse nested imports with dynamic imports (await import(...)). Dynamic imports are a modern standard for bundlers, letting you split bundles and defer loading safely.

Migration

To identify and fix nested imports in your project, use verbose mode in Meteor 3.3’s modern transpiler. Enable it with:

"meteor": {
  "modern": {
    "transpiler": {
      "verbose": true
    }
  }
}

When you run your app, [Transpiler] logs will show each file. Focus on (app) files that fail with messages like:

Error: 'import' and 'export' cannot be used outside of module code

Fix nested imports by moving them to the top of the file, or by replacing them with require or dynamic import.

You can skip migrating (package) code with nested imports. Meteor packages are still handled by the Meteor bundler in Rspack integration, but your app code is fully delegated to Rspack and must use standard syntax.

Backwards compatibility

While backwards compatibility was straightforward for the SWC integration in Meteor 3.3, achieving the same for Rspack in Meteor 3.4 is harder. In Meteor 3.3, we had a fallback mechanism using Babel+Reify per file, which SWC couldn’t handle but still worked at a file level. With Rspack, the integration is at the bundle level: the entire app code is processed by a modern bundler that not only parses files individually but also links them into a single distributable. Since Reify has its own way of linking modules, ensuring compatibility with a modern bundler has been difficult.

In the future, we might explore solutions, but our focus is on moving Meteor forward with a modern-first approach. For nested imports, there’s little reason to keep them today since standard alternatives exist and bring clear benefits with modern bundlers.

Remember, Rspack integration is optional. You can continue using Meteor’s bundler and still benefit only from Meteor 3.3 optimizations if you prefer.


Please answer the following question so we can assess how prepared your project is for nested imports migrations.

When using verbose mode, do you have nested imports in your <app> code?

  • Yes, I have nested imports, but I will migrate them
  • My code is standardized and ready to integrate Rspack
  • Regardless of nested imports, I prefer to keep Meteor specifics and will skip Rspack
0 voters
5 Likes

So looking forward to this!

Can you share anything about the reimplementation of dynamic imports with rspack? Is this provided by rspack now?

Just gave this verbose transpiler a run, and it found a half dozen nested imports in our projects. We were able to get rid of any warnings by replacing the nested imports with the following syntax:

  if (Package['browser-policy-common']) {
    console.log('Configuring content-security-policy.');
    import('meteor/browser-policy-common').then(({ BrowserPolicy }) => {
      // Use BrowserPolicy here
      BrowserPolicy.content.allowSameOriginForAll();
      // etc, etc....
    });
  }

Everything updated. Ready to RSPack when it drops.

4 Likes

I tried the beta and ran into a few broken cases

import { S3Client } from "@aws-sdk/client-s3";
console.log("s3client", S3Client);

Ends up generating a error:

Error: Cannot find module '@swc/helpers/_/_await_async_generator'
W20250903-16:03:41.027(-7)? (STDERR)     at makeMissingError (packages/modules-runtime.js:221:12)
W20250903-16:03:41.027(-7)? (STDERR)     at Module.require (packages/modules-runtime.js:240:17)

If that gets resolved there is then an issue with aldeed:collection2

import "meteor/aldeed:collection2/static";
Error: Cannot find package "aldeed:collection2". Try "meteor add aldeed:collection2".
W20250903-16:12:21.624(-7)? (STDERR)     at makeInstallerOptions.fallback (packages/modules-runtime.js:704:13)
W20250903-16:12:21.624(-7)? (STDERR)     at Module.require (packages/modules-runtime.js:243:14)
W20250903-16:12:21.624(-7)? (STDERR)     at Module.mod.require 

Both of these are runtime errors, as the packager believes everything is working

1 Like

I updated to v3.4 beta0 and ran my app on dev. I didn’t test every function, but what I ran seems to be working as usual

1 Like

Yesterday I kicked off the publish CI job for 3.4-beta at the end of the day. This morning I already have feedback, which is great. I appreciate the time and testing. Thanks :pray:

I’m verifying everything matches the checkout version, since I found some inconsistencies. The immediate issue is CI errors when releasing to specific architectures. Then, I’ve received some reports on the integration itself. I’ll check whether they are critical or just missing scenarios I can fix later. I may fix the quick and critical ones and republish.

My plan was to test the beta to verify stability and release a few quick internal betas to ensure it. Then I’ll publicly communicate here that the beta is live for everyone along the rest information.

7 Likes

Started a fresh project with the latest versions of React 19, TailwindCSS 4, shadcn, and Typescript… so far everything is working well on 3.4-beta.1! Haven’t ran into any issues yet :crossed_fingers:.

I’m going to try upgrading ozwell.ai to 3.4-beta.1 this next week at work and will report back my findings. incredibly excited about RSpack! :tada:

5 Likes

How did the tailwind installation go? Was it straightforward or you had to do some manual wiring?

I installed Tailwind in a fresh Meteor project using 3.4-beta.0 and I didn’t have to do any extra steps for it to work.

I’ve publicly released the first 3.4-beta.3 after resolving the last internal issues and feedback from early adopters. You can follow the details there.

Keep the requirements for migrating existing apps in mind, and try the new Meteor skeletons already using Rspack. The Tailwind skeleton is also available as requested above, showing how simple the integration is. Anything from the Rspack docs and bundler ecosystem can be adapted.

I’ll use this forum thread to keep sharing insights on the integration as I prepare content and find new things worth highlighting. Some topics I plan to cover include dynamic imports, bundle visualizer + rsdoctor, and more. Stay tuned.

Awesome. I got some great results with the bundle visualizer + rsdoctor. I was able to reduce the size of my bundle to just 585kb from 2.9mb. Still some work to do but this is great.

3 Likes
=> Started proxy.
┌─────────────────────────────────────────────────
│ Rspack Dependencies Installation
└─────────────────────────────────────────────────
The following Rspack dependencies need to be installed:
  • @rspack/cli@1.5.0
  • @rspack/core@1.5.0
  • @meteorjs/rspack@0.0.43

┌─────────────────────────────────────────────────
│ ❌ Rspack Installation Failed
└─────────────────────────────────────────────────
Run: meteor npm install -D @rspack/cli@1.5.0, @rspack/core@1.5.0 and @meteorjs/rspack@0.0.43
Rspack plugin error: Failed to install Rspack dependencies. Please install them manually with: meteor npm install -D @rspack/cli@1.5.0, @rspack/core@1.5.0 and @meteorjs/rspack@0.0.43

This message should be updated… I can’t just copy and paste it since the npm install command has commas in it.

EDIT:

Also, I’ve been stuck on this for about 20 minutes…

PS C:\prj\meteor-react-tailwind-prettier-starter\apps\meteor-react-tailwind-prettier-starter> meteor npm run start                                                    
     
> start
> meteor run

[[[[[ C:\prj\meteor-react-tailwind-prettier-starter\apps\meteor-react-tailwind-prettier-starter ]]]]]

=> Started proxy.
┌─────────────────────────────────────────────────
│ Adding Gitignore Entries for Meteor Modern-Tools build context directories
└─────────────────────────────────────────────────
The following entries will be added to .gitignore:
  • _build
  • */build-assets
  • */build-chunks
  • .rsdoctor
✅ Gitignore entries for Meteor Modern-Tools build context directories added
Rspack Error: spawn meteor ENOENT
Rspack Error: spawn meteor ENOENT
=> Started MongoDB.
   Loading plugin `rspack` from package ...  \

I tried CTRL-C and re-ran the command but it’s not getting anywhere fast.

@nachocodoner I just updated the reproduction repo for the Cannot find module '@swc/helpers/_/_async_generator_delegate'. It is caused by the openai package in the server bundle.

It looks like the app load process is stuck. It might be the app setup, but I’d bet it’s the environment here. It could be a Windows issue. What’s not great is that I don’t get any useful logs from Rspack Error: spawn meteor ENOENT to dig deeper.

Could you share the starter in a sample repo? I’ll try it on my Windows environment to see if I hit the same issue. I’ll provide more logs on the next stability beta if it errors.

Was it loading for you before, and now after -beta.3 it’s not? Your last message made it seem everything ran smoothly.

I’ve reproduced your problem. How frustrating this kind of errors that only happens in published version, not when running from checkout. I’ll likely release beta.4 soon as a stability update, along with fixes for other reports. Thank you.

Edit: @pmogollon

For the moment if your add "externalHelpers": false to your .swcrc config, it will work.

{
  "jsc": {
    "baseUrl": "./",
    "paths": {
      "@": ["./@"],
      "@hooks": ["./imports/ui/hooks"],
      "@condos": ["./imports/ui/condos"]
    },
    "externalHelpers": false
  }
}