node.jsでcliを作成します(コマンドラインインターフェース)

12548 ワード

node.jsでcliを作成します(コマンドラインインターフェース)
本文はあなたを連れて一歩ずつcliを完成して、本文に従って操作してきます.どうやってコマンドラインツールを作成するかを理解します.仕事で使うなら、自分でコマンドラインツールを書かないでください.nodejsに対して基本的な理解が必要です.
本論文の例は開発においてよく使われているプロジェクトのモデルを作成し、gitと依存インストールを含み、簡単なバージョンのvue-cliとみなすことができる.その中に含まれています
  • コマンドラインのパラメータ取得、--git-gおよび補足パラメータを提示する
  • を使用します.
  • どのようにjsファイルプログラムを実行可能なコマンドに追加しますか?
  • よく使われるコマンドライン作成npmパッケージ
  • コマンドラインツールと私たちが普段書いているnodeプログラムは全部nodejsプログラムの実行です.
  • の違いは、コマンドラインプログラムは直接カスタムコマンドを入力することによってプログラムの呼び出しを完了することができます.nodejsはnode***またはnpm scriptによって呼び出す必要があります.私たちが作成するコマンドラインは、bashのalias概念と似ています.あるいは実行可能プログラム名を実行可能リストに追加します.
  • の残りの作業は、解析パラメータであり、プログラム自体の機能を完了する
  • です.
    直接始めましょう
    (まず、プロセス中に関与した新しいアプリとnpmパッケージを管理しなくても、これらは完成したら見られます.)
    1.コマンドを作成し、パラメータを受信する
    nodejsプロジェクトを作成します.
    mkdir create-project && cd create-project
    npm init --yes
    
    ディレクトリの下でsrcディレクトリを作成し、cli.jsファイルを作成します.cli.js内容です.
    export function cli(args) {
     console.log(args);
    }
    
    このファイルはパラメータとトラヒックロジックを解析するためのファイルです.次はコマンドラインの入り口を作成します.ルートディレクトリはフォルダbinを作成して、その中に新しいファイルcreat-projectを作成して、内容を書き込みます.
    #!/usr/bin/env node
    
    require = require('esm')(module /*, options*/);
    require('../src/cli').cli(process.argv);
    
    この書類の中では小さな仕事しかしていません.まず、esmモジュールをロードしました.importを使って、私達のファイルの中で使用できます.(import方式でrequireを使用しなくてもいいです.本教程はES Moduleを使用しています.esmパッケージはこの方式の互換性を解決できます.)その後、cli.jsファイルをロードして、cli方法を呼び出します.このパラメータはコマンドラインの呼び出し時に入ってきたコマンドラインパラメータ配列です.
    この場合はesm依存をインストールする必要があります.
    npm install esm
    
    ディレクトリファイル:
    .
    ├── bin
    │   └── create-project
    ├── node_modules
    │   └── esm
    ├── package-lock.json
    ├── package.json
    └── src
        └── cli.js
    
    次に、パッケージマネージャ(npm)にCLI script(コマンドラインスクリプト)を暴くことをお知らせします.私たちはprocess.argvに適切なエントリを追加することによって、binフィールドでdescription name keyword mainフィールドを変更します.
    {
      "name": "@pipu11qiao/create-project",
      "version": "1.0.0",
      "description": "A cli to create project",
      "main": "src/index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "bin": {
        "create-project": "bin/create-project",
        "@pipu11qiao/create-project": "bin/create-project"
      },
      "publishConfig": {
        "access": "public"
      },
      "keywords": [
        "cli",
        "create-project"
      ],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "esm": "^3.2.25"
      }
    }
    
    binフィールドを通じて、npmにこのコマンドラインのインストールを通知します.私たちはbinフィールドに2つのコマンドを追加します.私たちnpmパッケージを使用すれば、ユーザー名で呼び出すことができます.一般ユーザーなら、直接package.jsonコマンドを使用することができます.
    コマンドラインに追加するには最後のステップが必要です.一番簡単な方法はnpm linkを使ってプロジェクトで実行します.
    npm link
    
    これは全体的にソフトリンクを作成します.これを通じてあなたのプロジェクトにアクセスできるので、後続のコードの更新もこの操作を更新する必要がありません.現在登録されているコマンドは呼び出し可能です.入力:
    create-projet
    
    出力が見えます.
    args [ '/usr/local/bin/node', '/usr/local/bin/create-project' ]
    
    前の二つのパラメータはnodeのインストール経路の変化によって異なります.コマンドパラメータの変化によって出力結果も多くなります.加えて--yesパラメータ
    create-project --yes
    
    結果:
    args [ '/usr/local/bin/node',
      '/usr/local/bin/create-project',
      '--yes' ]
    
    2.解析パラメータ処理ユーザ入力
    上のコマンドのパラメータはもう手に入りました.パラメータを解析します.私たちのコマンドはパラメータとオプションをサポートします.
  • templateは異なるテンプレートをサポートします.もしユーザが入力していないなら、ユーザーにテンプレートの選択を促すことになります.
  • --gitはcreate-projectを用いてプロジェクト初期化git
  • である.
  • -install自動装着依存
  • -yesスキップメッセージは、標準オプション
  • を使用します.
    私たちのプロジェクトではgit initを使用して、ユーザーに確実な入力オプションを提示し、inquirerパッケージを使ってパラメータを解析し、依存性を追加します.
    npm i inquirer arg
    
    まず、cli.jsファイルに解析パラメータの機能を追加します.
    import arg from 'arg';
    
    function parseArgumentsIntoOptions(rawArgs) {
     const args = arg(
       {
         '--git': Boolean,
         '--yes': Boolean,
         '--install': Boolean,
         '-g': '--git',
         '-y': '--yes',
         '-i': '--install',
       },
       {
         argv: rawArgs.slice(2),
       }
     );
     return {
       skipPrompts: args['--yes'] || false,
       git: args['--git'] || false,
       template: args._[0],
       runInstall: args['--install'] || false,
     };
    }
    
    export function cli(args) {
     let options = parseArgumentsIntoOptions(args);
     console.log(options);
    }
    
    creat-project--yesを実行して、skypProptsオプションがtrueに変わることが見えます.
    { skipPrompts: true,
      git: false,
      template: undefined,
      runInstall: false }
    
    次に、ユーザーの入力パラメータによって、不足しているパラメータのヒントを決定します.もしユーザがスキップを選択したら、そのままデフォルトのオプションに戻ります.そうでないと、ユーザに確かなオプションを選択して、promptForMissingOptions方法を追加して、parseArmogments IntoOptionsの後で呼び出します.
    import arg from 'arg';
    import inquirer from 'inquirer';
    
    function parseArgumentsIntoOptions(rawArgs) {
    // ...
    }
    
    async function promptForMissingOptions(options) {
     const defaultTemplate = 'JavaScript';
     if (options.skipPrompts) {
       return {
         ...options,
         template: options.template || defaultTemplate,
       };
     }
    
     const questions = [];
     if (!options.template) {
       questions.push({
         type: 'list',
         name: 'template',
         message: 'Please choose which project template to use',
         choices: ['JavaScript', 'TypeScript'],
         default: defaultTemplate,
       });
     }
    
     if (!options.git) {
       questions.push({
         type: 'confirm',
         name: 'git',
         message: 'Initialize a git repository?',
         default: false,
       });
     }
    
     const answers = await inquirer.prompt(questions);
     return {
       ...options,
       template: options.template || answers.template,
       git: options.git || answers.git,
     };
    }
    
    export async function cli(args) {
     let options = parseArgumentsIntoOptions(args);
     options = await promptForMissingOptions(options);
     console.log(options);
    }
    
    今はcreat-projectコマンドを実行しています.入力のヒントが見えます.
    ~/project/create-project  create-project
    ? Please choose which project template to use (Use arrow keys)
    ❯ JavaScript
      TypeScript
    
    テンプレートを選択したら、gitを開くかどうかを選択させます.最後の入力結果:
    { skipPrompts: false,
      git: false,
      template: 'JavaScript',
      runInstall: false }
    
    -yパラメータを追加することでヒントをスキップすることもできます.
    3.業務ロジックの追加
    私たちはもうコマンドラインで私たちが必要とするパラメータを得ることができました.次は私たちの実際のプログラムロジックです.テンプレートプロジェクトを作成して、空いているフォルダの中で、
  • プロジェクトファイルをテンプレートからコピーする
  • 実行git initとnpm instal負荷依存性
  • 3.1ファイルをコピーする
    まずテンプレートフォルダを作成します.templatesフォルダは、javascriptとtypescriptの2つのプロジェクトテンプレートを含んでいます.中にプロジェクトファイルsrcなどがあります.他の配置(.babelrc lint配置)とpackage.jsonファイルです.
    .
    ├── javascript
    │   ├── package.json
    │   └── src
    └── typescript
        ├── package.json
        └── src
    
    コピーファイルはargパケットを使用して、変更パケットはフォルダに再帰的にコピーすることができます.同名のファイルを強制的に上書きすることもできます.コンソールで色文字を出力することができます.
    すべてのプログラムロジックをmain.jsファイルに書いて、srcディレクトリの下で、コード:
    import chalk from 'chalk';
    import fs from 'fs';
    import ncp from 'ncp';
    import path from 'path';
    import { promisify } from 'util';
    
    const access = promisify(fs.access);
    const copy = promisify(ncp);
    
    async function copyTemplateFiles(options) {
     return copy(options.templateDirectory, options.targetDirectory, {
       clobber: false,
     });
    }
    
    export async function createProject(options) {
     options = {
       ...options,
       targetDirectory: options.targetDirectory || process.cwd(),
     };
    
     const currentFileUrl = import.meta.url;
     const templateDir = path.resolve(
       new URL(currentFileUrl).pathname,
       '../../templates',
       options.template.toLowerCase()
     );
     options.templateDirectory = templateDir;
    
     try {
       await access(templateDir, fs.constants.R_OK);
     } catch (err) {
       console.error('%s Invalid template name', chalk.red.bold('ERROR'));
       process.exit(1);
     }
    
     console.log('Copy project files');
     await copyTemplateFiles(options);
    
     console.log('%s Project ready', chalk.green.bold('DONE'));
     return true;
    }
    
    このコードは、まずfs.access法によってテンプレートが存在するかどうかを検出し、ncpによってテンプレートディレクトリをターゲットディレクトリにコピーすると、色付きのヒントが出力され、ファイルのコピーが完了したときに
    cli.jsでこの方法を呼び出します.
    import arg from 'arg';
    import inquirer from 'inquirer';
    import { createProject } from './main';
    
    function parseArgumentsIntoOptions(rawArgs) {
    // ...
    }
    
    async function promptForMissingOptions(options) {
    // ...
    }
    
    export async function cli(args) {
     let options = parseArgumentsIntoOptions(args);
     options = await promptForMissingOptions(options);
     await createProject(options);
    }
    
    フォルダtest-dirを新規作成して実行します.
    create-project TypeScript --git
    
    
    ヒントが表示されます.これはtypescriptディレクトリのファイルもフォルダにコピーされました.
    /create-project/test-dir  tree -L 2
    .
    ├── package.json
    └── src
    
    1 directory, 1 file
    
    3.git initとnpm instal負荷依存を実行する
    私達は3つの依存をインストールします.execaは外部の命令pkg-innstallインストールに依存しています.Listr定義のタスクリストには、タスク実行の進捗フィードバックが含まれています.
    npm install execa pkg-install listr
    
    mail.jsにコードを追加します.
    import chalk from 'chalk';
    import fs from 'fs';
    import ncp from 'ncp';
    import path from 'path';
    import { promisify } from 'util';
    import execa from 'execa';
    import Listr from 'listr';
    import { projectInstall } from 'pkg-install';
    
    const access = promisify(fs.access);
    const copy = promisify(ncp);
    
    async function copyTemplateFiles(options) {
     return copy(options.templateDirectory, options.targetDirectory, {
       clobber: false,
     });
    }
    
    async function initGit(options) {
     const result = await execa('git', ['init'], {
       cwd: options.targetDirectory,
     });
     if (result.failed) {
       return Promise.reject(new Error('Failed to initialize git'));
     }
     return;
    }
    
    export async function createProject(options) {
     options = {
       ...options,
       targetDirectory: options.targetDirectory || process.cwd()
     };
    
     const templateDir = path.resolve(
       new URL(import.meta.url).pathname,
       '../../templates',
       options.template
     );
     options.templateDirectory = templateDir;
    
     try {
       await access(templateDir, fs.constants.R_OK);
     } catch (err) {
       console.error('%s Invalid template name', chalk.red.bold('ERROR'));
       process.exit(1);
     }
    
     const tasks = new Listr([
       {
         title: 'Copy project files',
         task: () => copyTemplateFiles(options),
       },
       {
         title: 'Initialize git',
         task: () => initGit(options),
         enabled: () => options.git,
       },
       {
         title: 'Install dependencies',
         task: () =>
           projectInstall({
             cwd: options.targetDirectory,
           }),
         skip: () =>
           !options.runInstall
             ? 'Pass --install to automatically install dependencies'
             : undefined,
       },
     ]);
    
     await tasks.run();
     console.log('%s Project ready', chalk.green.bold('DONE'));
     return true;
    }
    
    ユーザがgit実行を選択すると、プロジェクトでgit initを実行し、負荷依存を選択するとnpm installまたはyarnを実行して依存をロードします.
    /project/create-project  rm -rf test-dir
    /project/create-project  mkdir test-dir
    /project/create-project  cd test-dir
    /project/create-project/test-dir  create-project typescript --git --install
      ✔ Copy project files
      ✔ Initialize git
      ✔ Install dependencies
    DONE Project ready
     wangyong@wangyongdeMacBook-Pro  ~/Study/project/create-project/test-dir   master 
    
    この時カタログで見ます.gitとnode_moduelsディレクトリ
    .
    ├── .git
    │   ├── HEAD
    │   ├── config
    │   ├── description
    │   ├── hooks
    │   ├── info
    │   ├── objects
    │   └── refs
    ├── node_modules
    │   └── esm
    ├── package-lock.json
    ├── package.json
    └── src
    
    ご成功おめでとうございます.最初のcliアプリを作成しました.
    アプリケーションを本物の他人が使えるモジュールにパッケージするには、srcディレクトリにindex.jsファイルを追加する必要があります.
    require = require('esm')(module);
    require('../src/cli').cli(process.argv);
    
    次のステップ
    これまで完全なコマンドラインアプリケーションのパッケージを作成しましたが、自分だけでnpm linkを使ってグローバルに登録してください.あなたのアプリケーションを他の人に共有したい場合は、github npmでリリースするなど、npmで配布する方法を強く推奨します.package.jsonにfilesフィールドを追加して、そのファイルがリリースされることを確認してください.
     },
     "files": [
       "bin/",
       "src/",
       "templates/"
     ]
    }
    
    npm pblishは自分のnpmバッグを発表します.
    関連記事
  • How to build a CLI with Node.js
  • The magic behind npm link