TL;DR - In this first part of the Migration series we will start upgrading an existing Meteor.js project to 3.0 (alpha). We will be using a determined strategy that should apply to any existing Meteor.js project. Focus is fixing dependency conflicts during the update process.
Follow-up articles will show how to fix dependencies (packages and build-tools) by example 💪
If you already know about Meteor's next major version then you can safely skip this section.
Meteor.js 3.0 is an upcoming major release. The platform is moving away from Fibers (coroutines) in favor of Node's native async-await implementation. The reason behind this step is, that Fibers are not compatible anymore with Node (add links here), beginning with version 16. Due to this circumstance, Meteor is stuck in at Node 14 and this brings lots of implications (security, maintenance, being up to date etc.) for any developer and company.
However, this transition to async-await comes with a massive impact on the very core of Meteor's execution model: In the past you were able to write sync-style code for async operations, for example a MongoDB-Collection fetch:
constdocument=MyCollection.findOne({title:'foo'})
This MongoDB operation has always been async in nature, but Meteor used Fibers to keep track of thing in a way, that the above code resolved without the need of Promises or async/await. In 3.0 operations like these will be replaced by they native Promise implementations and therefore require to be awaited:
This implies a necessary upgrade for every Meteor.js project out there.
Meteor 3.0 is still in Alpha, why doing this now? 🧐
Depending on the size of your project and your team being able to work on such an upgrade you will face challenges on a varying scale and this article should help you to get a first glimpse on the upcoming effort.
This will also help you to make a much better estimation with regards to the complexity and effort of the upcoming migration.
The API of the Alpha versions is 99% stable and most development resides around details or bugs. You can therefore safely start upgrading and refactoring, while you later will only have to switch from Alpha to the stable release.
I have already compiled some steps for preparing your existing projects that won't require an update to 3.0 yet:
From my current efforts I could compile a short but fundamental list of challenges and their manageability:
Challenge
Manageability
Direct dependency conflicts
Easily manageable for your own packages; requires manual fixing for third-party packages
Indirect dependencies version conflicts
Will definitely require manual cloning and fixing
Build tools
Will depend on the complexity of the plugin; will require manual fixing
Breaking Isomorphism
Manageable with big efforts; may require major architectural changes, depending on how much your projects rely on isomorphic code
Rewrite code to use async/await
Involves effort but manageable; read the official docs and my article on this topic for more info
Deployment
Managed for you, when using Galaxy; effort when doing custom deployment; for example with MUP; involves finding correct images, may require to build custom images
As you can see the actual change in your project's code is just a small portion of the overall challenges. This is why this article series tries to deal with all the other parts of it.
A Generic Migration strategy 🧩
My goal for this article was to provide a generic strategy that any of us could apply on their existing projects. Of course, the effort and complexity will vary but the overall procedure should work for anyone. A good approach is to start with a smaller project to get used to the process. Once you succeeded there you will already know most of the intricacies in order to be ready for your larger projects.
This strategy involves using git and assumes to commit often in order to being able to revert to the state of the last step/substep. This will apply for the project, as well as for cloned dependencies. The following example shows how to hard-revert to a certain commit hash:
# show the full list of commits on this branch$ git log
commit 126669f9b29f9172bea4ebaf0599670bd4493f03 (HEAD -> migrate-3.0, origin/master, origin/HEAD)
Merge: a8ac876 7dc118a
Author: Jan Küster <info@jankuester.com>
Date: Mon Nov 28 16:37:38 2023 +0200
migration(1.2): prepared project structure
# reset to the latest commit, omit anything newer than this commit$ git reset --hard 126669f9b29f9172bea4ebaf0599670bd4493f03
The following is a high-level overview of the strategy
Part 1 - Preparations 📝
1.1 Work on an exclusive branch
commit any remaining changes
checkout your most recent development branch
checkout a new branch, exclusively to migration towards 3.0: git checkout -b migrate-3.0
1.2 Prepare project structure for fixing dependency conflicts
if project is in top-level
create a new folder src and move project files in this folder
keep all Git-related files in top-level
create a lib folder in top-level (next to src) where all package-repos will be cloned, that may contain multiple packages or nested structures (see this example)
create packages folder for package-repos that have a direct structure (see this example)
commit your state
1.3 Upgrade to latest 2.x
run meteor update --release=2.13.3 (or newer; see the changelog)
if this causes issues you have to fix them first (out of scope; see the migration notes on the specific versions in the docs)
commit your state
Optional:
place `jkuester:migration-helper (after all core-packages, but before all non-core packages)
flag all non-core packages as # fixme:async that use sync mongo/cursor calls or create sync style methods/publications
comment all packages, expect core packages in .meteor/packages
remove @ suffix from all entries
commit your state
Part 2 - updating to 3.0 and dependency triage 🔬
Important note
During this phase you will often have to run/rerun the app in order to see which dependencies cause a conflict. However, since many of your packages are commented you should not expect the app to successfully start. You will likely then encounter other errors. But that's not the point of this step, it's only about resolving dependency conflicts.
2.1 Update only with core packages
run update meteor update --release=3.0-alpha.19
all core packages should have upgraded successfully and there should be no error message, involving version conflicts (other errors are to be expected, though)
if there is a version conflict, then
make sure the packages left uncommented in this step were only core packages!
comment all core-packages that are involved with the conflict and flag them as fixme:core
if you are 100% sure this is a core-package problem then open an issue in the Meteor GitHub repository
best would be to also add the issue link to the comment flag: fixme:core (link to issue)
if you are < 100% sure then reach out to the community on the Forums, Slack or Discord
else commit your state
2.2 Uncomment non-core packages stepwise (loop)
uncomment a single non-flagged non-core package
try to rerun the app
if this package cause a version conflict
comment and flag as fixme:deps
Proceed with step 3 for this package
else commit your state
if there are commented packages left in .meteor/packages then proceed with Step 2.2
else proceed to step 4
Part 3 - Fixing a dependency 🔥
This step has the scope of a single dependency (package or build tool).
3.1. Determine the package's location
if it's one of your local packages then keep it in it's place
look for the GitHub link on the package's page and navigate to the repository
if this package contains no GitHub repository link then you need to contact the owner to open this package or provide an upgrade, you may also seek help in the community
fork the repository
check the folder structure and determine, whether to place it in packages (proceed with 3.2) or in lib (proceed with 3.3)
3.2. Clone the package into packages
select the green button in the repo (labelled "code") and select the repo link (favorably ssh)
cd into the packages folder
run git clone <link>
cd into the new cloned package
run git checkout -b migrate-3.0
if you get fatal: A branch named 'migrate-3.0' already exists. then you are still in the packages folder!
3.3. Clone the package into lib
select the green button in the repo (labelled "code") and select the repo link (favorably ssh)
cd into the lib folder
run git clone <link>
cd into the new cloned package
run git checkout -b migrate-3.0
if you get fatal: A branch named 'migrate-3.0' already exists. then you are still in the top-level folder!
now you need to link the content of this package (or a subdirectory of the package) with your packages folder
cd back into src/packages
link using ln -sf ../../lib/<repo-name>/path/to/package
3.3 Fixing the package versions
open package.js in your editor/IDE
fix api.versionsFrom, it should look something like api.versionsFrom(['2.8', '3.0-alpha.19'])
search for further dependencies that caused confwhat-is-meteorjs-30-and-why-should-i-care-licts
fix their required dependency version, for example
api.use('minifier-css@1.6.0 || 2.0.0-alpha300.19')
continue with all dependencies in this file until no conflicts occur
commit this state in the package
commit this state in your project
3.4 Fixing the package's code to use async-await
It might be a good opportunity to review the package's code for usage of being involved in any MongoDB Collection or Cursor activity or any imports of 'fibers/future' or Promise.await inside the package's code. If any of them is the case then you will have to migrate this code as well, in order to have the package function properly.
It might be exhausting to realize that the package you used for years and which ran fine now of sudden needs thewhat-is-meteorjs-30-and-why-should-i-care-se big changes. You have basically three options now:
A) find a replacement (causing your project code to be rewritten to the new package's api)
B) contact the package maintainer (can take from a day to infinity to be fixed)
C) do it on your own (involves effort but you are in control of the situation)
This step assumes you go with option C and the follow-up articles will deal with this situation in detail.
3.5 post-fix cleanup
you may have to update the package's tests as well
document and commit your changes
push to your fork git push origin migrate-3.0 while being in the package repository folder
If you want to make this fix available for others then I encourage you to open a pull request
Part 4 - migrate your project code ⌨️
At this step you should have resolved all dependency conflicts and committed the current state.
If not, make sure this is the case, in order to being able of reverting to the state of this commit at any time.
This is the time where your own project code is to be migrated. In an optimal world you may have done this already when being on Meteor 2.x. If you didn't, worry not! 🧙♂️
Here are a few important resources, that might help you to advance in your own code migration:
Guides on upgrading to async/await
The following link should help you with the majority of issues when migrating your projects.
Engage with the Meteor community if you're getting stuck or if you want to share your migration success or if you simply want to get in touch with fellow Meteor developers.
You are finished with your migration and your project runs fine, including all tests. That's awesome! 💪
However, now it's time to get this up and running on the target infrastructure
Deploying a Meteor app can be done in multiple ways but you have to be aware that your deployment environment is 3.0 ready!
With the Meteor Cloud managed services you will already face a Meteor 3.0-enabled environment and you can easily deploy your app in one command. You can try their services with the free plan, perfectly suitable for demos, MVP or proof of concepts.
Deployment to Galaxy is a no-brainer, either via a single cli-command or automated via push-to-deploy. You can read it all up in the deployment guide:
Learn how to deploy on Galaxy by setting up Push to Deploy. The easiest and fastest way of deploying your Meteor app.
galaxy-guide.meteor.com
Custom Hosting using Meteor-Up (MUP)
For those who need alternatives, for example hosting on-premise, there is a great tool out there that simplifies the deployment process a lot: Meteor-Up
With this option you are entirely free to choose where your Meteor apps will run. Of course, this also brings the duties to manage the entire infrastructure on your own.
Furthermore, you will have to get deeper into Docker and Images in order to be able to successfully deploy your 3.0 app using MUP.
Summary and Outlook 🔭
With this article you were given methods and resources to migrate your app towards Meteor 3.0 with a big focus on resolving dependencies.
If you have any questions or trouble with any step of this process, please don't hesitate to share it in the comments or get in contact with the community:
In the next article I will demonstrate the migration process from 2.x to 3.0 with an example app that runs into some dependency conflicts. Together we will solve these conflicts, fix up the code and make the app run again but on the latest 3.0 alpha version.
About me 👋
I regularly publish articles here on dev.to about Meteor.js and JavaScript. Recently I also co-host the weekly Meteor.js community podcast, which contains the newest from Meteor.js and the community.