The difference between ember serve
and npm run start
When you have built a single-page-application using Ember CLI, you have two options for starting your app locally. You can either use the CLI's ember serve
command directly or you can use the handy npm
alias that is created for you after generating a new Ember app: npm run start
But what's the difference between those two?
What npm run start
is used for
First, npm run start
allows you to create your own default boot up command for your application by e.g. passing additional parameters to ember serve
and hide this modified booting instruction away in one, single, shorthand command via a definition in your package.json
. This makes you type less and can make your life easier when developing your app locally.
If you want to learn more about how to modify the ember serve
command, check out the official Ember CLI Docs.
In a fresh Ember app though, the functionality of both ember serve
and npm run start
will be almost identical. Emphasis on almost.
Sometimes ember serve
and npm run start
don't behave the same way
Recently, while working on one of my side projects, I tried to boot up an Ember application using TailwindCSS by running the ember serve
command. The build process was kicked off, but ultimately failed with the following error message:
Object.entries(...).flatMap is not a function
The stack trace pointed to issues with the PostCSS
compiler I had been using to integrate TailwindCSS with my app's styles:
Object.entries(...).flatMap is not a function
at PostcssCompiler.Plugin (/home/jayjayjpg/Documents/projects/my/project/node_modules/broccoli-plugin/index.js:7:31)
at new CachingWriter (/home/jayjayjpg/Documents/projects/my/project/node_modules/broccoli-caching-writer/index.js:18:10)
at new PostcssCompiler (/home/jayjayjpg/Documents/projects/my/project/node_modules/broccoli-postcss-single/index.js:20:5)
at Object.keys.map (/home/jayjayjpg/Documents/projects/my/project/node_modules/ember-cli-postcss/index.js:36:12)
// ...
After some debugging and double-checking my setup that I have followed from Chris Masters' working example for TailwindCSS in an Ember app in my ember-cli-build.js
was correct:
// ember-cli-build.js
'use strict';
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const isProduction = EmberApp.env() === 'production';
const purgeCSS = {
module: require('@fullhuman/postcss-purgecss'),
options: {
content: [
// add extra paths here for components/controllers which include tailwind classes
'./app/index.html',
'./app/templates/**/*.hbs',
'./app/components/**/*.hbs'
],
defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
}
}
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
postcssOptions: {
compile: {
plugins: [
{
module: require('postcss-import'),
options: {
path: ['node_modules']
}
},
require('tailwindcss')('./app/tailwind.config.js'),
...isProduction ? [purgeCSS] : []
]
}
}
});
return app.toTree();
};
...I started to do a quick google search, to see if any other folks using TailwindCSS ran into a similar issue.
And indeed, I found a couple of issues here and there that pointed to the fact, that the .flatMap
method used at build time of my application was a more recent feature of Node and only available from Node v11+.
I confirmed that I was still on an older version of Node, that this would explain the lacking support of the .flatMap
function:
node -v
10.16.3
...and was enthusiastic about quickly resolving the build problem with an upgrade of Node. Since I was using nvm my upgrade turned out as follows:
nvm install 14
nvm use 14
rm -rf node_modules/ && npm install
As soon as the installation finished, I tried my luck again by running ember serve
, and to my surprise - the build failed again with the very same .flatMap is not a function
error!
Still in disbelief, I tried running npm run start
instead and lo and behold - my app built successfully?
How could my application build fail when the build command was run directly, but still succeed when its alias was executed?
How Node, NVM and Ember CLI work together
Once I checked on the versions of my Ember CLI and my Node installs, it became more clear, why npm run start
and ember serve
would behave differently:
node -v
v14.17.1
ember -v
ember-cli: 3.26.1
node: 10.16.3
os: linux x64
How come that Ember CLI was linked to an older version of Node different from my local Node version?
In my case, I've been using nvm
to switch between different versions of Node on my machine.
When using nvm, it's important to be mindful on how this will affect the usage of globally installed packages. Nvm ensures that any global installs of binaries end up in a dedicated, versioned directory of nvm
in the user's HOME
directory. On Linux, you can verify this by checking the Ember CLI's binary location as follows:
whereis ember
ember: /home/jayjayjpg/.nvm/versions/node/v10.16.3/bin/ember
A while ago I had installed Ember CLI globally via npm install -g ember-cli
while on Node 10.16.3. This instructed nvm
to store the binary in the related 10.16.3 Node directory and make it available via this Node version. Now whenever I would run ember serve
on my command line, this outdated binary would be used, running on Node 10.16.3, regardless if I had instructed nvm
to switch to Node v.14.17.1 or not:
# Switching to Node 10.16.3
nvm use 10
node -v
v10.16.3
ember -v
node: 10.16.3
# Switching to Node 14.17.1
nvm use 14
node -v
v14.17.1
ember -v
node: 10.16.3
Whenever I would run npm run start
though, my project's local Ember CLI version would be used leading to a successful app build.
The behavior of ember serve
and npm run start
doesn't have to differ this way though, as nvm
provides you with a command to migrate all of your already existing, global npm installations over to a newer version of Node and make them available when switching to said version:
nvm install 14 --reinstall-packages-from=10
After the migration I could now see my global Ember CLI binary associated with the newer Node version and residing in the path that I'd expect:
whereis ember
ember: /home/jayjayjpg/.nvm/versions/node/v14.17.1/bin/ember
ember -v
ember-cli: 3.26.1
node: 14.17.1
And finally, upon running ember serve
, the app would build successfully using Node v.14.17.1 just as it would using npm run start
!
So what's the difference between ember serve
and npm run start
?
In a fresh Ember app and in nearly all cases, npm run start
and ember serve
function the exact same way. But if you have a different version of Ember CLI installed globally versus locally or if you're using version managers for Node, the outcome of these two commands may differ from each other.
This post has been inspired by this great response on Stackoverflow to the question "Is there a difference between ember serve
and npm start
?"
Top comments (5)
Very helpful insights. Thanks for sharing.
There is one additional difference between
npm run start
andember serve
: Ember CLI must be installed globally forember serve
to work.npm run start
also works with having Ember CLI installed as a project dependency only. This may help with onboarding new developers and to speed up CI. For CI it's most likely not aboutember serve
butember test
vs.npm run test
.You should checkout volta as a replacement for
nvm
especially for solving some of those issues with global packages.+1
Thank you for the article. It seems this issue isn't specific to ember, but rather would pop up for any npm package installed globally with -g flag.
Yes, exactly!