# 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`