Give Grunt the Boot! A Guide to Using npm as a Build Tool

来源:转载

Front-end build and workflow tools are available in abundance: Grunt, Gulp, Broccoli, and Jaketo name but a few. These tools can automate almost anything you find yourself doing repeatedly in a project, from minifying and concatenating source files, to running tests or compiling code. But the question is, do you need them? Do you really want to introduce an additional dependency to your project? The answer is “No!”. There is a free alternative that can do the majority of these tasks for you and it comes bundled with Node.js. Of course I’m talking about npm.

In this article we’ll discuss what npmis capable of as a build tool. If you’d like a quick primer on npm before starting, please refer to our beginner’s guide to npm. If you’d like to follow along, you can find the code used in this article on GitHub.

npm Scripts

To start our discussion, we’re going to create a directory for our new demo project, that we’ll call “buildtool”. Once done, we’ll move into this folder and then run the command npm initto create a package.jsonfile:

$ mkdir ~/buildtool && cd ~/buildtool$ npm init

You’ll be asked several questions. Feel free to skip all or part of them as you’ll replace the final content of the package.jsonfile with the following content:

As you can see, we have a scriptsobject with a propertycalled info. The value of infois going to be executed in the shell as a command. We can see a list of the scriptsproperties (also known as commands) and values defined in a project by running the command:

$ npm run

If you run the previous command in our project folder, you should see the following result:

If you only want the output of info, you can use the -sflag which silences output from npm:

$ npm run info -snpm as a build tool

We only used a simple echoso far, but this is a very powerful feature. Everything on the command line is available to us and we can be very creative here. So let’s build on what we’ve covered up to this point and install some packagesto create some common workflows.

Common Workflows More from this author A Beginner's Guide to npm — the Node Package Manager

The first thing we would like to implement is a linting capability for our JavaScript files. This involves running a program that will analyse our code for potential errors. We are going to use JSHintfor this, so the first step is to install the package via npm:

$ npm install jshint --save-dev

After you execute this command, you’ll see a new subfolder named node_modules. This is where JSHint has been downloaded. In addition, we also need to create the following folder structure for our project:

Hopefully the intent of this code is clear — we are declaring a constructor function whose purpose it is to create new Authorobjects. We also attach a couple of methods to Author’s prototypeproperty which will allow us to store and list the articles an author has written. Notice the exportsstatement which will make our code available outside of the module in which it is defined. If you’re interested in finding out more about this, be sure to read: Understanding module.exports and exports in Node.js.

Next, we have to add a propertyto our scriptsobject in package.jsonthat will trigger jshint. To do that, we’ll create a lintproperty as follows:

Here we’re taking advantage of the &&operator to chain the commands and file globs (the asterisk) which gets treated as a wildcard, in this case matching any file with a .jsending within the scriptdirectory.

Note: the Windows command line does not support globs, but when given a command line argument such as *.js, Windows passes it verbatim to the calling application. This means that vendors can install compatibility libraries to give Windows glob like functionality. JSHint uses the minimatchlibrary for this purpose.

Notice how we have wrapped everything in an immediately invoked function expression.

npm run lint -s=> linting

No errors. We’re good!

Testing

First we need to install the mocha package. Mocha is a simple, yet flexible JavaScript test framework for Node.js and the browser. If you’d like to read more about it, this article is a great place to start: Basic Front End Testing With Mocha & Chai

npm install mocha --save-dev

Next we are going to create some simple tests to test the methods we wrote previously. Open up test.jsand add the following content (notice the requirestatement which makes our code available to mocha):

It wouldn’t be very efficient if we were to run our test suite and it bailed straight away because of a syntax error. Luckily npm gives us the preand posthooks, so if you run npm run testit will first execute npm run pretestand npm run posttestwhen it finishes. In this case we want to run the lintscript before the testscript. The following pretestscript makes this possible.

Imagine we hadn’t have corrected the syntax errors in our script previously. In this case, the above pretestscript will fail with a non-zero exit code and the testscript won’t run. That is exactly the behavior we want.

Currently main.jsis not minified. This is as it should be, because it is the file we are working in and we need to be able to read it. However, before we upload it to the live server, we need to reduce its size and place it in the dist/public/jsdirectory. To do this we can install the uglify-js packageand make a new script.

One of the things that Grunt, Gulp and their ilk are great at, is watching a set of files and re-running a specific task whenever one of those files is detected to have changed. This is particularly useful in cases such as this, as it would be a pain in the neck to re-run the minification scripts manually.

The good news is that you can do that in npm, too, using a package such as watch, which is intended to make managing the watching of file and directory trees easier.

$ npm install watch --save-dev

Then in package.json, you need to specify the tasks to run when a change is detected. In this case JavaScript and CSS minification:

Now, whenever any file in assets/scripts/or assets/css/changes, the minification scripts will be called automatically.

Build Script

By now we have several scripts that we can chain together to make a buildscript which should do the following: linting, testing and minifying. It would, after all, be a pain to have to run these tasks individually time after time. To create this build script, alter the script object in package.json, thus:

Of course the serverscript can be added to the buildscript, but I leave that as an exercise for the reader.

Conclusion

Hopefully this article has demonstrated how flexible and powerful npm can be as a build tool. The next time you are starting a new project, try not to reach straight for a tool such as Gulp or Grunt — try to solve your needs by using npm only. You might be pleasantly surprised.

If you have any questions or comments, I’d be glad to hear them in the thread below.