Code editors come with "beautifiers" which programmers can use to make code uniformly and conventionally laid out, aka: "pretty".
But sometimes you have a snippet of code, maybe copied from the web, or maybe a packet of JSON curled on the command line, and you want to quickly format it.
Here is a way of using gulp and prettier to build a cute project to automate this common task. This will be a fun utility you can run in the background; and for those looking to learn gulp, it makes for a neat primer for a less common use case.
npm i gulp -g
curl
or http
on a command line. gulp is a NodeJS-based task runner. grunt is another well known library built for the same purpose, but in this article we'll use gulp because I like it better. In my opinion, gulp is more like code; grunt is more like configuration. Either are good. I like gulping better than I like grunting. If you want to grunt, find a grunter. I'm a gulper. Unless it's beer. Then I'm a sipper.
gulp uses a concept called a "pipe". A pipe is nothing more than a chain of functions where each transforms the input in some way before passing on the output of each stage to the next function. Metaphorically, this would be a valid gulp task:
function gulpPotatoesTask() {
return src(potatoes)
.pipe(wash())
.pipe(peel())
.pipe(slice())
.pipe(season())
.pipe(fry())
.pipe(drain())
.pipe(cool())
.pipe(bag())
.pipe(dest(shop))
}
Obviously gulp can't make potato chips (or "crisps" as we say in the UK); but it can perform many useful computer tasks. gulp itself doesn't have any task functions, all it does is pipe content from one function to another. The actual functions are imported from gulp based libraries; some of them gulp friendly versions of well known npm libraries. There are lots of them! You can even write your own.
We're going to look at a simple use case.
When I'm developing a REST API, I'm often curling the endpoint to verify the output. On the command line, the output is not pretty, so it makes it hard to verify whether a large JSON packet... especially a list of records... is error free. I'd usually to copy and paste the result into my code editor and run a "beautify" command. That's a simple task, which takes a few seconds, which I have to do a lot! It's a perfect candidate for a gulp task.
Let me show you what I mean with help from cat-facts.
On a command line, using curl or http or something else you like, run this:
curl -X GET https://cat-fact.herokuapp.com/facts
You'll see some output like this:
[{"status":{"verified":true,"sentCount":1,"feedback":""},"type":"cat","deleted":false,"_id":"5887e1d85c873e0011036889","user":"5a9ac18c7478810ea6c06381","text":"Cats make about 100 different sounds. Dogs make only about 10.","__v":0,"source":"user","updatedAt":"2020-09-03T16:39:39.578Z","createdAt":"2018-01-15T21:20:00.003Z","used":true},{"status":{"verified":true,"sentCount":1},"type":"cat","deleted":false,"_id":"588e746706ac2b00110e59ff","user":"588e6e8806ac2b00110e59c3","text":"Domestic cats spend about 70 percent of the day sleeping and 15 percent of the day grooming.","__v":0,"source":"user","updatedAt":"2020-08-26T20:20:02.359Z","createdAt":"2018-01-14T21:20:02.750Z","used":true},{"status":{"verified":true,"sentCount":1},"type":"cat","deleted":false,"_id":"58923f2fc3878c0011784c79","user":"5887e9f65c873e001103688d","text":"I don't know anything about cats.","__v":0,"source":"user","updatedAt":"2020-08-23T20:20:01.611Z","createdAt":"2018-02-25T21:20:03.060Z","used":false},{"status":{"verified":true,"sentCount":1},"type":"cat","deleted":false,"_id":"5894af975cdc7400113ef7f9","user":"5a9ac18c7478810ea6c06381","text":"The technical term for a cat’s hairball is a bezoar.","__v":0,"source":"user","updatedAt":"2020-11-25T21:20:03.895Z","createdAt":"2018-02-27T21:20:02.854Z","used":true},{"status":{"verified":true,"sentCount":1},"type":"cat","deleted":false,"_id":"58e007cc0aac31001185ecf5","user":"58e007480aac31001185ecef","text":"Cats are the most popular pet in the United States: There are 88 million pet cats and 74 million dogs.","__v":0,"source":"user","updatedAt":"2020-08-23T20:20:01.611Z","createdAt":"2018-03-01T21:20:02.713Z","used":false}]
...which ain't pretty!
We're going to write a gulp task which can be used to immediately view a prettier version of this (and other types of) output.
Prettier formats types of code, like Markdown, HTML, Javascript, JSON, php, SASS, JSX, and a few others. By format or "prettier" we mean making Yuck
[{x:0.8237408992028434,y:0.13037233515888857},{x:0.6963719435837943,y:0.4649206270118649},{x:0.7648934156460141,y:0.5717398107678364},{x:0.9959169251265363,y:0.2694142052523685},{x:0.6960740127788656,y:0.4607191696858737},{x:0.7997975402971844,y:0.7237427911168932},{x:0.2787610912100449,y:0.9407814810049839},{x:0.3814844890207585,y:0.5966765925995574},{x:0.8582462233630488,y:0.40239228496424784},{x:0.7787669281973615,y:0.08106311625800644},{x:0.2141057128647048,y:0.9657071629620191},{x:0.8275684202905218,y:0.5451389175963794},{x:0.165628605615336,y:0.14122051271223968},{x:0.20818796522948935,y:0.7404504627391517},{x:0.9674488899456264,y:0.20797270494407227},{x:0.4749080813580391,y:0.5771024073819466},{x:0.8843075075690756,y:0.6923906507976978},{x:0.3726781663836376,y:0.09623360192541552},{x:0.9597557422088943,y:0.7070691995654992},{x:0.6074224539058268,y:0.9773926533307942}]
look Yummy
[
{ x: 0.8237408992028434, y: 0.13037233515888857 },
{ x: 0.6963719435837943, y: 0.4649206270118649 },
{ x: 0.7648934156460141, y: 0.5717398107678364 },
{ x: 0.9959169251265363, y: 0.2694142052523685 },
{ x: 0.6960740127788656, y: 0.4607191696858737 },
{ x: 0.7997975402971844, y: 0.7237427911168932 },
{ x: 0.2787610912100449, y: 0.9407814810049839 },
{ x: 0.3814844890207585, y: 0.5966765925995574 },
{ x: 0.8582462233630488, y: 0.40239228496424784 },
{ x: 0.7787669281973615, y: 0.08106311625800644 },
{ x: 0.2141057128647048, y: 0.9657071629620191 },
{ x: 0.8275684202905218, y: 0.5451389175963794 },
{ x: 0.165628605615336, y: 0.14122051271223968 },
{ x: 0.20818796522948935, y: 0.7404504627391517 },
{ x: 0.9674488899456264, y: 0.20797270494407227 },
{ x: 0.4749080813580391, y: 0.5771024073819466 },
{ x: 0.8843075075690756, y: 0.6923906507976978 },
{ x: 0.3726781663836376, y: 0.09623360192541552 },
{ x: 0.9597557422088943, y: 0.7070691995654992 },
{ x: 0.6074224539058268, y: 0.9773926533307942 }
]
So let's start making pretty from ugly; yummy from yuck; unreadable to ... well... you get the point.
Start by creating a new folder for the project and initialize a npm package:
mkdir pretty-ugly
cd pretty-ugly
npm init -y
At this stage you'll have a folder with a
package.json
file - and that's it. Next we'll install some npm packages. npm i del gulp gulp-prettier prettier vinyl-paths
When the install is finished, run
gulp
gulp
[17:40:34] No gulpfile found
No gulpfile found!? Good point! So create
gulpfile.js
with your favourite text editor and put it in the root of your new project. Copy the following into the file and save it./pretty-ugly/gulpfile.js
const { src, dest, watch } = require("gulp")
const prettier = require("gulp-prettier"),
del = require("del"),
vinylPaths = require("vinyl-paths")
let prettyUglyFiles = ["./pretty/ugly/*.*"]
let prettyUglyTask = () => {
return src(prettyUglyFiles)
.pipe(vinylPaths(del))
.pipe(prettier())
.pipe(dest("./pretty/"))
}
let prettyUgly = () => {
return watch(prettyUglyFiles, prettyUglyTask)
}
exports.default = prettyUgly
For those of you who know gulp, this will be familiar. For those who don't know gulp, I will break it down. If you are a grunt user it will be useful for you to compare the approach.
First we import all the libraries we need from gulp:
src
, dest
, and watch
are part of gulp's core API; functions which form the backbone of gulp tasks. src
gets gulp to retrieve one or more source files/folders.dest
gets gulp to "pipe" the result of some transformation of those files into a destination folder.watch
gets gulp to (duh!) watch for changes in the src which can be used to trigger a new transformation of them which is piped into the destination folder.Next we import some of the gulp based transforming functions from other npm libraries.
const prettier = require("gulp-prettier"),
del = require("del"),
vinylPaths = require("vinyl-paths")
The "pipe" in
prettyUglyTask
is simple. let prettyUglyFiles = ["./pretty/ugly/*.*"]
let prettyUglyTask = () => {
return src(prettyUglyFiles)
.pipe(vinylPaths(del))
.pipe(prettier())
.pipe(dest("./pretty/"))
}
prettyUglyFiles
vinyl-paths
does - think of it as a marshal which deletes the files while keeping them in the pipe.let prettyUgly = () => {
return watch(prettyUglyFiles, prettyUglyTask)
}
The
prettyUgly
task is simple. We ask gulp to watch the source folder and if anything changes, we ask it to perform Task 1: prettyUglyTask. Watch handles the piping of the changed files into the prettyUglyTask
function.exports.default = prettyUgly
It is Task 2,
prettyUgly,
which we export. When we run gulp
, it will run Task 2, which does nothing until the files in the source directory change. The folder structure we're using is purely to humour me. We put the "ugly" files into the ugly folder and gulp puts the prettier versions into the pretty folder. You'll need these folders so make them:
mkdir -p pretty/ugly
You could use a different folder structure, i.e. making pretty and ugly siblings, but I like the nomenclature which implies the action, e.g. "[make] pretty [of that which is] ugly". I like that the files I want to see are in the parent folder, and the files I never want to see (the ugly ones) are buried. Small things please little minds.
At this point your project will look like this:
pretty-ugly
|- node_modules
|- pretty
| - ugly
|- gulpfile.js
|- package-lock.json
|- package.json
Now open up a different terminal and type the
gulp
command:gulp
[20:54:25] Using gulpfile ~/Dev/timitee/pretty-ugly/gulpfile.js
[20:54:25] Starting 'default'...
In your original terminal (and with an eye on your second terminal), type this:
curl -X GET "https://cat-fact.herokuapp.com/facts" --output ~/Dev/timitee/pretty-ugly/pretty/ugly/catfacts.json
You'll notice that gulp wakes up and prints:
[20:56:04] Starting 'prettyUglyTask'...
[20:56:04] Finished 'prettyUglyTask' after 164 ms
[20:56:05] Starting 'prettyUglyTask'...
[20:56:05] Finished 'prettyUglyTask' after 9.68 ms
Even though we
--output
our "cat facts" into the pretty/ugly
folder, you'll open the catfacts.json
from the pretty
folder. With this gulp task running, you can put (or output) any file prettier recognizes (see https://prettier.io/) into the
ugly
folder, and it will format the file and move the pretty version into the (duh!) pretty
folder. And that's pretty-ugly!
I hope this leaves you with:
Core, Thanks!