NPM for Everything
JS ecosystem is famous for it’s wide range of packages and tools. Besides the default package manager, which comes with Node.js – NPM (node package manager), there are Bower.js, Jam.js and many more.
For a quite long period, my default setup was like – NPM for all server-side components and Bower.js for client-side stuff. It worked quite good together, especially in conjunction with Require.js.
But recently, I switched to NPM only setup. That allowed me to use CommonJS style both front and back ends and got rid of task runners as Grunt and Gulp.
CommonJS
Doing a lot of back-end coding I really get used to CommonJS
style, which is simple, practical and powerful. CommonJS modularization is plain as,
1
2
3
4
5
var module = {
};
// export the module
module.exports = module;
and
1
2
// use the module
var module = require('./module');
Node.js highly popularized Common.js approach. And NPM appeared to be one of the best package managers ever developed.
But for front-end, NPM was not that suitable. First of all, because of browsers do not understand Common.js style of modules. Second, npm
modules are being placed to ./node_modules
folder, with is typically the root of project, that made things complicated to refer those scripts from index.html
and configure server to server static resources from ./node_modules
folder.
Browserify came to change this.
Browserify
Browserify is JavaScript transpiling tool, with allows to:
- Use Common.js style for front-end applications.
- Allowed to use various
npm
packages directly in a browser.
A lot of pure front-end frameworks and libraries were published to npm
. In fact, it’s more and more used for front-end libraries as according to NPM developers.
Of cause, nothing comes for free. The cost of Browserify is a build step. You have to introduce to transform Common.js application into a bundle which is loaded and executed by a browser.
Moreover, some packages are still being hosted on Bower. Even, it’s possible to use Bower packages together with NPM packages (by means of debowerify transformation) I quickly realized, that this approach is very unpractical.
So, during the last few months my main contributions on GitHub was adopting of some front-end packages, to be conformant to Common.js and publishing them on NPM after.
The setup
The NPM now is the only one package manager to use, both for front-end and backend.
All dependencies are mentioned on one file package.json
instead of several files.. and you will never forget to run bower install
, since npm install
will do install everything.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"dependencies": {
"async": "^0.9.0",
"babelify": "^5.0.4",
"body-parser": "^1.9.0",
"colors": "^1.0.2",
"cors": "^2.4.2",
"envify": "^3.4.0",
"express": "^4.9.5",
"method-override": "^2.2.0",
"moment": "^2.8.3",
"morgan": "^1.3.2",
"node-logentries": "^0.1.4",
"react": "^0.13.1",
"respawn": "^1.0.1"
}
The server side code,
1
2
3
4
5
6
7
var express = require('express');
// configurations...
app.listen(port, function () {
logger.info('editor-app listening on port ' + port + ' ' + env);
});
The client side code,
1
2
3
4
5
6
7
var React = require('react');
var Component = React.create({
});
module.exports = Component;
The same style, feels nice and clean.
No task runners
NPM is not only package manager. It’s also very simple task runner (or scripts runner). There is a special section, called scripts
responsible for that. Originally it’s been used to run some package related tasks (run tests, perform pre/post install configurations).
Traditionally Grunt.js and Gulp.js are ones that used dealing with compiling static assests. But CLI interface of many tools and stream piping could replace them.
As example, I’ll show pretty typical setup:
- build JavaScript with
browserify
and apply minification
- build Sass with
node-sass
and apply minification
- run watcher and rebuild changed assets
Building JavaScript
The browserify
need to be installed globally,
1
$ npm install -g browserify
In package.json
, in scripts
section, will add such script,
1
2
"scripts": {
"build-js": "browserify public/js/app.js -o public/build/app.js",
Since I also use several transformations in my applications, the Browserify have to properly configured for that. It check’s if package.json
contains browserify
section, and if it does – will use it contents as own options.
1
2
3
4
5
6
"browserify": {
"transform": [
"babelify",
"envify"
]
},
Building Sass
As with Browserify, node-sass
need to be installed globally,
1
$ npm install -g node-sass
Then package.json
changes,
1
2
3
"scripts": {
"build-js": "browserify public/js/app.js -o public/build/app.js",
"build-sass": "node-sass public/sass/main.scss public/build/main.css",
Minification
Now, let’s minify both assets using one of the best concepts ever – Unix pipes.
We need two packages additionally, uglifyjs
and cleancss
.
1
$ npm install -g uglifyjs clean-css
And corresponding scripts,
1
2
3
4
5
6
"scripts": {
"build-js": "browserify public/js/app.js -o public/build/app.js",
"build-sass": "node-sass public/sass/main.scss public/build/main.css",
// minifying
"build-min-js": "browserify public/js/app.js | uglifyjs -o public/build/app.min.js",
"build-min-sass": "node-sass public/sass/main.scss | cleancss -o public/build/main.min.css",
Watching
Now, let’s apply watching. One of the watchers I use for several years already, that works best for me is nodemon
.
nodemon
is not only able to watch file changes and restart node processes if necessary, but also running custom commands.
1
2
3
4
5
6
7
8
"scripts": {
"build-js": "browserify public/js/app.js -o public/build/app.js",
"build-sass": "node-sass public/sass/main.scss public/build/main.css",
"build-min-js": "browserify public/js/app.js | uglifyjs -o public/build/app.min.js",
"build-min-sass": "node-sass public/sass/main.scss | cleancss -o public/build/main.min.css",
// watching
"watch-js": "nodemon -e js -w public/js -x 'npm run build-js'",
"watch-sass": "nodemon -e scss -w public/sass -x 'npm run build-sass'",
Complex tasks
Typically, you need to run few tasks simultaneously, e.g. build all scripts or watch all application. We can combine commands, by running background tasks,
1
2
"build": "npm run build-js & npm run build-sass",
"watch": "npm run watch-js & npm run watch-sass",
To build full app,
1
$ npm run build
or to watch it,
1
$ npm run watch
Using dev-dependencies
Global packages are not cool, cause they might be missing on developers box there application is cloned. Moreover, dependency management becomes a bit more complex, if you need to work on different projects depending on different tools versions. As @bcomnes very correctly noticed, all tools as browserify
, uglify
etc. can be installed as local dev-dependency
, which are fetched during npm install
and will be available to call inside project folder as global commands,
1
$ npm install --save-dev browserify uglifyjs node-sass clean-css
So, the package.json
got such update,
1
2
3
4
5
6
"devDependencies": {
"browserify": "^9.0.7",
"clean-css": "^3.1.9",
"node-sass": "^2.1.1",
"uglifyjs": "^2.4.10"
},
Takeaways
- Once you implement front end library, consider UMD so your modules are reusable in any environment.
- Embrace CommonJS, use Browserify/Webpack/Whatever to use CommonJS (or ES6) on client.
- For many cases, you really don’t need Grunt or Gulp, all is easily doable with NPM scripts.
As example, please refer to brick, the project I currently use as boilerplate for new applications.
JS ecosystem is famous for it’s wide range of packages and tools. Besides the default package manager, which comes with Node.js – NPM (node package manager), there are Bower.js, Jam.js and many more.
For a quite long period, my default setup was like – NPM for all server-side components and Bower.js for client-side stuff. It worked quite good together, especially in conjunction with Require.js.
But recently, I switched to NPM only setup. That allowed me to use CommonJS style both front and back ends and got rid of task runners as Grunt and Gulp.
CommonJS
Doing a lot of back-end coding I really get used to CommonJS
style, which is simple, practical and powerful. CommonJS modularization is plain as,
1 2 3 4 5 |
|
and
1 2 |
|
Node.js highly popularized Common.js approach. And NPM appeared to be one of the best package managers ever developed.
But for front-end, NPM was not that suitable. First of all, because of browsers do not understand Common.js style of modules. Second, npm
modules are being placed to ./node_modules
folder, with is typically the root of project, that made things complicated to refer those scripts from index.html
and configure server to server static resources from ./node_modules
folder.
Browserify came to change this.
Browserify
Browserify is JavaScript transpiling tool, with allows to:
- Use Common.js style for front-end applications.
- Allowed to use various
npm
packages directly in a browser.
A lot of pure front-end frameworks and libraries were published to npm
. In fact, it’s more and more used for front-end libraries as according to NPM developers.
Of cause, nothing comes for free. The cost of Browserify is a build step. You have to introduce to transform Common.js application into a bundle which is loaded and executed by a browser.
Moreover, some packages are still being hosted on Bower. Even, it’s possible to use Bower packages together with NPM packages (by means of debowerify transformation) I quickly realized, that this approach is very unpractical.
So, during the last few months my main contributions on GitHub was adopting of some front-end packages, to be conformant to Common.js and publishing them on NPM after.
The setup
The NPM now is the only one package manager to use, both for front-end and backend.
All dependencies are mentioned on one file package.json
instead of several files.. and you will never forget to run bower install
, since npm install
will do install everything.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
The server side code,
1 2 3 4 5 6 7 |
|
The client side code,
1 2 3 4 5 6 7 |
|
The same style, feels nice and clean.
No task runners
NPM is not only package manager. It’s also very simple task runner (or scripts runner). There is a special section, called scripts
responsible for that. Originally it’s been used to run some package related tasks (run tests, perform pre/post install configurations).
Traditionally Grunt.js and Gulp.js are ones that used dealing with compiling static assests. But CLI interface of many tools and stream piping could replace them.
As example, I’ll show pretty typical setup:
- build JavaScript with
browserify
and apply minification - build Sass with
node-sass
and apply minification - run watcher and rebuild changed assets
Building JavaScript
The browserify
need to be installed globally,
1
|
|
In package.json
, in scripts
section, will add such script,
1 2 |
|
Since I also use several transformations in my applications, the Browserify have to properly configured for that. It check’s if package.json
contains browserify
section, and if it does – will use it contents as own options.
1 2 3 4 5 6 |
|
Building Sass
As with Browserify, node-sass
need to be installed globally,
1
|
|
Then package.json
changes,
1 2 3 |
|
Minification
Now, let’s minify both assets using one of the best concepts ever – Unix pipes.
We need two packages additionally, uglifyjs
and cleancss
.
1
|
|
And corresponding scripts,
1 2 3 4 5 6 |
|
Watching
Now, let’s apply watching. One of the watchers I use for several years already, that works best for me is nodemon
.
nodemon
is not only able to watch file changes and restart node processes if necessary, but also running custom commands.
1 2 3 4 5 6 7 8 |
|
Complex tasks
Typically, you need to run few tasks simultaneously, e.g. build all scripts or watch all application. We can combine commands, by running background tasks,
1 2 |
|
To build full app,
1
|
|
or to watch it,
1
|
|
Using dev-dependencies
Global packages are not cool, cause they might be missing on developers box there application is cloned. Moreover, dependency management becomes a bit more complex, if you need to work on different projects depending on different tools versions. As @bcomnes very correctly noticed, all tools as browserify
, uglify
etc. can be installed as local dev-dependency
, which are fetched during npm install
and will be available to call inside project folder as global commands,
1
|
|
So, the package.json
got such update,
1 2 3 4 5 6 |
|
Takeaways
- Once you implement front end library, consider UMD so your modules are reusable in any environment.
- Embrace CommonJS, use Browserify/Webpack/Whatever to use CommonJS (or ES6) on client.
- For many cases, you really don’t need Grunt or Gulp, all is easily doable with NPM scripts.
As example, please refer to brick, the project I currently use as boilerplate for new applications.