Node.jsを使用してコマンドラインアプリケーションを構築する方法
開発者として、あなたはほとんどの時間をターミナルで過ごし、いくつかのタスクを回避するのに役立つコマンドを入力する可能性があります。
これらのコマンドの一部はオペレーティングシステムに組み込まれていますが、npmやbrewなどのサードパーティのヘルパーを介してインストールしたり、バイナリをダウンロードして$PATH
に追加したりすることもできます。
一般的に使用されるアプリケーションの良い例には、npm、eslint、typescript、および Angle CLI 、 Vue CLI 、 Create ReactAppなどのプロジェクトジェネレーターが含まれます。
このチュートリアルでは、Node.jsで2つの小さなCLIアプリケーションを構築します。
- https://quotes.rest/qodからその日の見積もりを取得するQuoteOfTheDayツール。
- JSONを使用してデータを保存するTo-Doリストアプリ。
前提条件
このチュートリアルを完了するには、次のものが必要です。
- Node.jsのローカル開発環境。 Node.jsをインストールしてローカル開発環境を作成する方法に従ってください
ステップ1-シェバンを理解する
スクリプトファイルを見ると、ファイルの先頭に次のような文字が表示されます。
file.sh#!/usr/bin/env sh
またはこれ:
file.py#!/usr/bin/env python -c
これらは、オペレーティングシステムのプログラムローダーが実行可能ファイルの正しいインタープリターを見つけて使用するための方法として機能します。 ただし、これはUnixシステムでのみ機能します。
ウィキペディアから:
コンピューティングでは、シバンは、スクリプトの先頭にある文字の番号記号と感嘆符(#!)で構成される文字シーケンスです。
NodeJSには、サポートされている独自のシバン文字があります。
logger.js
という名前の新しいファイルをエディターで作成します。
nano logger.js
次のコードを追加します。
#!/usr/bin/env node console.log("I am a logger")
最初の行は、NodeJSでこのファイルを解析するようにプログラムローダーに指示しています。 2行目は、画面にテキストを印刷します。
ターミナルにこれを入力すると、ファイルを実行してみることができます。 実行が拒否された権限を取得します。
./logger
Outputzsh: permission denied: ./logger
ファイルの実行権限を付与する必要があります。 あなたはそれをすることができます。
chmod +x logger ./logger
今回は出力が表示されます。
OutputI am a logger
このプログラムはnode logger
で実行できますが、シバンを追加してプログラムを実行可能にする独自のコマンドを使用すると、node
と入力して実行する必要がなくなります。
今日の見積もりアプリを作成する
ディレクトリを作成してqod
と呼びましょう。 そして内部で、NodeJsアプリをインスタンス化します。
mkdir qod cd qod npm init -y
次に、quotesサーバーにリクエストを送信する必要があることがわかっているので、既存のライブラリを使用してこれを行うことができます。 axiosを使用します
npm install --save axios
また、ターミナルで色を印刷するのに役立つライブラリであるチョークを追加します。
npm install --save chalk
次に、これらの引用符を取得するために必要なロジックを記述します。
qod
という名前の新しいファイルを作成します。
nano qod
次のコードをqod
ファイルに追加して、シェバンを指定し、ライブラリをロードして、APIURLを保存します。
qod
#!/usr/bin/env node const axios = require('axios'); const chalk = require('chalk'); const url = "https://quotes.rest/qod";
次に、次のコードを追加して、APIにGET
リクエストを作成します。
[label qod]// make a get request to the url axios({ method: 'get', url: url, headers: { 'Accept': 'application/json' }, // this api needs this header set for the request }).then(res => { const quote = res.data.contents.quotes[0].quote const author = res.data.contents.quotes[0].author const log = chalk.green(`${quote} - ${author}`) // we use chalk to set the color green on successful response console.log(log) }).catch(err => { const log = chalk.red(err) // we set the color red here for errors. console.log(log) })
ファイルを保存します。
ファイルが実行可能になるようにファイルのアクセス許可を変更します。
chmod +x qod
次に、アプリケーションを実行します。
./qod
見積もりが表示されます:
OutputThe best way to not feel hopeless is to get up and do something. Don’t wait for good things to happen to you. If you go out and make some good things happen, you will fill the world with hope, you will fill yourself with hope. - Barack Obama
この例は、CLIアプリケーションで外部ライブラリを使用できることを示しています。
次に、データを保存するCLIプログラムを作成しましょう。
ToDoリストの作成
これは、データの保存と取得を伴うため、もう少し複雑になります。 これが私たちが達成しようとしていることです。
todo
というコマンドが必要です- コマンドは4つの引数を取ります。
new
、get
、complete
、およびhelp
。
したがって、使用可能なコマンドは次のようになります
./todo new // create a new todo ./todo get // get a list of all your todos ./todo complete // complete a todo item. ./todo help // print the help text
todo
というディレクトリを作成し、Node.jsアプリをインスタンス化します。
mkdir todo cd todo npm install -y
次に、 chalk を再度インストールして、色でログに記録できるようにします。
npm install --save chalk
最初に行うことは、これらのコマンドが使用可能であることを確認することです。 コマンドを機能させるには、NodeJの process / argv を使用します。これは、コマンドライン引数の文字列配列を返します。process.argv
プロパティは、ノードで渡されたコマンドライン引数を含む配列を返します。 .jsプロセスが起動されました。
ファイルtodo
を作成します。
nano todo
これをtodoファイルに追加します。
やること
#!/usr/bin/env node console.log(process.argv)
ファイルに実行可能ファイルのアクセス許可を与えてから、新しいコマンドで実行します。
chmod +x ./todo ./todo new
次の出力が得られます。
Output[ '/Users/sammy/.nvm/versions/node/v8.11.2/bin/node', '/Users/sammy/Dev/scotch/todo/todo', 'new' ]
配列の最初の2つの文字列は、プログラムへのインタープリターとファイルのフルパスであることに注意してください。 配列の残りの部分には、渡された引数が含まれています。 この場合はnew
です。
安全のために、これらを制限して、正しい数の引数(1つ)のみを受け入れることができ、new
、get
、およびcomplete
のみを受け入れることができるようにします。
todo
ファイルを次のように変更します。
やること
#!/usr/bin/env node const chalk = require('chalk') const args = process.argv // usage represents the help guide const usage = function() { const usageText = ` todo helps you manage you todo tasks. usage: todo <command> commands can be: new: used to create a new todo get: used to retrieve your todos complete: used to mark a todo as complete help: used to print the usage guide ` console.log(usageText) } // used to log errors to the console in red color function errorLog(error) { const eLog = chalk.red(error) console.log(eLog) } // we make sure the length of the arguments is exactly three if (args.length > 3) { errorLog(`only one argument can be accepted`) usage() }
最初にコマンドライン引数を変数に割り当ててから、下部で長さが3以下であることを確認します。
usage
文字列も追加しました。これは、コマンドラインアプリが期待するものを出力します。 以下のような間違ったパラメータでアプリを実行します。
./todo new app
Outputonly one argument can be accepted todo helps you manage you todo tasks. usage: todo <command> commands can be: new: used to create a new todo get: used to retrieve your todos complete: used to mark a todo as complete help: used to print the usage guide
1つのパラメーターを指定して実行すると、何も出力されません。つまり、コードはパスします。
次に、4つのコマンドのみが予期されていることを確認する必要があり、それ以外はすべて無効として出力されます。
ファイルの先頭にコマンドのリストを追加します。
やること
const commands = ['new', 'get', 'complete', 'help']
そして、長さを確認した後、渡されたコマンドで確認します。
やること
... if (commands.indexOf(args[2]) == -1) { errorLog('invalid command passed') usage() }
ここで、無効なコマンドを使用してアプリを実行すると、これが発生します。
./todo ne
Outputinvalid command passed todo helps you manage you todo tasks. usage: todo <command> commands can be: new: used to create a new todo get: used to retrieve your todos complete: used to mark a todo as complete help: used to print the usage guide
次に、usage
関数を呼び出して、help
コマンドを実装しましょう。 これをtodoファイルに追加しましょう:
やること
//... switch(args[2]) { case 'help': usage() break case 'new': break case 'get': break case 'complete': break default: errorLog('invalid command passed') usage() } //...
switch
ステートメントがあり、呼び出されたコマンドに基づいて関数を呼び出します。 よく見ると、help
の場合はusage関数を呼び出すだけであることがわかります。
new
コマンドは、新しいToDoアイテムを作成し、それをjsonファイルに保存します。 使用するライブラリはlowdbです。 必要に応じて、jsonファイルの読み取りと書き込みを行う関数を簡単に作成できます。
lowdbをインストールします
npm install --save lowdb
[readline](https://nodejs.org/api/readline.html)
とlowdb
の依存関係を追加して、データの保存に役立てましょう。 lowdbコードは、githubページの標準です。
やること
//... const rl = require('readline'); const low = require('lowdb') const FileSync = require('lowdb/adapters/FileSync') const adapter = new FileSync('db.json') const db = low(adapter) // Set some defaults (required if your JSON file is empty) db.defaults({ todos: []}).write() //...
次に、ユーザーにデータの入力を求める関数を追加します。
やること
//... function prompt(question) { const r = rl.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); return new Promise((resolve, error) => { r.question(question, answer => { r.close() resolve(answer) }); }) } //...
ここでは、readlineライブラリを使用して、ユーザーにプロンプトを表示して出力を読み取るのに役立つインターフェイスを作成しています。
次に、ユーザーがnew
コマンドを入力したときに呼び出される関数を追加する必要があります。
やること
//... function newTodo() { const q = chalk.blue('Type in your todo\n') prompt(q).then(todo => { console.log(todo) }) } //...
プロンプトの青色を取得するためにチョークを使用しています。 そして、結果をログに記録します。
最後に、new
の場合に関数を呼び出します。
やること
// ... switch(args[2]) { //... case 'new': newTodo() break // ... } // ...
新しいコマンドを使用してアプリを実行すると、todoを追加するように求められます。 入力してEnterキーを押します。
./todo new
OutputType in your todo This my todo aaaaaaw yeah This my todo aaaaaaw yeah
これに似たものが表示されるはずです。
また、db.json
ファイルがファイルシステムに作成されており、todosプロパティがあることにも注意してください。
次に、todoを追加するためのロジックを追加しましょう。 newTodo関数を変更します。
やること
//... function newTodo() { const q = chalk.blue('Type in your todo\n') prompt(q).then(todo => { // add todo db.get('todos') .push({ title: todo, complete: false }) .write() }) } //...
コードを再度実行します。
./todo new
OutputType in your todo Take a Scotch course
db.json
を見ると、todoが追加されていることがわかります。 次のgetコマンドで取得できるように、さらに2つ追加します。 db.json
ファイルのレコード数が増えると次のようになります。
db.json
{ "todos": [ { "title": "Take a Scotch course", "complete": false }, { "title": "Travel the world", "complete": false }, { "title": "Rewatch Avengers", "complete": false } ] }
new
コマンドを作成した後、get
コマンドを実装する方法をすでに理解しているはずです。
ToDoを取得する関数を作成します。
やること
//... function getTodos() { const todos = db.get('todos').value() let index = 1; todos.forEach(todo => { const todoText = `${index++}. ${todo.title}` console.log(todoText) }) } //... // switch statements switch(args[2]) { //... case 'get': getTodos() break //... } //....
コマンドを再度実行します。
./todo get
今すぐアプリを実行すると、次の出力が得られます。
Output1. Take a Scotch course 2. Travel the world 3. Rewatch Avengers
chalk.green
を使用して緑色にすることができます。
次に、少し複雑なcomplete
コマンドを追加します。
あなたは2つの方法でそれを行うことができます。
- ユーザーが
./todo complete
と入力するたびに、すべてのToDoを一覧表示し、Todoに完了としてマークするための番号/キーを入力するように依頼できます。 - 別のパラメーターを追加して、ユーザーが
./todo get
と入力し、./todo complete 1
などのパラメーターで完了としてマークするタスクを選択できるようにすることができます。
new
コマンドを実装したときに最初の方法を実行する方法を学習したので、オプション2を見ていきます。
このオプションを使用すると、コマンド./todo complete 1
は、指定されたコマンド数の有効性チェックに失敗します。 したがって、最初にこれを処理する必要があります。 引数の長さをチェックする関数を次のように変更します。
やること
//... // we make sure the length of the arguments is exactly three if (args.length > 3 && args[2] != 'complete') { errorLog('only one argument can be accepted') usage() return } ///...
このアプローチは真理値表を使用します。ここで、TRUE && FALSE
はFALSE
と等しく、complete
が渡されるとコードはスキップされます。
次に、新しい引数の値を取得し、todoの値を完了したものにします。
やること
//... function completeTodo() { // check that length if (args.length != 4) { errorLog("invalid number of arguments passed for complete command") return } let n = Number(args[3]) // check if the value is a number if (isNaN(n)) { errorLog("please provide a valid number for complete command") return } // check if correct length of values has been passed let todosLength = db.get('todos').value().length if (n > todosLength) { errorLog("invalid number passed for complete command.") return } // update the todo item marked as complete db.set(`todos[${n-1}].complete`, true).write() } //...
また、switch
ステートメントを更新して、complete
コマンドを含めます。
やること
//... case 'complete': completeTodo() break //...
./todo complete 2
でこれを実行すると、db.json
がこれに変更され、2番目のタスクが完了としてマークされていることがわかります。
db.json
{ "todos": [ { "title": "Take a Scotch course", "complete": false }, { "title": "Travel the world", "complete": true }, { "title": "Rewatch Avengers", "complete": false } ] }
最後に行う必要があるのは、./todo get
を変更して、実行されたタスクのみを表示することです。 これには絵文字を使用します。 次のコードでgetTodos
を変更します。
やること
//... function getTodos() { const todos = db.get('todos').value() let index = 1; todos.forEach(todo => { let todoText = `${index++}. ${todo.title}` if (todo.complete) { todoText += ' ✔ ️' // add a check mark } console.log(chalk.strikethrough(todoText)) }) return } //...
./todo get
と入力すると、これが表示されます。
Output1. Take a Scotch course 2. Travel the world ✔ ️ 3. Rewatch Avengers
結論
Node.jsで2つのCLIアプリケーションを作成しました。
アプリが機能したら、ファイルをbin
フォルダーに配置します。 このように、npmは、実行可能ファイルを配布するときに実行可能ファイルを操作する方法を知っています。 また、実行可能ファイルを配置する場所に関係なく、package.json
binプロパティを更新する必要があります。
この記事の焦点は、CLIアプリケーションがvanilla nodejsを使用してどのように構築されるかを調べることでしたが、現実の世界で作業する場合は、ライブラリを使用する方が生産的です。
npmに公開できる素晴らしいCLIアプリケーションを作成するのに役立つライブラリのリストを次に示します。
- vopral-フル機能のインタラクティブCLIフレームワーク
- meow-CLIヘルパーライブラリ
- commanderjs-CLIライブラリ
- minimist-引数の解析用
- yargs-引数の解析
そして、色を手伝ってくれたチョークのようなライブラリは言うまでもありません。
追加の演習として、CLIにDelete
コマンドを追加してみてください。