I need to confess – I like Javascript. I hate browsers. I love automation & build tools. I use Javascript in my projects extensively. I’ve been using Javascript build tools for a long time. I’ve used Grunt for a long time, then switched to Gulp. Recently, I realized that sometimes Gulp is overhead. For every single Gulp task, you need a plugin. Gulp plugins add an extra layer as most tools already have a command line interface. I’ll try to illustrate how to reduce the amount of dependencies and create an automated development environment using npm scripts.
The developer can define script in the package.json
file easily:
package.json
After that, foo
script can be executed with npm run foo
command. Quite simple, huh?
Examples
But that task was relatively simple, what about some real world usage? Let’s take a look at Browserify and let’s try to tun it via Gulp & via npm scripts.
package.json
Gulpfile.js
The first snippet looks a bit better, doesn’t it? What about some other tool? Let’s take a look at Webpack example.
package.json
Now it’s time for a real real-world example, a package.json
file for react-router-bootstrap library.
As can be seen in the example above, an almost complex set up is relatively easy to write, understand & maintain.
Lifecycle scripts
Another huge advantage of using npm as a build tool is the fact that npm supports the lifecycle scripts of the “scripts” property of package.json. You can find the complete reference in the npm docs.
Lifecycle scripts might be used to automate pre-
and post-
task scripts.
package.json
As can be seen in the examples above, lifeback scripts might be a real powerful tool to automate any pre-
and post-
task tasks.
Sub-tasks
Every single good automation system should be as simple as possible. Configuration should be easy to understand and even easier to maintain. As maintaining shell commands might be relatively hard for a non-geeky person, it’s important to create simple scripts that can be executed subsequently or “merged” runtime.
Let’s take a look at the example of bad practices:
Why do I consider the example above a bad practice? It doesn’t allow the developer to execute the command separately (in this example a separate script will be required to run eslint
or karma
separately). It’s also hard to read and maintain.
Now, the second version of the same package.json file:
I think the second version is way better, as it gives the developer possibility to run every single command separately. Additionally, it follows the single responsibility principle, as every single task is responsible only for running single tool according to it’s name. On the other hand, the test
scripts runs all test-related scripts.
Drawbacks
Using npm scripts as a build tool is a fantastic approach, however, there are few drawbacks that need to be mentioned:
- less extensible code – while keeping scripts clean & easy to maintain, they become less extensible
- multi-platform compatibility – one does not simply use
rm
on Windows - lack of CLI – some tools don’t provide any CLI.
Rationale
I don’t want to negate the point of build tools. The idea of this blog post is to show an alternative approach which might be a better solution for common problems. As a build tool already depends on a CLI library, why introduce an extra layer? Why not try to drop all intermediate dependencies and communicate with a tool directly via CLI?
I recently switched to npm scripts in my every single personal project & to be honest I’m not even thinking about switching back to Gulp or Grunt anymore.