Today's topic is upgrading dependencies on an older Ember app. This is a real-world app and the issues we face will be different from your apps, but you can learn the overall strategy and debugging approaches,
This is Part 2 of a series of blog posts. We're on a journey together to remodel an older Ember app, incrementally bringing it up to date with the latest and best Ember and Ember Data patterns.
About the series
We will walk through updating a complex, real-world app, ember-api-docs, which is the front end of api.emberjs.com. Along the way, you will learn how to approach this process for your own apps - updating dependencies, debugging build errors, migrating to Octane syntax, writing better Ember Data serializers & adapters… there's a lot to do!
For this series, I'm pair programming with Chris Thoburn, aka @runspired, who is known for his work on Ember Data. He has over 500 commits and some great debugging skills that you and I can learn from.
Deciding how to approach upgrading dependencies
There are two main approaches Ember developers take, either:
- Upgrade addons
- Upgrade core Ember and Ember Data dependencies
Or, they approach this in the reverse order:
- Upgrade core Ember and Ember Data dependencies
- Upgrade addons
The path you choose depends on your app - how many addons does it have? How outdated are they?
In our case, we decided on the latter. We didn't want to introduce too many changes at once if we didn't have to, and we were pretty confident in our ability to sort through addon-specific errors.
Running the Ember and Ember Data upgrade
This app was on 3.16. The latest Ember and Ember Data version is 3.26.
We can do the upgrade with the help of ember-cli-update
, which will apply the blueprint used for fresh apps.
npx ember-cli-update
You can read a whole bunch about upgrading in the Ember Guides and Ember CLI Guides, so we won't go into too much detail here.
We reviewed each file that was modified first, to confirm that we wanted those changes.
Then, we walked through all merge conflicts and resolved them.
Next, we installed our new dependencies with yarn install
.
Finally, we started the Ember server with npx ember serve
and hit our first bug.
(Why npx ember serve
? npx
ensures that the Ember CLI version used by
your app is the one specified in package.json, and not your globally
installed version. It usually doesn't matter, but when it does, it's often
when working on upgrade tasks like this!)
Debugging a build failure
Here's the build failure we saw in the console after we started up our server:
Build Error (broccoli-persistent-filter:Babel > [Babel: @ember/ordered-set]) in @ember/ordered-set/index.js
Duplicate plugin/preset detected.
If you'd like to use two separate instances of a plugin,
they need separate names, e.g.
plugins: [
['some-plugin', {}],
['some-plugin', {}, 'some unique name'],
]
Duplicates detected are:
[
{
"alias": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js",
"options": {
"emberVersion": "3.26.1",
"root": "/Users/jweber/projects/ember-api-docs",
"name": "@ember/ordered-set"
},
"dirname": "/Users/jweber/projects/ember-api-docs",
"ownPass": false,
"file": {
"request": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js",
"resolved": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js"
}
},
{
"alias": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js",
"options": {
"emberVersion": "3.26.1",
"root": "/Users/jweber/projects/ember-api-docs",
"name": "@ember/ordered-set"
},
"dirname": "/Users/jweber/projects/ember-api-docs",
"ownPass": false,
"file": {
"request": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js",
"resolved": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js"
}
}
]
Not very nice.
We did yarn why @ember/ordered-set
to confirm that only one version of
that dependency was in use:
yarn why @ember/ordered-set
yarn why v1.22.10
[1/4] 🤔 Why do we have the module "@ember/ordered-set"...?
[2/4] 🚚 Initialising dependency graph...
[3/4] 🔍 Finding dependency...
[4/4] 🚡 Calculating file sizes...
=> Found "@ember/ordered-set@4.0.0"
info Reasons this module exists
- "ember-data" depends on it
- Hoisted from "ember-data#@ember#ordered-set"
- Hoisted from "ember-data#@ember-data#record-data#@ember#ordered-set"
info Disk size without dependencies: "44KB"
info Disk size with unique dependencies: "1.98MB"
info Disk size with transitive dependencies: "47.57MB"
info Number of shared dependencies: 151
✨ Done in 1.83s.
Chris hypothesized that this was due to floating dependencies. Yarn and NPM let developers specify a range of acceptable dependency versions. Sometimes there are mismatches. It's often a good initial step to try deleting node modules and the lock file, as a small experiment, and then try again:
rm -rf node_modules
rm yarn.lock
yarn install
npx ember s
That resolved the error about duplicate plugins!
On to the next error - dependencies of dependencies
Next, we saw some errors about ember-popper
.
We looked to see what was using ember-popper
using yarn why
.
One of our dependencies, ember-styleguide
used it. We looked at
a more recent version of ember-styleguide
, in the package.json
.
The latest ember-styleguide didn't even use popper,
so upgrading ember-styleguide got us over this hurdle.
We also saw an error about ember-basic-dropdown
.
yarn why
told us it was used by ember-power-select
.
Upgrading ember-power-select
and following instructions in the
CHANGELOG fixed that issue.
Now, when we did yarn install
and npx ember s
, the app built successfully.
Debugging runtime errors
Now when we visted localhost:4200
, we saw something strange.
The app would render for a moment (thanks to Fastboot),
but then when the app's JavaScript loaded in fully, it crashed.
Apps that use Fastboot show their errors in the terminal running the server,
not the browser console. So, to get a closer look at what was going on,
we temporarily turned off fastboot by visiting http://localhost:4200?fastboot=false
.
There were two errors in the browser console:
One:
Error occurred:
- While rendering:
-top-level
application
es-header
es-navbar
bs-navbar
bs-navbar/content
bs-collapse
bs-navbar/nav
search-input
ember-tether
search-input/dropdown
Two:
Uncaught (in promise) ReferenceError: process is not defined
<anonymous> Ember
js vendor.js:173080
__webpack_require__ vendor.js:172838
<anonymous> Ember
js vendor.js:173069
__webpack_require__ vendor.js:172838
<anonymous> Ember
js vendor.js:173126
__webpack_require__ vendor.js:172838
...and a whole lot more
We wanted to see where the problem was coming from, as step one.
We commented out the es-header
component and saw the app render.
Then we commented out pieces of the app until we got to
identifying search-input
as the culprit.
The search feature of this app comes from a third-party library.
Is something in there using process
?
I enabled "Break on exceptions" in my browser's debugging tools.
This showed us that indeed, process.env
was being used in a third party script.
But why did this work before? What else had changed?
Debugging webpack
The webpack errors are a clue about what changed.
When we updated a bunch of things in package.json
using ember-cli-update
,
one of them was ember-auto-import
. This package uses webpack to
bring npm dependencies into an Ember app with zero config,
and we changed its versions.
One thing that changed was that certain polyfills were no longer included. This was outlined in the project README along with steps to fix it.
We added the following to our ember-cli-build.js
:
autoImport: {
webpack: {
node: {
process: 'mock'
}
}
},
Now, the app rendered successfully! We removed ?fastboot=false
from our
localhost URL, and saw that it was working fine in that mode too.
Up next
So, the app rendered, but the tests are failing. Coming soon, we'll tackle those test failures.