Try   HackMD

JavaScript 応用編3(学習日:9/30 Node.jsでCLIアプリ作成)

1. Node.jsを用いたアプリ作成の基本

今回用いるNode.jsは、CLI(コマンドライン)で動かすため、改行コードをLF(↓)に、文字コードはUTF-8に統一する。

node.jsでのファイル実行

ファイルが保存されているフォルダ内でcmd(コマンドプロンプト)を起動し、
node JavaScriptファイル名 と入力すると、ファイルが実行される。
console.logの内容は、そのまま出力処理として現れる。

Chrome(ブラウザ)との違い

Node.jsはChromeと同じエンジン(V8)を使用しているが、ブラウザ環境とはグローバルオブジェクトが異なる。

両方の環境で使えるもの

  • ECMAScriptで定義されたビルドインオブジェクトやラッパーオブジェクト(Boolean, String, Map, Promise等)
  • 形が異なってもほぼ同等の機能を持つもの(Console API, setTimeout関数など)

WebブラウザとCLIで扱いが異なるもの

  • グローバルオブジェクト(ブラウザ→window, Node.js→grobal)
  • グローバルオブジェクトに付属する関数やプロパティ
    • windowオブジェクト→document, XMLHttpRequest等
    • globalオブジェクト→process, Buffer等

2. コマンドライン引数の処理

processオブジェクト

processオブジェクトは、Node.jsのグローバル変数の一つ。
同オブジェクトのagrvプロパティは、コマンドライン引数を出力する。
形式は文字列の配列型として返される。
ファイルを読み込んだ場合、配列の1番目は実行プロセスパス、2番目にはスクリプトファイルのパスが必ず返る。
したがって、ファイル名の後ろに記述した引数は、配列の3番目以降に記される。

//main.jsに記述。 console.log(process.agrv);

コマンドプロンプトで
node main.js one two=three four
と入力して実行

実行結果↓
[
'C:\Program Files\nodejs\node.exe',
'C:\勇希\会社\アジャイル講習\JavaScript\nodecli\main.js',
'one',
'two=three',
'four'
]

文字列のパース

コマンドライン引数には、アプリケーションに不要なものが含まれる。また、文字列型で返すため扱いにくい場合がある。従って、一度パース(分割)して扱いやすい値に成型するのが一般的である。

commanderパッケージ

コマンドライン引数をパースできるパッケージ。ライブラリであるため、npmで取得しないと使えない。
取得前後でpackage.jsonの内容が変化している。

CommonJSモジュール

Node.jsでのJavaScriptモジュール化の仕組みである。標準パッケージやnpmで取得できるパッケージは多くがこの仕組みで提供される。

module.exportsプロパティに代入されたオブジェクトは、その一つのみがファイルからエクスポートされる。
インポートはrequire関数で行う。node_modulesというディレクトリの中に保存されているものをインポートする。

ライブラリからのパッケージ取得は、以上の方法で行う。

commanderによるパース

commanderを利用したパースでは、実行ファイルのプロセスパスを省略し、引数部分のみの配列に整理することができる。
require("commander").parse(process.argv);

3. ファイル読み込み

fsモジュール

Node.jsでファイルの読み書きを行う際に利用するモジュール。
非同期形式と同期形式の両方が提供されている。

非同期形式の特徴

  • コールバック関数を受必ずけ取る。
  • 第一引数は必ずエラーオブジェクト(成功時はnullかundefined)で、第二引数以降は処理の戻り値となる。

同期形式の特徴

  • 例外が通常通り発生する。(try…catch文が使える)

ファイルの読み込み

非同期形式の場合

fs.readFile(ファイルパス,コールバック関数(失敗処理,成功処理))
※但し、コールバック関数の第二引数(成功処理)は、ファイルの中身をバイト列で保持するBufferインスタンスである。
人間が読めるようにするためには、fs.readFileの第二引数に文字コード指定{encoding:"utf8"}を記述し、コールバック関数を第三引数に置く。
fs.readFile(ファイルパス,{encoding:"utf8"},コールバック関数(失敗処理,成功処理))

同期形式の場合

fs.readFileSync(ファイルパス)

エラー処理の記述

コールバック関数の第一引数のエラー処理には、一般的なエラーを表すprocess.exit(1)を記述し、returnする。

4. MarkdownをHTMLに変換する

markedライブラリ

読み込んだMarkdownファイルをHTMLに変換するライブラリ。(Ajax通信の回で用いたDOMのようなもの)
これまでのライブラリと同様に、npmで取得してから利用する。

変換オプションの作成

markedでは、HTMLの性質が変化するオプションの設定が可能である。

gfmオプション

gfmオプションは、GithubのMarkdown仕様(GFM)に合わせて変換するかを決めるオプションである。(デフォルトはtrue)
GFMの機能としては、URLの自動リンク化が代表的である。
gfmのオプションを使えば、返還時にURLは自動的にハイパーリンク化される。

コマンドライン引数からオプションを受け取る

commanderのoptionメソッドを利用すれば、CLIでオプション入力時にオプションを設定できるようになる。
CLIで次の通り入力する node main.js gfm sample.md(オプションはファイル名の前でも後ろでも良い。

//GFMオプションを定義する。 program.option("--gfm", "GFMを有効にする") //コマンドライン引数をパースする。 program.parse(process.argv); //オプションのパース結果をオブジェクトとして取得する const options = program.opts(); console.log(options.gfm)

デフォルト設定を定義する

アプリケーション側でデフォルト設定を持っておくと、markedの挙動が変わった際にも影響を受けにくくなる。
オプションの結果をオブジェクトで取得し、デフォルト設定(gfm:false)をspread構文()を使って置き換えるような変数を作っておく。

// コマンドライン引数のオプションを取得し、デフォルトのオプションを上書きする const cliOptions = { gfm: false, ...program.opts(), };

5. ユニットテストを記述する

ユニットテストを行えるよう、モジュール化を行う。
今回は、コマンドライン引数の処理部分と、MarkdownをHTMLに変換する処理に分割する。

CommonJSを用いたモジュール化

2.で紹介の通り、CommonJSの機能を用いてモジュール化を行う。
モジュール化では、各機能ごとの個別処理を担う機能部分と、全体処理を行うメイン部分に分かれる。
機能部分のモジュールからエクスポートされた関数等を、メイン部分がインポートして利用する。
役割分担がはっきりし、テストなどの保守性や可読性が高まる。

機能部分からモジュールをエクスポート

module.exportsで指定された処理をエクスポートできる。

メイン部分へモジュールをインポート

requireで機能部分のモジュールファイルをインポートできる。
但し、ファイル指定を行う際は"./ファイル名"とすることに注意する。

ユニットテストの実行環境を作る

Mochaによるテスト実行環境を作成する。こちらもnpmからインストールする。
JSONのscripts内のtestの部分は、あらかじめ"mocha test"/に変更しておく。

テストコードを記述する

assertモジュールのassert.strictEqualメソッドは、第一引数と第二引数が完全等価でない場合に例外を投げる。
今回はこのメソッドを利用してテストを行う。
testフォルダにテストコードを、testフォルダ内のfixturesフォルダ内にsample.mdとexpected.html、expected-gfm.htmlの種類のファイルを格納する。(htmlファイルは、sample.mdの内容がHTMLに変換が想定されるコードと同じものを、gfmオプションの有無を分けて行っている。
格納が完了したら、npm testを実行する。

tags: JavaScript