This short tutorial demonstrates building command-line tools in TypeScript, non-blocking operations with async/await, continuous integration with Mocha automated testing, and Travis – CI.

Intro

Recently TJ released Node-Prune to clean up the redundant files in node_modules, but the project was written in Go, so I ported a JavaScript version. You can continue to read the article with the source code. Projects are built in TypeScript and automatically translated to JavaScript when published by NPM, so feel free to use the latest syntax and type detection such as async/await. It also uses TS-Node to debug directly in TypeScript. Project code is small, suitable as a template for similar gadgets ~

TypeScript Setup

The final structure of the project is as follows. The source code is placed in the SRC/directory, and finally transcoded to lib/ and published to NPM. The test code is in the test/ directory.

.

├ ─ ─ SRC /

├ ─ ─ the test /

├ ─ ─ lib /

├ ─ ─ node_modules /

├ ─ ─ LICENSE

├ ─ ─ the README, md

├ ─ ─ package - lock. Json

├ ─ ─ package. Json

└ ─ ─ tsconfig. JsonCopy the code

First use NPM init to initialize the project and install TypeScript

npm i typescript -DCopy the code

After entering, open package.json and add:

{
    "scripts": {
        "build": "tsc",
        "dev": "tsc -w",
        "prepare": "npm run build",
    }
},Copy the code

TSC is a TypeScript translation command. The -w parameter is used to monitor source code changes and prepare is executed before NPM install and NPM publish to ensure that the latest translated code is published.

To tell TSC how to translate, you need a configuration file, tsconfig.json, that generates a default template from TSC –init on the command line. To support node6.x, 7.x, change the “target”: “ES2015” for the Node project. Module generation requires “module”: “commonJS” and then specifies the ts files included below.

{
  "compilerOptions": {
    "target": "ES2015"."module": "commonjs"."outDir": "./lib"."strict": true
  },
  "exclude": [
    "node_modules"."lib"]."include": ["src/**/*.ts"]}Copy the code

Now we can have fun writing TS code. Try writing a function for all files and folders in walk.

import * as fs from 'fs-extra';
import * as path from 'path';

export async function walk(dir: string, prunerF: (p: string, s: fs.Stats) => void): Promise<void> {
  let s = await fs.lstat(dir);
  if (!s.isDirectory()) return;

  const items = await fs.readdir(dir);
  for (let item of items) {
    const itemPath = path.join(dir, item);
    const s = await fs.lstat(itemPath);
    const pruned = await prunerF(itemPath, s);
    if (!pruned && s.isDirectory()) {
      await walk(itemPath, prunerF);
    }
  }
}Copy the code

Note here that we can use async/await directly and type the incoming parameters, and if you use an editor with plug-in support, you will have a pleasant experience with intelligent completion.

Async is implemented by defining an __awaiter function. If you are interested, you can explore it by yourself.

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise(a))function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch(e) { reject(e); }}function rejected(value) { try { step(generator["throw"](value)); } catch(e) { reject(e); }}function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};Copy the code

CLI

You can run a script from Node file.js, but how do you install an NPM executable, as described by npmjs.com, in package.json

{
    "bin": {
        "prune": "lib/cli.js"}}Copy the code

You can then install a Prune into your system path and add #! To the top of the file you execute. /usr/bin/env node, otherwise it is not recognized as a node script.

To read arguments passed from the command line, you can use args, this code in SRC /cli.ts


const argv = yargs
  .usage('Prune node_modules files and dependencies\n\nUsage: node-prune <path>')
  .option('config', {
    alias: 'c',
    description: '<filename> config file name'.default: '.prune.json'.type: 'string'
  })
  .option('dryrun', {
    alias: 'd',
    description: 'dry run'.default: 'false'.type: 'boolean'
  })
  .option('verbose', {
    description: 'log pruned file info'.default: 'false'.type: 'boolean'
  })
  .help('help').alias('help'.'h')
  .version('version'.'0.1.0 from').alias('version'.'v')
  .argv;

const path = argv._[0] | |'node_modules';
const configs = {
  config: argv.config,
  dryrun: argv.dryrun,
  verbose: argv.verbose
};Copy the code

It can produce something like this:

$ prune -h
Prune node_modules files and dependencies

Usage: node-prune <path>

Options:
  --config, -c   <filename> config file name   [string] [default: ".prune.json"]
  --dryrun, -d   dry run                            [boolean] [default: "false"]
  --verbose      log pruned file info               [boolean] [default: "false"]
  --help, -h     Show help                                             [boolean]
  --version, -v  Show version number                                   [boolean]Copy the code

You can import the business code you wrote to perform operations after obtaining the parameters, which is omitted here and can be seen on Github for example.

After that, we can use NPM publis after NPM install -g pruner-cli to download the installation tool and execute prune call directly.

Async Test

The test code also needs to use TypeScript and async/await. Mocha was selected to conduct BDD style tests. Chai is a library of assertions that work best when used together.

$ npm install mocha ts-node -g
$ npm install mocha chai ts-node --save-devCopy the code

Mocha normally executes JavaScript in the test/ directory. To test the TS code directly, skip the translation. We can bind ts-Node to execute the test code directly.

{
    "scripts": {
        "test": "mocha -r ts-node/register test/**/*.spec.ts"}},Copy the code

So all *.spec.ts under test/ will be tested, and you can expect BDD with await async. An example.

Travis

Add the configuration for the project language after adding.travis. Yml under the project

language: node_js
node_js:
  - "6"
  - "Seven"
  - "8"
  - "9"Copy the code

Then add your own open source project to travis-ci.org/ to automatically test compilation and test every push.

travis

Click the build | after passing the picture link to project can be displayed on the making of CI in the README state!

The original address