Hvorfor forlod jeg Gulp og Grunt for npm-scripts

Jeg ved hvad du tænker. HVAD ?! Dræbte Gulp ikke bare Grunt? Hvorfor kan vi ikke bare være tilfredse i et par minutter her i JavaScript-land? Jeg hører dig, men ...

Jeg har fundet ud af, at Gulp og Grunt er unødvendige abstraktioner. npm-scripts er meget kraftfulde og ofte lettere at leve med.

Lad os begynde med et eksempel ...

Jeg var en stor fan af Gulp. Men på mit sidste projekt endte jeg med 100 linjer i min gulpfile og omkring et dusin Gulp-plugins. Jeg kæmpede for at integrere Webpack, Browsersync, hot reloading, Mocha og meget mere ved hjælp af Gulp. Hvorfor? Nogle plugins havde ikke tilstrækkelig dokumentation for min brugssag. Nogle plugins udsatte kun en del af det API, jeg havde brug for. Den ene havde en underlig fejl, hvor den kun ville se et lille antal filer. En anden strippede farver, når de blev sendt til kommandolinjen.

Disse er løse problemer, men ingen af ​​disse problemer opstod, da jeg ringede direkte til værktøjerne.

For nylig har jeg bemærket, at mange open source-projekter simpelthen bruger npm-scripts. Jeg besluttede at træde tilbage og undersøge igen. Har jeg virkelig brug for Gulp? Det viser sig, at jeg ikke gjorde det.

Jeg besluttede at prøve at bruge bare npm-scripts på mit nye open source-projekt. Jeg oprettede et rigt dev-miljø og byggeproces til React-applikationer ved hjælp af kun npm-scripts. Nysgerrig, hvordan det ser ud? Tjek React Slingshot. Jeg gennemgår, hvordan jeg opretter denne byggeproces ved hjælp af npm-scripts i "Building a JavaScript Development Environment" på Pluralsight.

Det overraskende er, at jeg nu foretrækker at arbejde med npm-scripts frem for Gulp. Her er hvorfor.

Hvad er der galt med Gulp and Grunt?

Over tid har jeg bemærket tre centrale problemer med opgaveløbere som Gulp og Grunt:

  1. Afhængighed af pluginforfattere
  2. Frustrerende fejlretning
  3. Adskilt dokumentation

Lad os overveje hvert af disse spørgsmål.

Udgave nr. 1: Afhængighed af pluginforfattere

Når du arbejder med nye eller upopulære teknologier, findes der muligvis ikke noget plugin. Og når der findes et plugin, kan det være forældet. For eksempel blev Babel 6 for nylig frigivet. API'et ændrede sig markant, så mange Gulp-plugins var uforenelige med den nyeste version. Da jeg brugte Gulp, sad jeg fast, fordi det Gulp-plugin, jeg havde brug for, ikke var opdateret endnu.

Med Gulp eller Grunt skal du vente på, at plugin-vedligeholdere leverer opdateringer eller retter det selv. Dette forsinker din evne til at bruge nye versioner af moderne værktøjer. I modsætning hertil, når jeg bruger npm-scripts, bruger jeg værktøjer direkte uden et ekstra abstraktionslag . Dette betyder, at når nye versioner af Mocha, Istanbul, Babel, Webpack, Browserify og så videre frigives, er jeg i stand til at bruge de nye versioner med det samme.

Med hensyn til valg slår intet npm:

Når du bruger npm-scripts, søger du ikke efter et Grunt- eller Gulp-plugin. Du vælger mellem over 227.000 npm-pakker.

For at være retfærdig, hvis det nødvendige Grunt- eller Gulp-plugin ikke er tilgængeligt, kan du helt sikkert bruge npm-pakker direkte. Men så udnytter du ikke længere Gulp eller Grunt til den specifikke opgave.

Udgave nr. 2: Frustrerende fejlretning

Da integrationer mislykkes, kan fejlfinding i Grunt og Gulp være frustrerende. Da du arbejder med et ekstra abstraktionslag, er der flere potentielle årsager til enhver fejl:

  1. Er basisværktøjet brudt?
  2. Er Grunt / Gulp-pluginet brudt?
  3. Er min konfiguration brudt?
  4. Bruger jeg inkompatible versioner?

Brug af npm-scripts eliminerer nr. 2. Og jeg finder # 3 er langt mindre almindeligt, da jeg typisk kalder værktøjets kommandolinjegrænseflade direkte. Endelig er nr. 4 mindre almindelig, da jeg har reduceret antallet af pakker i mit projekt ved at bruge npm direkte i stedet for at bruge en task runner's abstraktion.

Udgave nr. 3: Adskilt dokumentation

Dokumentationen for de kerneværktøjer, jeg har brug for, er næsten altid bedre end de tilknyttede Grunt- og Gulp-plugins. For eksempel, hvis jeg bruger gulp-eslint, ender jeg med at opdele min tid mellem gulp-eslint docs og ESLint-webstedet. Jeg er nødt til at skifte kontekst mellem pluginet og det værktøj, det abstraherer. Kernen i friktion i Gulp og Grunt er dette:

At forstå værktøjet er ikke nok. Gulp og Grunt kræver, at du forstår plugins abstraktion.

De fleste bygningsrelaterede værktøjer tilbyder klare, kraftfulde og veldokumenterede kommandolinjegrænseflader. Se dokumenterne på ESLints CLI som et godt eksempel. Jeg finder læsning og implementering af et kort kommandolinjekald i npm-scripts klarere, lavere friktion og lettere at debugge (da der er fjernet et lag med abstraktion).

Nu hvor jeg har etableret smertepunkterne, er spørgsmålet, hvorfor tror vi, at vi har brug for opgaveløbere som Gulp og Grunt?

Hvorfor har vi ignoreret npm for builds?

Jeg tror, ​​at der er fire centrale misforståelser, der førte til, at Gulp og Grunt blev så populære:

  1. Folk tror, ​​at npm-scripts kræver stærke kommandolinjefærdigheder
  2. Folk tror, ​​at npm-scripts ikke er stærke nok
  3. Folk tror, ​​at Gulp's streams er nødvendige for hurtige builds
  4. Folk tror, ​​at npm-scripts ikke kører på tværs af platforme

Lad os løse disse misforståelser i rækkefølge.

Misforståelse nr. 1 : npm-scripts kræver stærke kommandolinjefærdigheder

Du behøver ikke vide meget om dit operativsystems kommandolinje for at nyde kraften i npm-scripts. Sikker på, grep, sed, awk og rør er livslange færdigheder, der er værd at lære, men du behøver ikke være en Unix- eller Windows-kommandolinjewizard for at bruge npm-scripts . Du kan udnytte 1000'erne af pakker i npm for at få arbejdet gjort i stedet.

For eksempel ved du muligvis ikke, at dette i Unix sletter en mappe med kraft: rm -rf. Det er okay. Du kan bruge rimraf, som gør det samme (og det fungerer på tværs af platforme for at starte). De fleste npm-pakker tilbyder grænseflader, der antager meget lidt kendskab til dit operativsystems kommandolinje. Bare søg npm efter pakker, der gør, hvad du har brug for, læs dokumenterne, lær, mens du går. Jeg søgte efter Gulp-plugins. Nu søger jeg efter npm-pakker i stedet. En stor ressource: libraries.io.

Misforståelse nr. 2: npm-scripts er ikke kraftige nok

npm-scripts er overraskende kraftfulde alene. Der er konvent-baserede præ- og post-kroge:

 { "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" } }

Alt du gør er at følge konventionen. Ovenstående scripter kører i rækkefølge baseret på deres præfiks. Det forudbyggede script kører før build-scriptet, fordi det har samme navn, men er foran "pre". Postbuild-scriptet kører efter build-scriptet, fordi det har præfikset "post". Så hvis jeg opretter scripts med navnet pre-build, build og postbuild, kører de automatisk i den rækkefølge, når jeg skriver 'npm run build'.

Du kan også nedbryde store problemer ved at ringe til et script fra et andet:

{ "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" } }

I dette eksempel kalder den forudbyggede opgave den rene opgave. Dette giver dig mulighed for at nedbryde dine scripts i små, velnavn, enkelt ansvar, en-liners.

Du kan kalde flere scripts serielt på en enkelt linje ved hjælp af &&. Scriptene i det rene trin ovenfor kører den ene efter den anden. Denne enkelhed får dig virkelig til at smile, hvis du er en person, der har kæmpet for at få en liste over opgaver, der skal køres i orden i Gulp.

Og hvis en kommando bliver for kompliceret, kan du altid ringe til en separat fil:

{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "build": "node build.js" } }

Jeg kalder et separat script i buildopgaven ovenfor. Dette script køres af Node og kan således bruge de npm-pakker, jeg har brug for, og udnytte al JavaScript-kraft indeni.

Jeg kunne fortsætte, men kernefunktionerne er dokumenteret her. Der er også et kort Pluralsight-kursus om brug af npm som et byggeværktøj. Eller tjek React Slingshot for et eksempel på alt dette i aktion.

Misforståelse nr. 3: Gulp's Streams er nødvendige for hurtige bygninger

Gulp fik hurtigt trækkraft over Grunt, fordi Gulp's in-memory-streams er hurtigere end Grunt's filbaserede tilgang. Men du behøver ikke Gulp for at nyde kraften i streaming. Faktisk er streaming altid blevet indbygget i både Unix og Windows kommandolinjer . Røroperatøren (|) streamer output fra en kommando til input af en anden kommando. Og omdirigeringsoperatøren (>) omdirigerer output til en fil.

Så for eksempel kan jeg i Unix bruge 'grep' indholdet af en fil og omdirigere output til en ny fil:

grep ‘Cory House’ bigFile.txt > linesThatHaveMyName.txt

Arbejdet ovenfor streames. Der skrives ingen mellemfiler. (Undrer du dig over, hvordan du udfører kommandoen ovenfor på tværs af platforme? Læs videre ...)

Du kan også bruge operatøren `&` til at køre to kommandoer på samme tid på Unix:

npm run script1.js & npm run script2.js

De to scripts ovenfor kører på samme tid. Hvis du vil køre scripts samtidigt på tværs af platforme, skal du bruge npm-run-all. Dette fører til den næste misforståelse ...

Misforståelse nr. 4: npm-scripts kører ikke på tværs af platforme

Mange projekter er bundet til et bestemt operativsystem, så bekymringer på tværs af platforme betyder ikke noget. Men hvis du har brug for at køre på tværs af platforme, kan npm-scripts stadig fungere godt. Utallige open source-projekter er bevis. Sådan gør du.

Dit operativsystems kommandolinje kører dine npm-scripts. Så på Linux og OSX kører dine npm-scripts på en Unix-kommandolinje. På Windows kører npm-scripts på Windows-kommandolinjen. Således, hvis du vil have dine build-scripts til at køre på alle platforme, skal du gøre både Unix og Windows lykkelige. Her er tre tilgange:

Approach 1: Use commands that run cross-platform. There’s a surprising number of cross-platform commands. Here’s a few:

&& chain tasks (Run one task after another)  redirect command output to a file | redirect command output to another command

Approach 2: Use node packages. You can use node packages instead of shell commands. For instance, use rimraf instead of `rm -rf`. Use cross-env to set environment variables in a cross-platform way. Search Google, npm or libraries.io for what you want to do and there is almost certainly a node package that will get it done cross-platform. And if your command line calls get too long, you can call Node packages in separate scripts as well like this:

node scriptName.js

The script above is plain old JavaScript, run by Node. And since you’re just calling a script on the command line, you’re not limited to .js files. You can run any script that your OS can execute such as Bash, Python, Ruby, or Powershell as well.

Approach 3: Use ShellJS. ShellJS is an npm package that runs Unix commands via Node. So this gives you the power to run Unix commands on all platforms, including Windows.

I used a combination of approach #1 and #2 on React Slingshot.

Pain Point

There are admittedly some downsides: The JSON spec doesn’t support comments, so you can’t add comments in package.json. There are a few ways to work around this limitation:

  1. Write small, well-named, single purpose scripts
  2. Document scripts separately (in a README.md for instance)
  3. Call a separate .js file

I prefer option #1. If you break each script down to have a single responsibility, comments are rarely necessary. The script’s name should fully describe the intent, just like any small well-named function. As I discuss in “Clean Code: Writing Code for Humans”, small single responsibility functions rarely require comments. When I feel a comment is necessary, I use option #3 and move the script to a separate file. This gives me all the compositional power of JavaScript when I need it.

Package.json also doesn’t support variables. This sounds like a big deal, but it’s not for two reasons. First, the most common need for variables revolves around environment, which you can set on the command line. Second, if you need variables for other reasons, you can call a separate .js file. Check out React-starter-kit for an elegant example of this pattern.

Finally, there’s also a risk of creating long, complex command line arguments that are difficult to understand. Code reviews and diligent refactoring are a great way to assure npm scripts are decomposed into small, well-named, single purpose functions that everyone understands. If it’s complex enough to need a comment, you should likely refactor the single script into multiple well named scripts or extract it to a separate file.

Abstractions Must Be Justified

Gulp and Grunt are abstractions over the tools I use. Abstractions are useful, but abstractions have a cost. They leak. They make us dependent upon the plugin maintainers and their documentation. And they add complexity by increasing the number of dependencies. I’ve decided task runners like Gulp and Grunt are abstractions I no longer need.

Looking for details? I walk through how to create a build process using npm scripts from scratch in “Building a JavaScript Development Environment” on Pluralsight.

Comments? Chime in below or on Reddit or Hacker News.

Finally, I’m far from the first person to suggest this. Here are some excellent links:

  • 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 er fantastisk, men har vi virkelig brug for det? - Gonto
  • NPM-scripts til buildværktøj - Andrew Burgess

Cory House er forfatter til "React and Redux in ES6", "Clean Code: Writing Code for Humans" og flere andre kurser om pluralsight. Han er softwarearkitekt hos VinSolutions og uddanner softwareudviklere internationalt i softwarepraksis som front-end-udvikling og ren kodning. Cory er Microsoft MVP, Telerik Developer Expert og grundlægger af outlierdeveloper.com.