Meteor + Webpack integration - Tree-shaking, Secured code and more

Hello everyone!

Some of you may know me, others don’t. As I haven’t posted for a long time, I will take this as a quick presentation. I’m Nacho Codoñer, a former core developer from Pathable platform, which sadly was shutdown as you may already know. I have been developing with Meteor in my entire career, and I have developed much enthusiasm in every part of the software development process.

Currently, I dedicate my time to make to real a dream app, of course using Meteor, but other kind of interesting tooling learned over previous years.

Today I would like to present a straightforward solution that I have on my projects to have Tree shaking, secured code, and other advantages on last compilers features. I am using the best Webpack can offer along with what Meteor delivers.

I used a small repository from the community: fredmaiaarantes/simpletasks.

You can check out this forked repository where I explain it with more details:
https://github.com/freak2geek/simpletasks-webpack

The solution doesn’t rely on modifying the core or creating any Meteor build package as was a previous solution. Instead it keeps a simple solution of script processes and configuration (a bit hacky maybe). I am working with a more advanced project and it works perfectly with much more stuff integrated on the webpack configuration. So I believe it may be useful for some of you, at least the idea that you can go beyond the limits that Meteor imposes if you need.

Surely, when there are any advantages, it comes with its own set of disadvantages, like I’m not using anymore nested imports and dynamic imports using DDP protocol. But for me this is like what is happening with Fibers, non-standard methodologies will become a problem and drive us to reformulate our solutions at some point.

Just that, I hope it is useful for someone and I will try to get more time to share learnings on my way. I am also using CapacitorJS with a similar kind of solution that I hope to validate soon and share with you my journey.

11 Likes

What are your thoughts on using Vite? Is Vite a potential replacement for Webpack or are they two different animals?

It’s possible that the gains in tree-shaking outweighs the gains of nested/dynamic imports.

It might be possible to create a webpack plugin to leave those nested/dynamic imports as is since meteor will be able to handle those during its bundling process

1 Like

I’m posting this question to the community, hoping someone can provide a clear answer. I am not experienced in Vite, the only thing I know is that it is “more performant” in theory because it uses browser native capabilities during development. However, I lack practical experience with Vite’s environment, features, and plugins.

In general, I tend to stick with technologies I already know well instead of adopting new ones to cover capabilities that existing technologies already support. My approach is to maximize the advantages of my existing knowledge rather than chasing the latest hype. Of course, this perspective varies for each individual.

In contrast, I can say which are the strong points of Webpack. The ecosystem of Webpack is so mature and widely adopted, a huge community of developers is behind.

It offers an impressive set of core capabilities, including code splitting (aka dynamic imports), tree shaking, asset management, cache, web workers, advanced minification, and much more; all of these with full customization for advanced usage.

One of the standout features of Webpack is its plugin system. There are some built-in plugins like “Define plugin” for generating secure code bundles, the community also offers a wide array of plugins that can handle almost anything you can imagine, before, during, and after compilation.

Here are some additional examples of useful configurations and extensions:

And much more that you will find over your research.

Just a clarification here. Dynamic imports are already supported by Webpack. I have provided additional details on this topic in the repository, available here.
However, the approach Meteor used to apply dynamic imports is not supported so far through this setup. As you suggested, I achieved it through a Webpack’s configuration to ignore import statements and ready on the Webpack’s output for them to be available for Meteor compilation and parsing, but I encountered some issues during production builds. Surely it can be adapted if explored further, but I decided to skip it as the effort required to make it effective didn’t seem justified for my current needs. Webpack’s dynamic imports are enough for my purposes.

Regarding Meteor’s nested imports, I also attempted to replace them with Webpack before and after compilation. However, it didn’t seem worth it for my current needs. I might consider re-attempting this at a later point in time.

1 Like

Thanks for sharing the solution with us, Nacho!

It’s amazing to see that we can make Meteor and Webpack work together in such a seamless way. The reduction in size thanks to tree-shaking is very impressive.

3 Likes

I went a different direction with React & Meteor. With react-router-dom@6, and using createBrowserRouter with lazy and loading the component and your data in parallel, I was able to achieve even better performance and the bundle size is in kb not mb.

I used this guide as a reference and it works very well with Meteor: Lazy Loading Routes in React Router 6.4+ | Remix

Posting this in case someone wants similar gains and doesn’t use webpack.

4 Likes

In an ideal scenario, both dynamic loading (lazy loading) and tree shaking should be available in Meteor.

2 Likes

This is fantastic, @nachocodoner! I too am surprised and impressed that Meteor and Webpack can be used together in this way. Thank you for sharing.

I also wanted to say that I am super interested to hear about your experiences with Capacitor someday. :smiley:

1 Like

@nachocodoner – this is super interesting! Do you think it would be possible to use the Million.js webpack plugin with this approach?

1 Like

I have given a quick attempt to enable it since the setup guide seems to be straightforward. However, I realized that millionjs requires node >15 to work (Meteor currently limited to 14 version). I tested by just running the webpack process with a higher node version, however, I run into other issues.

Definitely it is an interesting configuration to try, but it would be really helpful to finally reach the Meteor node upgrade version which is close in order to facilitate the setup and the inclusion of this kind of modern tooling.

Thank you for sharing this tool, it seems promising, maybe when I have some spare time I will continue my attempt on it.

1 Like

I believe the next 3.0 alpha will have support for Node 18. Hopefully it’ll drop soon: Release 3.0 by denihs · Pull Request #12359 · meteor/meteor · GitHub

3 Likes

Thanks for the update, really appreciate you spending some time on this. Hopefully we can get there soon with Meteor v3 :pray:

3 Likes

I have upgraded the simpletasks-webpack app to Meteor version 3.0.1.

Additionally, I added esbuild-loader as a replacement for babel, providing significant build performance improvements. Similar results using swc-loader.

On my machine, using the latest simpletasks-webpack, I observed a 50%-80% gain with a simple change to the webpack configuration. This change doesn’t affect the meteor build and server start times, as it only impacts the app build process, on which is responsible webpack.

Before (babel)

Client

Cold start: 620ms
Incremental change #1: 97ms
Incremental change #2: 33ms

Server

Cold start: 535ms
Incremental change #1: 366ms
Incremental change #2: 59ms

After (esbuild)

Client

Cold start: 154ms (75% faster)
Incremental change #1: 44ms (54% faster)
Incremental change #2: 17ms (48% faster)

Server

Cold start: 95ms (82% faster)
Incremental change #1: 75ms (79% faster)
Incremental change #2: 20ms (66% faster)


If you’re using a modern bundler to build your app code while having Meteor inject logic from atmosphere packages, consider replacing babel with esbuild or SWC. The project I am working on has seen impressive benefits from this change.

This is just an alternative approach and won’t be integrated into Meteor core due to its incompatibility with specific Meteor bundle capabilities. This consensus was discussed in other thread, where you can also understand more the approach discussed here. As a core solution for reducing build times, we will still explore various approaches. However, this approach explained here may be sufficient for many users.

6 Likes

@nachocodoner, thanks for exploring and sharing the details, as usual.

If I understand this correctly, what we will be losing with this approach is the legacy bundle. So if we don’t need the legacy bundle, this will be a very good alternative.

1 Like

@nachocodoner ,Hi
How are you deploying the server?
I am distributing it using mup. Is there a way to distribute it in conjunction with this?

The repository deploys using Galaxy, with the app deployed there via Webpack as the bundler.

For alternative deployment strategies, simply build the app. A script is provided in the repository to assist with this task (npm run build). First, Webpack compiles your app, and then Meteor builds the deployment artifacts (./dist/**). You can use these artifacts to run it directly on your server or via the zodern/meteor image or any custom image. This is the approach I use on my apps.

Regarding meteor-up (MUP), it’s a configuration-based strategy that I haven’t tried personally. However, if you first build the Webpack packages using npm run build:webpack, mup deploy should pick up the correct Webpack files for client and server builds and integrate them with the Meteor build for deployment. Let me know if that works.

Using esbuild, you work with modern bundles. However, esbuild only compiles the app’s client and server code. The result is then recompiled by Meteor, including Atmosphere packages. Meteor still relies on Babel and legacy transpilation. While I haven’t tested it, it seems likely to be supported as soon as you enable legacy on Meteor itself.

MUP is not affecting the build process afaik it uses meteor build under the hood. It’s main job is configuring the target server (docker, nginx, let’s encrypt, mongodb etc.) and deploys the app.

If your bundle runs locally using esbuild then it should also do when deployed unless there is a missing step in the minification phase.