Zero-dependency CLIs with Node.js
The contents here is created from the official CascadiaJS Page
Overview
- name: Ian Sutherland
- Speaker Page
Abstract
Node.js is a popular choice for building dev tools and some recently added features make it possible to build powerful tools without any external dependencies. We’ll look at these new features and show how to use them to build a custom CLI app with zero external dependencies.
Notes
From Aminamos
Go to text →
aminamos
Notes by- topics
- what is a cli
- what are dependencies, why avoid them
- what helps us write cli apps
- new upcoming nodejs features
- CLIs use flags/options
- why build with node?
- usually a language your team is already familiar with
- JSON data operations, network requests are pretty well supported
- node has a package ecosystem (useful when needed, still avoid when possible)
- dependencies
- code that your program needs to run
- all users of your app will need to install dependencies
- smaller apps, like CLIs, not having an install or build step makes setup, contributing, and distribution easier
- new node syntax
- import instead of require
- ESM instead of CJS
- argument parsing
- this process is surprisingly complicated
- https://nodejs.org/docs/latest/api/process.html#processargv
- third party library like yargs or commander, in the past
- parseArgs is ~the future~ as of node 18.3 (potentially backported to node 16)
- polyfill proposal for util.parseArgs() - https://www.npmjs.com/package/@pkgjs/parseargs
- doesn't support all previous functionality, but useful error messages
- https://nodejs.org/api/util.html#utilparseargsconfig
- fetch
- before fetch, had to do a lot of pre-work to setup, run, and handle http requests
- with fetch you can just
await fetch()
and return the body/errors git blast
is real https://github.com/iansu/git-blast
- test runner
- recursive file system operations
- wild speculation
- self contained executibles
- glob
- typescript
node index.ts
- what's next, ways to contribute
- join node tooling group https://github.com/nodejs/tooling
From Hunt
Go to text →
- Neo Financial, Head of DEX and OSS
- twitter @iansu (Private)
- flags
-l1
or-l -1
Short options--color
Long options
- subcommands
- the
status
ingit status
- the
- "CLI is serious 80s vibes"
- Node has a small core which is why there are lots of dependencies installed with
npm
for a given project likeisNumber
. - Bundling isn't the best because it can still result in a large bundle size.
type: module
to package.json to useimport
and ESMM or us.mjs
(as opposed to.cjs
).- import Node.js internals with
node:
- eg,
import fs from 'node:fs'
- eg,
- There are many different ways to set arguments in a cli
- parseArgs:
process.argv
-> Tedious so useyargs
ofcommander
.- NOT TEDIOUS ANYMORE!
parseArgs from 'node:util'
usesprocess.argsv
- declare the options (kind of like commander)
- has polyfill, will be "backboarded" to v16
- NOT TEDIOUS ANYMORE!
- fetch: now available in node@18
- Built on top of Undici (HTTP/1.1)
- see
iansu/git-blast
on github to run git blast and see people's twitter handle's if supplied on githu - node@18 now has a basic test runner.
- don't necessarily need jest anymore.
node --test
- recursive mkdir function with
{recursive: true}
- recursive rm operation
{recursive: true, force: true}
- recursive cp (same as mkdir) **experimental
- readdir **open pr, not merged
- Speculation of what's next
- glob patterns
- self contained executables
- native typescript (aka deno)
From Kevinslin
Go to text →
kevin
Notes by-
cli apps
- stuff that runs on the terminal
-
features
- options (eg.
ls
) - subcommands (eg.
git
)
- options (eg.
-
why node
- scripting language
- good at json, parallel task, network request, etc
- lots of packages
-
dependencies
- other code (eg. library)
- node does not have big standard lib
-
are deps bad?
- depends
- does introduce overhead
- you can bundle to make it better
-
new syntax
- using
import
(es module) - import nodejs internals with
node:
prefiximport fs from "node:fs"
- every npm name is taken, we couldn't add anything new, hence new namepsace
- using
-
argument parsing
- old way:
// get everything from STDIN, split by whitespace // eg. [node, mycli, --ls] const args = process.argv
- old way:
-
argument parsing
import {parseArgs} from "node:util" const input = process.argv.slice(2) const options = { silent: { type: 'boolean', short: 's' } } const {values} = parseArgs({input, options}):
-
fetch
- make http request
- its available in node 18
- built on top of undici (http/1.1 client)
- has web streams api
// available globally const response = await fetch("...") const body = await response.json() // you get JSON { ... }
-
demo to add new git subcommand the old way
// very long example // ...
-
demo with new way
// very short example (less than 10 lines) // ...
-
test runner
- basic api to do this
- eg. mocha but builtin to node
import test from "..." test("synchornous test", (t) => { //... }) // also works async
-
recursive file system ops
import {rm} from "node:fs/promises" // experimental import {cp} from "node:fs/promises" // doesn't exist yet, open pr import {readdir} from "node:fs/promises"
-
whats next
DISCLAIMER: complete speculation
- glob?
- self contained executable
- typescript (lol - i wish)
Children
Tags
Backlinks