New 3.4-beta.12 Release, Faster Builds, Smaller Bundles and Modern Setups with the Rspack integration ⚡

I believe I tried something similar already. First I tried to pass it to babel.

import { defineConfig } from '@meteorjs/rspack'

// eslint-disable-next-line no-unused-vars
export default defineConfig(Meteor => ({
  builtins: {
    // enable React/JSX handling in rspack (SWC-backed)
    react: {
      runtime: "automatic", // or "classic"
      development: Meteor.isDevelopment,
      importSource: "react"
    }
  },

  module: {
    rules: [
      // 1) Preprocess Flow: run babel-loader first to strip Flow types
      {
        enforce: "pre",               // run before other loaders / built-in parsing
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            babelrc: false,
            presets: [
              "@babel/preset-flow",
              "@babel/preset-react" // keeps JSX-compatible for any files handled by babel
            ]
          }
        }
      },

      // 2) TypeScript (+ JSX in .tsx)
      {
        test: /\.(ts|tsx)$/,
        type: "tsx" // tells rspack to parse TS + JSX (SWC)
      },

      // 3) JS / JSX - let rspack's built-in JS parser (SWC) handle remaining .js/.jsx
      {
        test: /\.(js|jsx)$/,
        // 'type: "jsx"' enables JSX parsing/transforms via SWC
        type: "jsx",
        exclude: /node_modules/ // I also treid with excluding everything but my NPM of interest.
      }
    ]
  },

  resolve: {
    extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
  }
}))

I also tried something like this:


import { defineConfig } from '@meteorjs/rspack'

// eslint-disable-next-line no-unused-vars
// (Same imports as earlier)

export default defineConfig(Meteor => {
 return {
   // Exclude native modules from the bundle (use Meteor runtime)
  ...Meteor.compileWithMeteor(['node_modules/@act/toastr-component/index.js']),
 } 
})

but then I tried this:

import { defineConfig } from '@meteorjs/rspack'

// eslint-disable-next-line no-unused-vars
// (Same imports as earlier)

export default defineConfig(Meteor => {
 return {
  module: {
    rules: [
      {
        test: /\.jsx?$/, // Target both .js and .jsx files
        use: {
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              parser: {
                syntax: 'ecmascript',
                jsx: true, // Enable JSX parsing
              },
            },
          },
        },
        type: 'javascript/auto',
      },
      // ... other rules (e.g., for .tsx files)
    ],
  }
 } 
})

They all yield the same error.

Then I used your configuration @schlaegerz and I am getting a different error which was reported by @wreiske here: GitHub · Where software is built

I tried transpiler options in package.json of the project

 "modern": {
      "transpiler": {
        "exclude": "/node_modules/@act/toastr-component",
        "excludeNodeModules": true
      }
    },

I finally killed the package and moved it in the main project as a regular React component. It compiled nicely but … now, with no errors, the page is not loading.

I spent more than 12 hours cumulated in multiple weeks trying to debug this, but I think I need to park this indefinitely. I am not sure this is going to work for me, the Meteor build now is a Frankenstein of babel, swc, webpack, rspack and I just don’t understand it and I don’t know which docs to use, when, in what context, which part comes from which tech, which settings to use where and where to take them from … etc.

@paulishca: I’ve added your issue to my work queue. As soon as it’s as easy to reproduce as you described above, it should be easier to track down the fix. Sometimes these problems come from package incompatibilities when certain packages don’t follow specific standards. We’ll see.

By the way, Meteor.compileWithMeteor accepts an array of package names, not paths. Not sure if that helps with the issue.

1 Like

I don’t get the error you get. I get this instead:

image

image

To solve it, I added:

module.exports = defineConfig((Meteor) => {
  return {
    ...Meteor.compileWithRspack(['@act/component']),
    // ..
  };
}

Btw, this is a similar config as making a module.rules entry similar as suggested above. But we created this abstraction to help keep it easy.

Why? When you use local packages that still have rich syntax, like JSX, you need to tell the compiler to transpile them to plain JS CommonJS (the format Meteor expects).

Most npm packages are already published in that plain format, so you don’t need to treat node_modules often. But some are not, including your own internal packages if you keep modern JS/JSX syntaxes in them. In that case, call compileWithRspack and pass the package name/s so it gets recompiled to plain JS before Meteor runs it.

Docs: compileWithRspack

If you still see the issue, please share an exact reproduction repo so we can iterate on it with your specific case. I shared docs about this in previous updates, and I’ll try to make them clearer and more guided so it’s easier to understand how to recompile modern syntax in local packages.

I see your previous message and I did try with only the package name too. Ok what I didn’t do is to pass it to Rspack instead of Meteor (as I used Meteor.compileWithMeteor). My logic was that if it worked with Meteor in the past, will try to compile it with Meteor further. Ok, these are the type of challenges which discouraged me to spend any more time on it. Maybe I am less patient too but I really read through these documentations and it is not like they are clear on what you can do. The info is a bit thin in online too.

Anyway, this is good to know for future reference. I was past it in 2 ways, I manually recompiled with babel and generated a dist folder with the compiled code and it worked but the page would not load. At this point I moved everything out of NPM into the code and completely moved around the initial problem but the page would not load.

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

This in package.json shows details of the SWC, all looks ok but I don’t get info about rspack/webpack. The HTML loads, but React doesn’t seem to mount into the div.

What I should see in dev:

What I see (no html under the div with id=“app” where React should render.) :

I try to add as much information as possible and keep the docs clear, giving full explanations and details. I know it can still be difficult for those just starting with bundler configuration, so I always try to keep that in mind and keep improving the docs. I also work on creating helpers like Meteor.compileWithRspack or Meteor.compileWithMeteor. Maybe there’s a better name given the current explanations, and I’m open to improving that. We’re also in a beta, so it’s the time for do it.

The issues mentioned above could have been fixed through direct Rspack configuration, but that would make it more verbose and harder for newcomers. These helpers I added aim to simplify that. Meteor already provides a default Rspack configuration internally, so users can have a solid starting point with minimal setup, tweak only what’s necessary also via config helpers, and still have the flexibility to go advanced if they want.

Anyway, in the end there’s no other path: if you want to get the most out of Rspack or any other tool, you need to learn the tool and how a bundler config works. AI can also help a lot with some of the config. You get many benefits that can easily pay off the effort. But it’s also totally fine for some apps to skip it. The good part is that Rspack is opt-in, and you can always go back to how Meteor handles things.

If you want me to look at other approaches you tried to solve the problem, or understand the blank page / other issues, I prefer that you share a reproducible repository. :pray: I can’t guess the exact state of your code, what is breaking in your process, or any specific situation in your app without that. Feel free to DM me, I’m happy to help. Anyway, I wouldn’t complicate things. I’d use Rspack’s built-in mechanism to recompile any npm package or piece of modern code instead of doing it manually with Babel or SWC.

1 Like

Is there a way to separate out server and client unit tests?

How so? Do you mean that any file inside the client or server folders, at any level in the file system, should automatically run only on the client or server? I haven’t used this pattern much and I’m not sure if it ever worked, but please share more details.

Figured out the memory problem was at least partially because unit tests were including the .spec. files which is not what they used to do, which was causing it to include my app test, which then includes way too much and causes it to crash

I don’t get this, what do you mean?

How so? Do you mean that any file inside the client or server folders, at any level in the file system, should automatically run only on the client or server? I haven’t used this pattern much and I’m not sure if it ever worked, but please share more details.

So in existing meteor if I run
meteor test --driver-package meteortesting:mocha --port 3050
Then it segregates the tests into client and server. Client being ones that require the browser to be able to run.
I believe it does this just by deciding that if a test is under any /client/ folder it is a client unit test, and if it is not it is a server unit test.

I don’t get this, what do you mean?

For our existing app we have two ways to run test, the above for unit tests and

meteor test --full-app --port 3005 --settings test.settings.json --driver-package meteortesting:mocha --exclude-archs web.browser.legacy

for functional tests.

In existing meteor the .spec files are only loaded for the functional tests, not the unit tests. Loading the specs for my unit tests was causing my meteor to crash with a memory error. Not exactly sure why, but there was too much being loaded in any case.

The other thing that is broken in my repo in my last message is that if you run the functional tests. It should run a mostly normal server instance along with a client instance. In old meteor on the server Meteor.isAppTest would be set to true, in the RSPack Meteor.isAppTest is being set to false, which breaks tests.

The dummy app in this package does the same thing

I followed the docs here:

What I understand is that for unit tests, meteor test eagerly loads both .spec[s] and .test[s] files, and that’s what I implemented.

Then for full app / functional tests, the pattern is *.app-test[s].* and *.app-spec[s].*. Isn’t this what you’re getting? Or is your result different from what the official docs describe? You stated that “In existing meteor .spec files are only loaded for the functional tests” but that seems only be achieved via unit testing according to docs.

About client/server specifics, you’re right, I didn’t cover all those edge cases when using eager loaded tests (testModule at package.json achieve it). I’ll look into solutions for that using the repositories you shared for eager tests.

Also, do you know if server/client tests only apply to the conventional server/client folders, or if they also affect files within imports or any other root folders with client or server folders within? Do you have tests described outside client/server folders?

Ah you are right about the spec, the problem was more about not loading anything that was outside of client/server folders.

For my unit tests it does seem that if it is not inside of a client or server folder it won’t load the test at all.
I tried today to find where this is specified anywhere in the meteor docs and I believe it is based on this behavior Application Structure | Meteor Guide

Now I agree this kind weird and probably not what we want the expected behavior to be, but I bet there are going to be a lot of apps that are structured like my mine and this should be either the default behavior or very easy to switch to.

The Meteor.isAppTest not being true is definitely a bug and should be resolved.

I’m trying to turn off rspack hot reloading for testing purposes. Here is my relevant rspack.config.js. I have temporarily removed Meteor package hot-module-replacement as well. But, the client console logs show HMR is still being loaded. What am I missing?

rspack.config.js

        devServer: {
            allowedHosts: 'all',

            hot: false,
            liveReload: false,
            webSocketServer: false,

            client: {
                overlay: false,
            },
        },
        devtool: Meteor.isDevelopment ? 'eval-source-map' : false,
    };

My client console logs show HMR is still being loaded:

Client Console log

[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay disabled.
log.js:39 [HMR] Waiting for update signal from WDS...

@nachocodoner , please ignore my post about disabling HMR. It’s accurate but the only reason I was trying it was to rule out a variable involved in connecting rspack HMR to Cloudflare. I’m pretty sure now that HMR is part of an anomaly I’m encountering, and I do want it, so I’m turning it back on.

1 Like

Rspack 1.6.0 is now available. If you’re using Meteor 3.4-beta.12, you can already upgrade Rspack to version 1.6.0.

The Meteor-Rspack integration is loosely coupled, so you no longer need to wait for Meteor updates to use newer Rspack versions. Meteor only defines a minimum verified version, meaning it’s the version we’ve tested and confirmed to work reliably with Meteor. You’re free to upgrade to newer versions at your app’s package.json on your own responsibility, without waiting for Meteor to bump that minimum. Rspack’s minor versions rarely introduce breaking changes, so you can safely benefit from newer releases whenever you decide.

That said, in the upcoming 3.4-beta.13, we’ll raise the minimum verified Rspack version to 1.6.0, ensuring it works smoothly.

Highlights of Rspack 1.6.0:

  • Improved tree-shaking for dynamic imports, allowing more cases to benefit from reduced bundle sizes.
  • Support for import defer. I was following this proposal closely and it’s great to see it supported in Rspack already. For context, import defer works similarly to Meteor’s old “nested imports”, deferring module evaluation (and its dependency subtree) until the module is actually used. This helps reduce initialization costs and improve performance. I’ll update the docs to cover this.
  • Fix for a persistent cache issue reported by the Meteor community. Those who experienced caching problems should see improvements in this release. Please keep sharing your issues with the wider open-source communities, even those outside Meteor, they seem to listen.

Let me know your experience with Rspack 1.6.0 if you test it early.

5 Likes

The Meteor-Rspack integration is loosely coupled, so you no longer need to wait for Meteor updates to use newer Rspack versions.

Looks like someone learned their lesson from Cordova design. :face_holding_back_tears:
:clap: :clap: :clap:

Meteor only defines a minimum verified version, meaning it’s the version we’ve tested and confirmed to work reliably with Meteor. You’re free to upgrade to newer versions at your app’s package.json on your own responsibility, without waiting for Meteor to bump that minimum. Rspack’s major versions rarely introduce breaking changes, so you can safely benefit from newer releases whenever you decide.

:exploding_head:

5 Likes

@nachocodoner

Hoss, you are the GOAT.

Your RSPack upgrade got the Cornerstone3D viewer working; the GPU optimizations working; the webworkers working; the WASM modules working.

DUDE. Phenomenal work. Maybe the most significant upgrade to the stack since Meteor 0.6.4. I mean, this is easily a 4.0 release, as far as I’m concerned.

And that’s not even talking about it being like 2x faster to compile.

There were some hiccups that are typical of a beta release… hot-module loading hiccups that need the occasional meteor reset; Material UI styling broke; behavior of React hooks in the Atmosphere packages changed; and no more static linking from packages/* into the parent app. But Claude and I were able to pass hooks and schemas and other objects through the Meteor object into the Atmosphere packages, and once we settled on that pattern, everything fell into place.

Phenomenal work. Can’t thank you enough for this upgrade.

10 Likes

Womp, womp.

Different project, hitting a file size error again. RSPack is allowing us to use the most-modern libraries and web features (web workers, GPU optimization, WASM, etc.), and I think there’s a tendency for some of them to have larger binary dependencies for some of the advanced browser features. For whatever reason, IPFS and Cornerstone both maxed out the hosting limits.

Which is a little frustrating, in so far as Galaxy does let us choose the option of container size; yet the uploader seems to be capped at a tiny size.

Any possibility of a meteor deploy --size medium command? My dream deployment/dev-ops scenario would be running something like:

meteor deploy --size medium --settings packages/nifty-workflow/configs/settings.json --extra-packages 'myorg:nifty-workflow' nifty-workflow.meteorapp.com
1 Like

@philippeoliveira Galaxy folks?

1 Like

Oh, I didn’t know about @swc/helpers… To be honest, reading the description in their docs, I’m not 100% sure what it does (maybe I need a second look when less tired). But I do recall at one point having similar issues to what you described before the 3.4 beta, which went away, but maybe I should look into this now.

This is what I’m using on the Vite side:

import { meteor } from 'meteor-vite/plugin'
import vue from '@vitejs/plugin-vue'
import { vuetify } from 'vite-plugin-vuetify'
import UnpluginFonts from 'unplugin-fonts/vite'
// ^ for some reason, they use unplugin, but don't support Webpack, apparently?
import swc from 'unplugin-swc'
import { analyser } from 'vite-bundle-analyzer'
// ^ this one's new, probably irrelevant

My own thoughts so far:

  • I think I could possibly replace UnpluginFonts with @fontsource-variable/* packages (see here for an exmaple installation: Roboto | Install | Fontsource).
  • I think I can also use the Vuetify Webpack plugin.
  • Vue as an SPA should be trivial with SWC.

To be honest, I’ve got a foot in the Meteor world, a foot in the Nuxt/Nitro world, and both hands in the Python world (it’s a mental game of Twister for me), so I don’t know if I will be doubling down on Meteor, following Vite to the Nitro world (I’m a big fan of Nitro + H3, and Nuxt UI), or expanding the role of Python in our stack.

My ideal world where Meteor – as a runtime framework with an accounts system – is just a WinterTC compatible app I can hook into something like Nitro, and voila, Meteor deployed on AWS Lambda :laughing: (or maybe a hypothetical Galaxy Functions running on Deno/Bun, if I may plant the seeds of chaos…). But the specific subset of Meteor I currently use is narrow enough for that to make sense, I know that there’s a lot of people who rely on Meteor as a full stack framework + build system.

(But maybe with time the coupling will be loose enough that it’ll all just fall into place)

Also…. lower priority, but a biggish enough item to separate v3.4 from version 4.0:

Repack doesn’t completely handle the Atmosphere build pipeline for packages/*

I tried to install Cornerstone3D in packages/dicom, so it could be enabled as an optional module, but that did not work. Web workers weren’t registered, codexes couldn’t be accessed, etc. Eventually had to install Cornerstone in the main app, and implement ad-hoc tree-shaking if DICOM support isn’t needed.

Thank you very much. I’m glad to hear the upgrade meets the goals behind the work: improving build times, reducing bundle size, and staying flexible enough to support any modern setup and configuration.

It’s a relief to see this effort paying off for each of you. I appreciate the support and feedback, and especially the patience and trust. This kind of upgrade has many edges to consider, and the commitment is to keep iterating and make sure it serves everyone well.

But Claude and I were able to pass hooks and schemas and other objects through the Meteor object into the Atmosphere packages, and once we settled on that pattern, everything fell into place.

Repack doesn’t completely handle the Atmosphere build pipeline for packages/*

I tried to install Cornerstone3D in packages/dicom, so it could be enabled as an optional module, but that did not work. Web workers weren’t registered, codexes couldn’t be accessed, etc. Eventually had to install Cornerstone in the main app, and implement ad-hoc tree-shaking if DICOM support isn’t needed.

Mainly, the reason is that Meteor packages use their own pipeline to keep the Rspack integration simple and reliable. While deeper Rspack integration in Meteor packages would be nice, I recommend moving to npm packages to gain the benefits of today’s standard, feature-ready Node/Web packaging.

I think part of the positive experience with the Rspack upgrade comes from delegating to a modern, faster bundler in Meteor 3.4. That shift follows a modern-first mindset, embracing established workflows. The same applies to moving app Atmosphere packages to npm to unlock these benefits.

With npm packages, it’s easy to bring them into the Rspack pipeline and ensure everything is registered and configured as expected, just like at app level. You also get npm ecosystem gains like caching and local management tools (linking, workspaces, lerna, pnpm, etc.). Every meteor/* import in your npm packages will still resolve externally, and Meteor will inject them properly. Besides, you avoid passing anything to the Meteor property for cross-package communication as you suggested to do before, everything is managed from the same common NPM ecosystem.

In my apps, I use a lerna setup with npm packages under ./packages. My Rspack config forces those packages to be compiled by Rspack because they use modern syntax, so that I don’t need to create distributable versions myself (see Meteor.compileWithRspack, where you can pass a package name or a regex for all your branded packages). It’s like keeping packages at the Atmosphere level, but with the extra features the npm ecosystem offers.

With this, your Meteor app moves to a more standardized and portable setup. Atmosphere packages remain for Meteor core needs, existing community packages, or cases that must hook into the Meteor bundler (addFiles, assets management, and other specifics).

So, regarding your suggestion: it’s worth considering and we will do, but I prefer to prioritize on modern-first solutions, and keep backward-compatibility paths simple. We can explore improvements for Atmosphere packages, but that tends to add complexity (I’ve tried before hardly) and keeps the community tied to older paths instead of adopting newer ones across the framework and JS standards. We’ve been living with Meteor-specifics for a long time, even promoting their expansion in previous directions, which kept us distant from the wider ecosystem. I hope by embracing the new, it can change that, as we’re already seeing with the latest upgrades.

4 Likes

I’ll pass a note to the Galaxy team about your suggestions.


From the OSS side, what I can say is that the upcoming Meteor 3.4-beta.13 will include long-requested optimizations in the Meteor bundler and in how Atmosphere packages and their attached npm dependencies are added to production bundles.

For a long time, we’ve had related issues reported on GitHub and the forums, remaining idle for quite some time. As part of Meteor 3.4, we’re not only focusing on the new Rspack tool, but also revisiting the Meteor bundler to ensure we deliver quick improvements to the experience we all know and optimize it further. So even though Rspack already provides smaller bundles, Meteor’s Atmosphere packages will now contribute to reducing the final bundle size as well.

Meteor 3.4 introduces two new features for Atmosphere packages:

  • The ability to mark packages as devOnly, so they’re completely removed from production builds.
  • A new Npm.devDepends field to define dev dependencies, ensuring only the necessary node modules for development are included during dev time and excluded from production (meteor build).

We’ve marked several core packages as dev-only (babel-compiler, typescript, minifiers, and others), which previously added unnecessary size (noted further after the new SWC integration). This also opens the door to fixing bundle size issues in packages that should use dev dependencies, such as reywood:publish-composite and eslint, once their authors update them for Meteor 3.4 and adopt Npm.devDepends, along with any other upcoming revisions in both core and community packages.

As you can see in the picture below, the final bundle size produced by meteor build will be smaller, meaning your Galaxy deployment will take up less space. For example:

image

This is about Atmosphere package size, but it’s also an important note and a useful tip:

If any NPM dependency you’ve added in your app’s package.json is used only for development (for example, loaders and similar tools you may have added after adopting Rspack, or tools like eslint or monorepo), make sure they are listed under devDependencies. Review everything under dependencies and move any dev-only packages to devDependencies, this can significantly reduce your production bundle size. Of course, if some are part of your app’s runtime functionality, leave them there, but it’s worth double-checking. I’ve seen this often in real projects, where production bundles end up unnecessarily heavier because of this.

6 Likes