Faster Builds in Meteor 3.3: Modern Build Stack with SWC and Bundler Optimizations

That’s what I call an awesome release. Congrats on the work, guys.

And a huge thank you to this amazing community that makes it all possible :slight_smile:

3 Likes

What are the future of Babel plugins? Right now they are mentioned as “backwards compatible” which sounds to me that they get dropped in a future release?

I ask because right now the only way I know to get decent code coverage for meteortesting:mocha is to use lmieulet:meteor-coverage which uses babel-plugin-istanbul. Do I already need to plan a rewrite soon?

1 Like

After updating to 3.3, no babel configurations or plugins work, which doesn’t let us compile.

I see the move to SWC, but is there any guide on how to migrate everything manually?

1 Like

I don’t think we’ll drop Babel entirely, it’s deeply embedded in Meteor core and many packages still depend on it. Even with SWC as the default in modern bundlers, Babel remains a valid, opt-in choice for its mature ecosystem.

For those who want to move fully to SWC, we’ll need to adapt the Meteor tools that rely on Babel or provide a SWC alternative. For example, there’s the swc-plugin-coverage-instrument, though I’m not sure how much work is needed to make it run on the meteor-coverage package. Testing in general needs modernization. It would be great to address this at some point.

2 Likes

We have added documentation to explain the migration process and the verifications behind it.

Try the verbose option documented and, if you see any errors, report them in an issue, including the Babel plugins you’re using, and any other additional context. Please report an issue on the Meteor issue tracker.

We’d like to support more setups. Even after several betas and RC, some specific setups may still require specific migrations. If SWC doesn’t work for you, you can keep using Babel by disabling the modern setting (it should fall back automatically unless there’s an unknown side effect). We appreciate your feedback to improve compatibility.

2 Likes

I must be doing something wrong – I’m getting no speedup yet with 3.3. I’ve:

  • run meteor update --release 3.3
  • run meteor reset
  • tried “meteor”: {
    “modern”: true
    } at the top level of both package.json and settings.json

…but have seen no speedup yet.

…wait, I’ve got a couple of babel dependencies. That must be it. I’ll try that and report back.

[LATER] No, I’m still not seeing faster builds yet.

In my METEOR_PROFILE printout, I see a lot of dependencies on Babel. Could this be what’s preventing the speedup?

METEOR 3.3 PROFILE (edited to just show babel dependencies)

| (#1) Profiling: Server startup
|
| Server startup…6,966 ms (1)
| ├─ Load server bundles…6,881 ms (1)
| │ ├─ packages/react-fast-refresh.js…12 ms (1) [Uses Babel]
| │ │ └─ require(“/node_modules/meteor/react-fast-refresh/server.js”).12 ms (1)
| │ │ ├─ require(“semver/functions/gte”) 6 ms (1)
| │ │ ├─ require(“react-refresh/babel”) 5 ms (1) [Babel Dependency]
| │ │ └─ other require(“/node_modules/meteor/react-fast-refresh/server.js”) 1 ms
| │ ├─ packages/babel-runtime.js…1 ms (1) [Uses Babel]
| │ ├─ packages/minimongo.js…17 ms (1) [Babel Dependency]
| │ │ └─ require(“/node_modules/meteor/minimongo/minimongo_server.js”).17 ms (1)
| │ │ └─ require(“/node_modules/meteor/minimongo/minimongo_common.js”).16 ms (1)
| │ │ ├─ require(“/node_modules/meteor/minimongo/local_collection.js”).13 ms (1)
| │ │ │ ├─ require(“/node_modules/@babel/runtime/helpers/objectSpread2.js”) 5 ms (1) [Babel Dependency]
| │ │ │ ├─ require(“/node_modules/meteor/minimongo/cursor.js”).4 ms (1)
| │ │ │ │ ├─ require(“/node_modules/meteor/minimongo/common.js”) 2 ms (1)
| │ │ │ │ └─ other require(“/node_modules/meteor/minimongo/cursor.js”) 2 ms
| │ │ │ └─ other require(“/node_modules/meteor/minimongo/local_collection.js”) 3 ms
| │ │ ├─ require(“/node_modules/meteor/minimongo/matcher.js”) 1 ms (1)
| │ │ └─ other require(“/node_modules/meteor/minimongo/minimongo_common.js”) 1 ms
| │ ├─ packages/ddp-client.js…19 ms (1) [Babel Dependency]
| │ │ └─ require(“/node_modules/meteor/ddp-client/server/server.js”).19 ms (1)
| │ │ └─ require(“/node_modules/meteor/ddp-client/common/namespace.js”).18 ms (1)
| │ │ ├─ require(“/node_modules/meteor/ddp-client/common/livedata_connection.js”).16 ms (1)
| │ │ │ ├─ require(“/node_modules/@babel/runtime/helpers/objectWithoutProperties.js”) 2 ms (1) [Babel Dependency]
| │ │ │ ├─ require(“/node_modules/meteor/ddp-client/common/connection_stream_handlers.js”) 1 ms (1)
| │ │ │ ├─ require(“/node_modules/meteor/ddp-client/common/message_processors.js”) 3 ms (1)
| │ │ │ ├─ require(“/node_modules/meteor/ddp-client/common/document_processors.js”) 2 ms (1)
| │ │ │ └─ other require(“/node_modules/meteor/ddp-client/common/livedata_connection.js”) 6 ms
| │ │ └─ other require(“/node_modules/meteor/ddp-client/common/namespace.js”) 2 ms
| │ ├─ packages/babel-compiler.js…154 ms (1) [Uses Babel]
| │ │ ├─ Npm.require(“semver”) 29 ms (1)
| │ │ ├─ Npm.require(“json5”) 4 ms (1)
| │ │ ├─ Npm.require(“@meteorjs/swc-core”) 12 ms (1)
| │ │ ├─ Npm.require(“@meteorjs/reify/lib/compiler”) 101 ms (1)
| │ │ └─ other packages/babel-compiler.js 4 ms
| │ ├─ packages/logging.js…9 ms (1) [Babel Dependency]
| │ │ └─ require(“/node_modules/meteor/logging/logging.js”)…8 ms (1)
| │ │ ├─ require(“/node_modules/meteor/logging/node_modules/@babel/runtime/helpers/objectSpread2.js”) 7 ms (1) [Babel Dependency]
| │ │ └─ other require(“/node_modules/meteor/logging/logging.js”) 2 ms
| │ ├─ require(“/node_modules/meteor/mongo/mongo_driver.js”)…30 ms (1) [Babel Dependency]
| │ │ ├─ require(“/node_modules/meteor/mongo/oplog_tailing.ts”)…29 ms (1)
| │ │ │ ├─ require(“/node_modules/meteor/mongo/node_modules/lodash.isempty/index.js”) 2 ms (1)
| │ │ │ ├─ require(“/node_modules/meteor/mongo/mongo_connection.js”).24 ms (1)
| │ │ │ │ ├─ require(“/node_modules/meteor/mongo/asynchronous_cursor.js”).5 ms (1)
| │ │ │ │ │ ├─ require(“/node_modules/meteor/mongo/mongo_common.js”).4 ms (1)
| │ │ │ │ │ │ ├─ require(“/node_modules/meteor/mongo/node_modules/lodash.clone/index.js”) 3 ms (1)
| │ │ │ │ │ │ └─ other require(“/node_modules/meteor/mongo/mongo_common.js”) 1 ms
| │ │ │ │ │ └─ other require(“/node_modules/meteor/mongo/asynchronous_cursor.js”) 1 ms
| │ │ │ │ ├─ require(“/node_modules/meteor/mongo/cursor.ts”) 2 ms (1)
| │ │ │ │ ├─ require(“/node_modules/meteor/mongo/oplog_observe_driver.js”).8 ms (1)
| │ │ │ │ │ ├─ require(“/node_modules/@babel/runtime/helpers/asyncIterator.js”) 1 ms (1) [Babel Dependency]
| │ │ │ │ │ ├─ require(“/node_modules/meteor/mongo/node_modules/lodash.has/index.js”) 2 ms (1)
| │ │ │ │ │ └─ other require(“/node_modules/meteor/mongo/oplog_observe_driver.js”) 4 ms
| │ │ │ │ ├─ require(“/node_modules/meteor/mongo/polling_observe_driver.ts”) 2 ms (1)
| │ │ │ │ └─ other require(“/node_modules/meteor/mongo/mongo_connection.js”) 5 ms
| │ │ │ └─ other require(“/node_modules/meteor/mongo/oplog_tailing.ts”) 2 ms
| │ │ └─ other require(“/node_modules/meteor/mongo/mongo_driver.js”) 2 ms
| │ └─ other Load server bundles 1,687 ms
| ├─ Call Meteor.startup hooks…73 ms (1)
| └─ Run main() 13 ms (1)
|
| Top leaves:
| other Load server bundles…1,687 ms (1)
| Npm.require(“@meteorjs/reify/lib/compiler”)…101 ms (1)
|
| (#1) Total: 6,966 ms (Server startup)

Just to double-check, where did you add your "modern": true config? It needs to go in your app project’s package.json file (no settings.json). Also, make sure it isn’t already there, most Meteor apps already include the Meteor config with mainModule and testModule settings.

I attach an example of package.json config:

image

Also, to understand if SWC or Babel is used you should activate the verbose mode, adding modern like:

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

I’ll send you a DM to help enable your SWC, and you can forward me the verbose logs so I can verify everything is set up correctly.

Edit:

The “Server startup” log you provided doesn’t prove SWC isn’t used, since SWC runs at build time and the startup phase is runtime, where Babel may still be in use. Instead, the “Build App” phase can be used to confirm SWC is enabled.

Do we still need modernWebArchsOnly and modernTranspiler in our package.json file?

like here:

"meteor": {
    "modern": true,
    "modernWebArchsOnly": true,
    "modernTranspiler": true,
    "mainModule": {
      "client": "client/main.js",
      "server": "server/main.js"
    }
  },

Also running the app with --exclude-archs web.browser.legacy. When I run it, I get this warning:

modern.webArchOnly and --exclude-archs are both active. If both are set, --exclude-archs takes priority.

Thanks in advance, congrats to the team on this milestone,

Andreas

modernTranspiler and modernWebArchsOnly are no longer needed. They were added in the first beta, but in the next beta we replaced them with a single modern field that activates all context optimizations by default. You can opt out of each one within modern.

Please check the docs and each component’s section for more details.

Also running the app with --exclude-archs web.browser.legacy . When I run it, I get this warning:

The warning is harmless. It just means that enabling “modern” already applies “exclude-archs,” so you don’t need to include it.

Thanks @nachocodoner . I’ll reply on the DM.

FANTASTIC SPEEDUP FOUND

Initial Build Timings
Meteor 3.2, with METEOR_PROFILE turned off: 63 seconds

Meteor 3.3, with METEOR_PROFILE and verbose transpiler logs both turned off: 31 seconds

Meteor 3.3, with METEOR_PROFILE and verbose transpiler logs both turned on: 50 seconds

1 Like

Here’s what I learned from @nachocodoner:

If you’re not seeing a speedup, turn on verbose logging by adding this to package.json:

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

The logs will call out things that need to be fixed.

If you use React and have some functions in .js files that return jsx, add this file to the root of your app directory:

filename: .swcrc — first character is . – it’s an invisible file
contents:

{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": true
    }
  }
}

That tells SWC to treat all .js files like .jsx files.

1 Like

Great work, the speed improvements are amazing :smile:

2 Likes

Something I noticed.
After upgrading 3.3 from 3.3-rc.0 I could no longer deploy some of my projects. They would upload to AWS EBS with MUP and fail to start on the EC2.

Deleting the local builds from the .meteor folder and rebuilding again fixed the issue. This happened on 2 (the largest) of 5 projects. 2 others had no issues, the 5th I haven’t tried yet.

It looks like sometimes could be needed meteor reset first, especially after the inclusion of SWC, or as moving from legacy to modern build stack, probably due to transpilation caching inconsistencies. I hope this doesn’t recur with each new release, or it’ll become a hassle.

Please let me know if this happens on future deploys, in the same version or in new ones, including betas. :pray:

It happened to me in the past when moving from one version to another within Meteor 3. I imagined this had something to do with some caching of files in the build, and once enough is changed in Meteor, incompatibilities arise.
If I remember right, meteor reset no longer deletes the local DB. Perhaps, when a new version is published, the announcement could include a suggestion to do a meteor reset just to make sure things are “fresh”

So this issue isn’t limited to the modern build stack? It probably existed earlier. If you learn more, please open an issue in the tracker.

Yes, we avoided deleting the database, that change came in Meteor 3.0.x (the first official version I think). It might be related. I’ll still include the meteor reset suggestion in the announcements.

Thanks for the tip, I’ve got this fallback that I don’t understand:

[Transpiler] Used Babel for blaze/exceptions.js                               (package)                          ⚠️  Fallback (web.browser)

  ↳ Error:   x 'eval' and 'arguments' cannot be used as a binding identifier in strict mode
    ,-[packages/blaze/exceptions.js:56:1]
 53 |   if (typeof f !== 'function')
 54 |     return f;
 55 | 
 56 |   return function (...arguments) {
    :                       ^^^^^^^^^
 57 |     try {
 58 |       return f.apply(this, arguments);
 59 |     } catch (e) {
    `----


Caused by:
    Syntax Error

Also it seems that the owner of ostrio:flow-router-extra and montiapm need to fix these:

[Transpiler] Used Babel for ostrio:flow-router-extra/server/plugins/fast-render.js(package)                          ⚠️  Fallback (os.osx.x86_64)

↳ Error:   x Return statement is not allowed here
,-[packages/ostrio:flow-router-extra/server/plugins/fast-render.js:6:1]
3 | import { FlowRouter } from '../_init.js';
4 |
5 | if(!Package['communitypackages:fast-render']) {
6 |   return;
:   ^^^^^^^
7 | }
8 |
9 | const FastRender = Package['communitypackages:fast-render'].FastRender;
`----

Caused by:
Syntax Error
[Transpiler] Used Babel for montiapm:meteorx/src/fiberless/mongo.js           (package)                          ⚠️  Fallback (os.osx.x86_64)

  ↳ Error:   x 'import', and 'export' cannot be used outside of module code
   ,-[packages/montiapm:meteorx/src/fiberless/mongo.js:6:1]
 3 | export async function exposeMongoAsync(MeteorX) {
 4 |   if (!MeteorX._mongoInstalled) return
 5 | 
 6 |   import { MongoInternals } from "meteor/mongo";
   :   ^^^^^^
 7 | 
 8 |   const coll = _getDummyCollection();
   `----


Caused by:
    Syntax Error

  💡 Tip: Remove nested imports or replace them with require to support SWC and improve speed.

Am I correct?

x ‘eval’ and ‘arguments’ cannot be used as a binding identifier in strict mode

If you rename arguments to args, it passes the SWC syntax check. In strict mode, eval and args are reserved identifiers. Though, that seems to be within the blaze package? If that is, could you provide a PR fixing this?

Also it seems that the owner of ostrio:flow-router-extra and montiapm need to fix these:

Yes, if you can report to those repositories or fix these in a PR it would be great.

Verbose mode lets you check SWC compatibility with your project and ensures you fix all fallbacks using this mechanism.

When we deliver the Meteor-RSPack integration, which uses SWC to transpile and bundle your app code, your project will need to be fully compatible with SWC since RSPack likely won’t offer fallbacks. In Meteor 3.3 modern build stack, you can keep Babel fallbacks for the few affected files and still get good performance. If you want the full benefits of the future modern bundler integration, start preparing now.

1 Like