# 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番目以降に記される。
```javascript=
//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(オプションはファイル名の前でも後ろでも良い。
```javascript=
//GFMオプションを定義する。
program.option("--gfm", "GFMを有効にする")
//コマンドライン引数をパースする。
program.parse(process.argv);
//オプションのパース結果をオブジェクトとして取得する
const options = program.opts();
console.log(options.gfm)
```
### デフォルト設定を定義する
アプリケーション側でデフォルト設定を持っておくと、markedの挙動が変わった際にも影響を受けにくくなる。
オプションの結果をオブジェクトで取得し、デフォルト設定(gfm:false)をspread構文(...)を使って置き換えるような変数を作っておく。
```javascript=
// コマンドライン引数のオプションを取得し、デフォルトのオプションを上書きする
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`