ゼロからnode足場ツールを構築する(一)


次の転送ドア:ゼロからnode足場ツールを構築する(二)
前言
実際の開発では、ゼロからエンジニアリング構造のvue-cli、create-react-app、コードシートを保存するsnippetsまで、さまざまな足場ツールに遭遇し、私たちの開発に多くの便利さをもたらしました.これらの足場ツールには、vue-cliはvueを作成するプロジェクトのみをサポートし、カスタマイズの程度が低く、snippetsは軽量レベルすぎて、複数の協力開発をサポートしていないなど、それぞれの利点と不足があります.だから、nodeを勉強した後、自分で自分のニーズに合ったnode足場ツールを作るつもりです.足場ツールのgithubアドレスを貼って、参考にしてください:YOSO:You only set once
typescript
私が選んだ開発言語はtypescriptです.Javascriptは動的タイプの言語であることはよく知られていますが、開発時にオブジェクトのタイプにあまり注意しません.これは確かにいくつかの便利さをもたらし、コードを簡潔にすることもありますが、時には面倒をもたらすこともあります.例えば、ずいぶん前に書いた関数ですが、良い注釈がなければ、この関数の入力と出力が何なのか自分では思い出せないかもしれません.また、コードを再構築するときに、関数にパラメータを追加すると、うっかり呼び出しが漏れやすくなります.特に大規模なプロジェクトでは、プログラマーの負担がかえって重くなっています.
typescriptはjavascriptのスーパーセットで、javascriptのすべての構文をサポートし、純粋なjavascriptにコンパイルして実行することができます.その大きな特徴は静的タイプです.ダイナミックタイプよりも、いくつかのタイプコードを多くする以外に、利点が多い.
検出エラー
typescriptの最も重要な点は、オンラインになってから発見することなく、コンパイル時にタイプエラーを検出できることです.
プログラミング仕様
typescriptのもう一つの利点は、プログラミングの規範を強化することであり、インタフェースを定義する簡便な方法を提供し、システムモジュールは抽象的にtypescript定義のインタフェースと見なすことができる.明確なインタフェースを有するモジュールを用いて大規模なシステムを構造化し,これはより抽象的な設計形式である.
コード可読性
タイプ寸法は、コードの可読性の向上にも役立ちます.また、typedocなどのツールを利用することで、ドキュメントの生成も容易になります.
typescriptを使うことを決めた後、開発にはいくつかの注意点があります.
  • まず、typescriptを使うことを決めた以上、その優位性を十分に利用しなければならない.タイプを書くときはいつでもanyを使わないで、できるだけ自分で定義したinterfaceを多く使ってください.そうしないと、これは厄介です.多重性を考慮すると、よく使われるタイプはdecelationファイルに定義でき、参照しやすいです.
  • typescriptにnpmパケットをインポートすると、Could not find a declaration file for moduleのエラーが発生する場合があります.これは、ほとんどのjavascriptライブラリにtypescriptタイプ定義がないためです.この問題を解決するために、DefinitelyTypeが作られた.これは高品質のtypescriptタイプ定義リポジトリであり、npm install @types/jquery --save-devを使用してライブラリにタイプ定義を追加できます.DefinitelyTypeで必要なライブラリが見つからない場合は、importメソッドを使用しないで、requireリファレンスに変更しても、エラーを回避できます.

  • デザイン
    正式に開発する前に、まず設計をしなければならない.プロジェクト全体の構造はnest-cliの構造を参考にしました.主に以下の内容が含まれています.
  • binフォルダの下にあるエントリファイル
  • commandsフォルダには、モジュール化されたcommandファイルが格納され、入力されたコマンドを受信および解析するために使用されます.nest-cliのcommandsフォルダは以下のとおりです.
  • commands
    ├── abstract.command.ts
    ├── add.command.ts
    ├── command.input.ts
    ├── command.loader.ts
    ├── generate.command.ts
    ├── index.ts
    ├── info.command.ts
    ├── new.command.ts
    └── update.command.ts
  • actionsフォルダには、コマンドの処理と実行のためにモジュール化されたactionファイルが格納されています.nest-cliのactionsフォルダは次のとおりです.
  • actions
    ├── abstract.action.ts
    ├── add.action.ts
    ├── generate.action.ts
    ├── index.ts
    ├── info.action.ts
    ├── new.action.ts
    └── update.action.ts
  • .gitignore、tsconfig.json、npm関連の書類など、これらの書類はすべて業界の規範に従って来ればいいので、私の工事
  • を参考にすることができます.
    足場工具のワークフローは大体以下の通りです.コマンドラインの処理にはcommander、inquirerなどのサードパーティライブラリが使用されます.また、react+inkを使用してuiを開発し、インタラクティブなインタフェースをより使いやすくします.テンプレートのロードでgithubウェアハウスからロードすることを選択しました.githubに関するapiを理解し、ユーザーのgithubウェアハウスアドレスを記録する必要があります.テンプレートエンジンMozillaのnunjucksを選びました.より有名なhandlebars、pug、ejsなどのテンプレートエンジンが選択されていないのは、これらのテンプレートエンジンが主にhtml言語を対象にしているため、xss攻撃を防ぐために転義的な処理が多く、足場ツールとしてjsなどのファイルを生成するのは面倒です.nunjucksのデフォルトでは、エスケープの処理は行われず、さまざまな面で差が少ない場合は、足場ツールに適しています.最後に、ファイル操作はnodeが持参したfsツールで行えます.
    開発開始
    プロジェクトの初期化
    デザインが完成すれば、本格的な開発を始めることができます.第一歩は、些細なことを完成して、プロジェクトに名前をつけて、名前をつける前に npm info nameで重名があるかどうかを見ることができます.プロジェクトコードを格納するgitウェアハウスを新規作成し、ローカルに対応するディレクトリを作成してリモートウェアハウスに接続します.npm initのコマンドで初期化してpackageを生成する.jsonファイル.
    typescriptで開発するのでtypescriptをインストールします.次にtsconfigを作成する.jsonファイル、tsconfig.jsonでtypescript関連の構成項目を設定します.このときtscを実行してjsファイルを正常にコンパイルできるかどうかを確認できます.それから、私达の设计によって、私达の必要なこれらのフォルダを新筑して、しかもbinフォルダの中で1つの入り口のファイルを新筑して、普通は入力のコマンドと同名で、私のここはyosoと言います.ts.それからまたjsonに構成項目を追加します.
    "scripts": {
        "build":"tec"
    }
    "bin": {
        "yoso": "bin/yoso.js"
    }

    注意しなければならないのはtypescriptで開発したが、実際には最終的にjsにコンパイルして実行し、私たちが送ったnpmパッケージもコンパイル後のjsコードでなければならないことだ.ここではbin/yoso.tsが作成されていますが、packageです.jsonではまだ書きますよ.js.そしてtsファイル、書き込み:
    #!/usr/bin/env node
    console.log("yoso!")

    先頭の#!/usr/bin/env nodeは少なくできません.これはnode環境のパスを指定します.保存して、テストできます.
    デバッグと発注
    もちろんbuildを選択してテストを実行することができますが、これは本当に愚かです.TSコードをテストするには、ts-nodeを選択してnpm install -D ts-nodeをインストール後、package.jsonにscriptを加える
    "scripts": {
        ...
        "start":"ts-node bin/yoso.ts"
    }

    その後、npm run startまたはnpm startを実行し、出力yoso!が見られると成功する.
    それからバッグを出してみてください.npmに行ってアカウントを登録し、npm loginに戻ります.建設する前に、まずpackageをください.jsonのバージョンは0.0.1に変更され、以降は毎回バージョン番号を変更し、0から始まる代表テストバージョンです.そしてtsファイルをコンパイルするには、tscコマンドで変更します.npmignoreファイルは、tsファイルを無視しますが、d.tsファイルを含めます.参考までにこのように書きました.
    .idea/
    .gitignore
    tsconfig.json
    
    #doc
    doc/
    
    #test
    test/
    
    # source
    **/*.ts
    *.ts
    
    # definitions
    !**/*.d.ts
    !*.d.ts

    コンパイルが完了したら、npm publishコマンドで発注します.発注前にコンパイルを忘れないようにscriptにnpmフックのコマンドを入れることをお勧めします
    script:{
        ...
        "prepublish": "npm run build",
        "postpublish": "npm run build:clear",
        "build:clear": "find ./actions ./bin ./commands ./utils ./ui ./component -type f -name '*.d.ts' -delete & find ./actions ./bin ./commands ./utils ./ui ./component -type f -not -name '*.ts*' -delete",
        ...
    }

    これによりpublishの前に自動的にコンパイルされ、publishの後にコンパイルされたjsファイルも自動的にクリアされ、非常に安心します.
    発注が成功すれば、npm install -g yosoをグローバルにインストールし、命令yosoを実行することができ、正常であれば出力が見えます.
    しかし、グローバルデバッグのたびにパケットを送信する必要はありません.コンパイル後、npm linkコマンドでグローバルのソフトリンクを確立し、yosoコマンドをグローバルに使用することができます.
    モジュール化
    モジュール化されたコマンドを書くことができますここではcommanderフレームワークを使っていますが、githubのホームページで例を見てみましょう.私のところには最も簡単なコマンドが貼られています.
    program
      .command('exec ')
      .alias('ex')
      .description('execute the given remote cmd')
      .option("-e, --exec_mode ", "Which exec mode to use")
      .action(function(cmd, options){
        
      });

    複数のoption、複数のこのようなコマンドがあれば、一緒に置くとコードが非常に不明確になり、メンテナンスに不利になります.モジュール化された開発では,各commandを抽象化し,実行されるactionも抽出する必要がある.そしてloadファイルにcommandと対応するactionをロードします.nest-cliを参照して、簡単なinitコマンドを書くことができます.まずactionsでactionの抽象ファイルを作成します.
    //abstract.action.ts
    interface Input {
      name: string;
      value: boolean | string;
    }
    export abstract class AbstractAction {
      public abstract async handle(
        inputs?: Input[],
        options?: Input[]
      ): Promise;
    }

    Inputのタイプは自分で定義したり、外部のタイプ宣言ファイルに書いたりすることができます.次にcommandsフォルダにcommandの抽象を作成します.
    //abstract.command.ts
    import { CommanderStatic } from 'commander';
    import { AbstractAction } from '../actions/abstract.action';
    
    export abstract class AbstractCommand {
      constructor(protected action: AbstractAction) {}
    
      public abstract load(program: CommanderStatic): void;
    }

    そしてこの抽象的なcommandを簡単に継承することができます.ここではinitと言います.command.ts.
    //init.command.ts
    import { Command, CommanderStatic } from "commander";
    import { AbstractCommand } from "./abstract.command";
    
    export class InitCommand extends AbstractCommand {
      public load(program: CommanderStatic) {
        program
          .command("init [tpl] [path]")
          .alias("i")
          .description("Init Files From Git, example: tpl init demo src")
          .action(async (tpl: string, path: string) => {
            let inputs: any = { path, tpl};
            await this.action.handle(inputs);
          });
      }
    }

    タイプは自分で定義するので、anyは書かないほうがいいです.次にactionsにabstractから簡単に継承すると書きます.action.tsのactionインスタンスであり、actionのエントリファイルindex.tsに導入する.
    //init.action.ts
    import { AbstractAction } from "./abstract.action";
    
    export class InitAction extends AbstractAction {
      public async handle(inputs: any) {
        console.log(inputs.tpl);
        console.log(inputs.path);
      }
    }

    ここまでcommandインスタンスもactionインスタンスも書き終わりましたが、次にcommandと対応するactionをロードします.commandsにcommandを作成する.loader.tsはindexに導入される.
    //command.loader.ts
    import { CommanderStatic } from "commander";
    import { InitAction } from "../actions";
    import { InitCommand } from "./init.command";
    
    export class CommandLoader {
      public static load(program: CommanderStatic): void {
        new InitCommand(new InitAction()).load(program);
      }
    }

    最後に、前に作成したbin/yosoを変更します.tsファイル.
    #!/usr/bin/env node
    
    import * as commander from 'commander';
    import { CommanderStatic } from 'commander';
    import { CommandLoader } from '../commands';
    
    const bootstrap = () => {
      const program: CommanderStatic = commander;
      program.version(require('../package.json').version);
      CommandLoader.load(program);
      commander.parse(process.argv);
    
      if (!program.args.length) {
          program.outputHelp();
      }
    };
    
    bootstrap();

    大きな成果を収めて、新しいinitコマンドnpm start init tpl-path out-pathをテストして、tplパスと出力パスがあるかどうかを確認します.成功すれば、actionで足場の操作を豊富にすることができます.
    次の転送ドア:ゼロからnode足場ツールを構築する(二)