loading...

yargs v16 released

bcoe profile image Benjamin E. Coe ・3 min read

About 5 months ago, Mael Le Guen proposed converting the yargs codebase to TypeScript. I was a bit skeptical at first...

I saw benefits TypeScript could offer the project:

  • yargs has a large API surface with many implicit contracts, e.g., how calling a method like .option() eventually feeds into yargs-parser.
  • some parts of the codebase are quite messy, and type safety would give additional confidence to future refactors.

A few concerns were motivating my skepticism:

  • I knew this would be a large project, and was worried we'd deliver something that was only halfway there.
  • The existing @types/yargs TypeScript definitions have >12,000,000 downloads/week, I didn't want to disrupt this community.
  • I didn't want to significantly bloat the size of yargs.

Thanks to the hard work of Mael, who lead the conversion project (with help from others like QmarkC), and thanks to a few compromises, I'm happy to say that the TypeScript conversion project was successful.

Beyond the benefits I expected (ease of refactoring, and explicit interfaces), TypeScript made two additional improvements to yargs easier, which I'm excited to announce in v16:

  • yargs now supports both ESM and CommonJS (you can even use yargs directly in the browser, without bundling).
  • yargs now has experimental support for Deno.

yargs is now written in TypeScript, but...

As the TypeScript project approached completion, it became clear that it would be significant amount of work to match the type definitions exposed in @types/yargs...

We've made the decision to not ship yargs with Type declaration files. As a TypeScript user of yargs, usage instructions shouldn't change, and you will still npm i @types/yargs --save-dev.

Developers working on the yargs project now benefit from type safety, and the project benefits from the build steps we've introduced (more on this in a moment), but the existing TypeScript community should be able to upgrade with minimal disruption.

ESM and CJS support

yargs@v16 is ~26% larger than past version of yargs. This is because, using Node.js' conditional exports, yargs now provides an interface for both CommonJS and ESM:

CommonJS example:

const {argv} = require('yargs')

if (argv.ships > 3 && argv.distance < 53.5) {
  console.log('Plunder more riffiwobbles!')
} else {
  console.log('Retreat from the xupptumblers!')
}

ESM example:

import yargs from 'yargs'
import { hideBin } from 'yargs/helpers'

yargs(hideBin(process.argv))
  .command('curl <url>', 'fetch the contents of the URL', () => {}, (argv) => {
    console.info(argv)
  })
  .demandCommand(1)
  .argv

To facilitate this, we target ESM with the TypeScript compilation step, then have an additional compile step with Rollup which creates a CommonJS bundle of the library.

It's my hope that taking this approach of shipping a dual mode library will help smooth the process for folks experimenting with ESM (and that the additional bloat in the library will be forgiven 😊)

Deno Support

Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.

Taking on the work to make yargs and its dependencies fully support ESM, combined with converting the codebase to TypeScript, made it a short addition step towards supporting the Deno runtime.

Deno example:

import yargs from 'https://deno.land/x/yargs/deno.ts'
import { Arguments, YargsType } from 'https://deno.land/x/yargs/types.ts'

yargs()
  .command('download <files...>', 'download a list of files', (yargs: YargsType) => {
    return yargs.positional('files', {
      describe: 'a list of files to do something with'
    })
  }, (argv: Arguments) => {
    console.info(argv)
  })
  .strictCommands()
  .demandCommand(1)
  .parse(Deno.args)

I am a Deno novice, and would characterize yargs' support of the platform as experimental. However, I'm excited to see folks adopt the functionality, and will happily fix bugs for the platform as they arise.

Significant Breaking Changes

  • the use of Conditional exports makes yargs' exported files explicit. Folks who were requiring deep files, e.g., lib/utils/obj-filter.js, will not be able to do so. The helpers yargs exposes have been defined explicitly.
  • the rebase helper method has been removed from yargs (this was just wrapping path.relative).
  • Node 8 support has been dropped.

Other changes are listed in the CHANGELOG.

Related:

Posted on by:

bcoe profile

Benjamin E. Coe

@bcoe

Ben works on the open-source libraries yargs, nyc, and c8, and is a core collaborator on Node.js. He works on the JavaScript client libraries at Google.

Discussion

markdown guide