Abstract: Some argue that front-end building tools such as Gulp and Grunt are still indispensable, while others argue that Gulp and Grunt are unnecessary and add a layer of abstraction that can cause problems. Recently, Cory wrote about his understanding of Gulp, Grunt, and NPM scripts, and believes that in today’s projects, we can do away with Gulp and Grunt and use NPM scripts.
Cory House is the author of “Building Applications with React and Flux” and “Clean Code: Writing Code for Humans,” as well as the instructor of numerous classes at Pluralsight. He is a software architect at VinSolutions, where he has trained numerous software developers around the world in software development practices such as front-end development and clean code. Cory is Microsoft MVP, Telerik developer expert, and founder of Outlierdeveloper.com. Currently, there is much debate around the Gulp, Grunt, and NPM Scripts communities about the continued use of Gulp and Grunt in projects. Some argue that front-end build tools such as Gulp and Grunt are still essential, while others argue that Gulp and Grunt are completely unnecessary and add a layer of abstraction that can cause problems. Recently, Cory wrote about his understanding of Gulp, Grunt, and NPM scripts, and believes that in today’s projects, we can do away with Gulp and Grunt and use NPM scripts.
Gulp and Grunt are well-known build tools used by many projects, and they also have a wealth of plug-ins. However, I think Gulp and Grunt are completely unnecessary abstractions and NPM scripts are more powerful and easier to use.
I am a fan of Gulp. In my last project, however, gulpfile was over 100 lines long and used quite a few Gulp plug-ins. I’m trying to integrate Webpack, Browsersync, hot loading, Mocha, etc. with Gulp. Why? This is because some plug-ins are poorly documented; There are also plug-ins that only expose part of the API I need. One of the plug-ins had a strange Bug that allowed it to see only part of the file. Another plugin loses color when output to the command line.
Of course, these problems are solvable; However, when I use these tools directly, all problems disappear. Lately, I’ve noticed a lot of open source projects just using NPM Scripts. So I decided to re-examine my approach. Do I really need Gulp? The answer is: not at all. I decided to use only NPM Scripts in my new open source project. I set up the development environment and build process for a React application using only NPM Scripts. Want to know what the project looks like? Watch React Slingshot. Right now, I prefer NPM scripts to Gulp, and here’s why.
What happened to Gulp and Grunt?
Over time, I’ve seen three core problems with task runners like Gulp and Grunt:
- Dependencies on plug-in authors
- Frustrating debugging
- Disconnected documents
The following is a detailed analysis of the above three problems.
Problem 1: Dependency on plug-in authors
In the case of newer or less popular technologies, there may be no plug-ins at all. By the time plug-ins are available, they may already be obsolete. For example, Babel 6 was released a while ago. The API has changed so much that many Gulp plug-ins are not compatible with the latest version. When using Gulp, I was deeply hurt because the Gulp plugin I needed hadn’t been updated yet. With Gulp or Grunt, you have to wait for the plugin maintainer to provide updates or fix them yourself. This will hinder your chances of using the latest version of modern tools. In contrast, with NPM Scripts, I go straight to the tools and don’t have to add an extra layer of abstraction. This means that when a new version of Mocha, Istanbul, Babel, Webpack, Browserify, etc., comes out, I can use the new version immediately. For choice, nothing beats NPM:
As you can see from the figure above, Gulp has nearly 2,100 plugins; Grunt has nearly 5,400; NPM offers more than 227,000 packages, and continues to add more than 400 a day.
When using NPM scripts, you no longer need to search for Grunt or Gulp plugins; Just choose from more than 227,000 NPM packages. To be fair, if the required Grunt or Gulp plug-ins don’t exist, you can certainly use NPM Packages. However, it is no longer possible to use Gulp or Grunt for this particular task.
Problem 2: Frustrating debugging
Debugging in Grunt and Gulp can be frustrating if integration fails. Because you’re dealing with an extra layer of abstraction, there are more potential causes for any Bug:
- Is there a problem with the basic tools?
- Is there a problem with Grunt/Gulp?
- Is there a configuration problem?
- Are the versions used incompatible?
Using NPM scripts eliminates point 2 above, and I find that point 3 is also rarely present because I usually call the tool’s command line interface directly. Finally, point 4 is also rare, because I reduced the number of packages in the project by using abstraction directly from NPM instead of the task runner.
Problem 3: Disjointed documentation
In general, the documentation quality of the core tools I need is better than that of the Grunt and Gulp plug-ins associated with them. For example, if I use gulp-ESLint, I switch back and forth between the gulp-ESLint document and the ESLint website; You have to switch context back and forth between the plug-in and the tool it abstracts from. The problem with Gulp and Grunt is that it is not enough to understand the tools used. Gulp and Grunt require that you also understand the abstraction of plug-ins.
Most build-related tools provide a clear, powerful, and well-documented command-line interface. The ESLint CLI documentation is a good example. I find it easier to read and implement a short command line call in NPM Scripts, less cumbersome, and easier to debug (because no abstraction layer exists). Now that we know the pain points, the question is, why do we feel we need task runners like Gulp and Grunt?
I believe the reasons are individual. There’s no doubt that task runners like Gulp and Grunt have been around for a long time, and the plugin ecosystem around them is thriving. Depending on these plug-ins, many daily tasks can be automated and run well. Thus, it is assumed that only through these task runners can tasks be built, files packed, workflows run well, and so on. Another reason is that NPM Scripts is not well understood; NPM scripts is also superficial in what it can do. This further results in people not realizing that NPM Scripts can perform many of the daily build tasks of development. I believe that as developers learn more about NPM scripts, they will find that NPM scripts can do the same things that Gulp and Grunt task runners can do with much simpler and more straightforward configuration. Because it uses the target tool directly without having to use the wrapper for the target tool.
Why did we ignore NPM during build?
I think there are four reasons why task runners like Gulp and Grunt have become so popular:
- NPM Scripts is thought to require strong command-line skills
- NPM Scripts is not considered powerful enough
- Gulp streams are considered indispensable for rapid builds
- NPM Scripts is not considered cross-platform
Let me explain these misconceptions in order.
Myth # 1: Using NPM scripts requires strong command-line skills
You don’t really need to know much about the operating system command line to experience the power of NPM Scripts. Of course, grep, sed, AWk, plumbing, etc., are skills worth learning and useful to all. However, you don’t have to be a Unix or Windows command-line expert to use NPM Scripts. There are over 1000 well-documented scripts in NPM to get the job done.
For example, you may not know that in Unix, the command rm -rf forces a directory to be deleted, which is fine. You can do the same with Rimraf (which is also cross-platform). Most NPM packages provide interfaces that assume you don’t know much about the command line of your operating system. Just search NPM for the packages you want to use and learn as you go. In the past, I used to search for Gulp plug-ins, but now I search for NPM packages. Libraries. IO is a great resource.
Myth # 2: NPM scripts are not powerful enough
NPM Scripts is actually quite powerful on its own. It provides convention based pre and Post hooks:
{name: "NPM scripts example", version: "1.0.0", description:" NPM scripts example", scripts: {prebuild: "echo I run before the build script", build: "cross-env NODE_ENV=production webpack", postbuild: "echo I run after the build script" } }Copy the code
All you have to do is follow the agreement. The above scripts are run in order according to their prefixes. Prebuild scripts are run before build scripts because they have the same name, but prebuild scripts have a “pre” prefix. The postbuild script is run after the build script because it has the “POST” prefix. So, if you create scripts called prebuild, Build, and postbuild, they will automatically run in that order when I type “NPM run build”.
In addition, you can break down large problems by calling another script in one script:
{"name": "NPM scripts example", "version": "1.0.0", "description":" NPM scripts example", "scripts": {"clean": "rimraf ./dist && mkdir dist", "prebuild": "npm run clean", "build": "cross-env NODE_ENV=production webpack" } }Copy the code
In the example above, the Prebuild task calls the Clean task. This allows you to break down the script into smaller, well-named, single-responsibility, single-line scripts.
Multiple scripts can be called consecutively on a single line by &&. In the example above, the scripts in the Clean step are run one after the other. This simplicity will certainly appeal to you if you need to run tasks in a task list in Gulp one after the other in order.
If a command is complex, a separate file can be called:
{"name": "NPM scripts example", "version": "1.0.0", "description":" NPM scripts example", "scripts": {"build": "node build.js" } }Copy the code
I called a separate script in the build task above. The script will be run by Node, so I can use any NPM packages I need, while also taking advantage of JavaScript capabilities. I could go on, but interested readers should refer to the core features document. There is also a course on how to use NPM as a build tool at Pluralsight. You can also see React Slingshot to get a sense of how it works.
Myth 3: Gulp streams are essential for fast builds
When Gulp came out, people quickly gravitated to it and abandoned Grunt because Gulp’s memory flow was much faster than Grunt’s file-based approach. However, you don’t really need Gulp to enjoy the power of streams. In fact, streams have long been built into the Unix and Windows command lines. Pipe (|) operator will as the output of a command to flow through the input of another command. The redirect (>) operator redirects output to a file. In Unix, for example, I can “grep” the contents of a file and redirect the output to a new file:
Grep 'Cory House' bigfile. TXT > linesthathavemyname.txtCopy the code
The above process is streaming and is not written to an intermediate file (want to know how to implement the above command in a cross-platform manner? Read on).
In Unix, it is also possible to run two commands simultaneously with the ampersand operator:
npm run script1.js & npm run script2.js
Copy the code
Both scripts run simultaneously. To run the script simultaneously in a cross-platform manner, use npm-run-all. This leads to the following misconception.
Myth # 4: NPM scripts cannot run across platforms
Many projects are tied to a particular operating system, so cross-platform is not a big deal. However, NPM Scripts can still work well if you need to run them in a cross-platform manner. Countless open source projects are proof of this. Here’s how to do it.
The operating system command line will run your NPM scripts. Therefore, on Linux and OS X, NPM scripts runs on the Unix command line. On Windows, NPM scripts runs on the Windows command line. Thus, if you want your build scripts to run on all platforms, you need to adapt Unix and Windows. There are three implementations:
Method 1: Use cross-platform commands
There are many cross-platform commands available. Here are a few:
&& chain task (a task and then run a task) will < the file content in a command > will command output to file | will redirect output of a command to another commandCopy the code
Method 2: Use the Node package
You can use node packages instead of shell commands. For example, use rimraf instead of “rm-rf ‘”. Use cross-env to set environment variables in a cross-platform manner. Search Google, NPM, or libraries. IO for what you need, and there will almost always be a node package for your purpose in a cross-platform way. If the command line calls are too long, you can call the Node package in a separate script like this:
node scriptName.js
Copy the code
The above script is plain JavaScript and is run by Node. Since you are calling the script from the command line, you are not limited to.js files. You can run any script that the operating system can run, such as Bash, Python, Ruby, Powershell, etc.
Mode 3: UseShellJS
ShellJS is an NPM package that runs Unix commands through Node. This allows you to run Unix commands on all platforms, including Windows.
I use both methods 1 and 2 in React Slingshot.
The above section describes misconceptions about NPM Scripts and the power that NPM Scripts itself provides. With the infrastructure, NPM scripts, and commands provided by the operating system, NPM Scripts can be used to implement the functions provided by task runners like Gulp and Grunt in a much lighter way. Here are some pain points in NPM Scripts and how to deal with them.
Pain points
Obviously, there are some problems with using NPM scripts: annotations are not supported by the JSON specification, so you cannot add annotations to package. JSON. But there are ways around this limitation:
- Write small, well-named, single-purpose scripts
- Separate documents from scripts (such as in readme.md)
- Call a separate.js file
I prefer the first solution. If you break down each script so that it has a single responsibility, annotations become less important. The name of the script should fully describe its intent, just like any short, well-named function. As I said in Clean Code: Writing Code for Humans, short, single-responsibility functions rarely require comments. If I feel the need to add comments, I use the third option, which is to move the script into a separate file. This takes advantage of the power of JavaScript composition.
Package.json also does not support variables. This may seem like a big problem, but it isn’t, for two reasons. First, many times the variables we need involve the environment, which can be set on the command line. Second, if you need variables for other reasons, you can call separate.js files. Interested readers can take a look at the React-starter-Kit to see how this works.
Finally, there is the risk of using long, complex command-line arguments that are hard to understand. Code review and refactoring are a great way to ensure that NPM scripts stay small, well named, single-responsibility, and easy for everyone to understand. If the script is complex enough to require comments, you should either refactor a single script into multiple well-named scripts or extract it into separate files.
We need to prove that abstraction makes sense
Gulp and Grunt are abstractions of the tools I use. Abstraction is useful, but abstraction comes at a cost. It makes us overly dependent on plug-in maintainers and documentation, and as the number of plug-ins increases, they introduce complexity. I have decided not to use task runners like Gulp and Grunt anymore.
In fact, there are a number of developers out there who agree with me, like the following:
- Task Automation with NPM Run — James Holliday
- Advanced Front-end Automation with NPM Scripts — Kate Hudson
- How to use NPM as a build tool — Kieth Cirkel
- Introduction to NPM as a Build Tool — Marcus Hammarberg
- Gulp is awesome, but do we really need it? -? Gonto
- NPM Scripts for Build Tooling — Andrew Burgess
Cory’s post received a lot of feedback from developers, and people expressed their opinions. Here are some of the typical opinions:
Jason Trill says:
Another benefit is the standardization of Node-based projects. It would be nice to just run tasks through “NPM run”, even though these tasks are nothing more than wrappers for Gulp/Grunt.
Here’s Dwayne Crooks:
That’s great. I’ve been wondering lately whether I need to use Gulp in my workflow, and I’m using it less and less in my projects. This article has convinced me that Gulp and other build tools are completely unnecessary, thank you very much.
Vladimir Agafonkin:
We have a lot of JavaScript repositories on Mapbox, and they all use NPM scripts, not Gulp or Grunt. There’s nothing wrong with it, it’s easy to build, it’s easy to understand and manage.
Martin Olsen says:
I read this article a year ago
Blog. Keithcirkel. Co. UK/according to – we – shou…Then they started using NPM scripts instead of Gulp. I like the simplicity of NPM Scripts. Impartially, the only pain point is that you can’t add comments to the script, and you have to escape the double quotes.
Tim Wisniewski:
I did the same, and the point of view of the article happened to coincide with me.
Akshay Bist says:
You can run any script the operating system can run, not just node packages. So you can also run Python, bash scripts, and so on.
Cecil McGregor says:
Thank you very much. Although I have to use Grunt at work, I use NPM Scripts most of the time at home. Many of the plug-ins had problems, and I wasted a lot of time trying to figure out what the problems were.
Jess Hines says:
Thank you very much. In general, we think that abstraction makes things easier, but I find NPM Scripts to be friendly enough and very powerful. If necessary, I will try the following to deepen my understanding.
Adam Seldan says:
Totally agree with the article. I’ve been using NPM package.json scripts lately, especially for projects that use Node.js a lot and don’t require complex conversion chains at all. If that doesn’t feel right (it hasn’t yet, and you usually know the size of the project you’re working on ahead of time, and the steps to build it), then Webpack is a great way to introduce and learn, and it’s better than Grunt and Gulp in some ways.
Dylan J Harris:
Thank you As a new task runner, I’ve already encountered three of the problems mentioned in this article and hate this abstraction. I plan to use NPM Scripts directly for my next project. Great article.
Jason Karns says:
Using NPM Scripts directly gives us more configuration options; NPM exposes package.json objects as environment variables, prefixed with npM_package; NPM has well-defined configuration look-ups, so options can be defined in different places and overridden if necessary.
InfoQ Chinese readers, have you used Gulp, Grunt, etc in your projects? Have you used NPM Scripts directly? What do you think is the difference between the two? Can NPM Scripts completely replace Gulp and Grunt? Of course, Gulp and Grunt have a long history and have been used in many large projects; Its reliance on plug-ins has long been criticized, but plug-ins themselves are a big advantage. So if you’re developing a new project, do you use task runners like Gulp and Grunt or do you just use NPM scripts? Feel free to share your thoughts with other readers in the comments below.