@@ -44,78 +44,42 @@ $ node main.js one two=three four
4444
4545## コマンドライン引数をパースする {#parse-args}
4646
47- ` process.argv ` 配列を使えばコマンドライン引数を取得できますが、取得できる情報にはアプリケーションに不要なものも含まれています 。
47+ ` process.argv ` 配列を使えばコマンドライン引数を取得できますが、アプリケーションには不要なものも含まれています 。
4848また、文字列の配列として渡されるため、フラグのオンオフのような真偽値を受け取るときにも不便です。
4949そのため、アプリケーションでコマンドライン引数を扱うときには、一度パースして扱いやすい値に整形するのが一般的です。
5050
51- 今回は[ commander] [ ] というライブラリを使ってコマンドライン引数をパースしてみましょう。
52- 文字列処理を自前で行うこともできますが、このような一般的な処理は既存のライブラリを使うと簡単に書けます。
53-
54- ### ` commander ` パッケージをインストールする {#install-commander}
55-
56- commanderは[ npm] [ ] の` npm install ` コマンドを使ってインストールできます。
57- まだnpmの実行環境を用意できていなければ、先に「[ アプリケーション開発の準備] [ ] 」の章を参照してください。
58-
59- npmでパッケージをインストールする前に、まずは` package.json ` というファイルを作成します。
60- ` package.json ` とは、アプリケーションが依存するパッケージの種類やバージョンなどの情報を記録するJSON形式のファイルです。
61- ` package.json ` ファイルのひな形は、` npm init ` コマンドで生成できます。
62- 通常は対話式のプロンプトによって情報を設定しますが、ここではすべてデフォルト値で` package.json ` を作成する` --yes ` オプションを付与します。
63-
64- ` nodecli ` のディレクトリ内で、` npm init --yes ` コマンドを実行して` package.json ` を作成しましょう。
65-
66- ``` shell
67- $ npm init --yes
68- ```
69-
70- 生成された` package.json ` ファイルは次のようになっています。
71-
72- [ import, title:"package.json"] ( src/package.init.json )
73-
74- ` package.json ` ファイルが用意できたら、` npm install ` コマンドを使って` commander ` パッケージをインストールします。
75- このコマンドの引数にはインストールするパッケージの名前とそのバージョンを` @ ` 記号でつなげて指定できます。
76- バージョンを指定せずにインストールすれば、その時点での最新の安定版が自動的に選択されます。
77- 次のコマンドを実行して、commanderのバージョン9.0をインストールします。[ ^ 1 ]
78-
79- ``` shell
80- 81- ```
82-
83- インストールが完了すると、` package.json ` ファイルは次のようになっています。
84-
85- [ import, title:"package.json"] ( src/package.install.json )
86-
87- また、` npm install ` をすると同時に` package-lock.json ` ファイルが生成されています。
88- このファイルはnpmがインストールしたパッケージの、実際のバージョンを記録するためのものです。
89- 先ほどcommanderのバージョンを` 9.0 ` としましたが、実際にインストールされるのは` 9.0.x ` に一致する最新のバージョンです。
90- ` package-lock.json ` ファイルには実際にインストールされたバージョンが記録されています。
91- これによって、再び` npm install ` を実行したときに、異なるバージョンがインストールされるのを防ぎます。
92-
51+ 今回は、Node.jsの標準モジュールである[ ` node:util ` モジュール] [ ] の[ parseArgs] [ ] という関数を使ってコマンドライン引数をパースしてみましょう。
52+ コマンドライン引数のパース処理を自前で行うこともできますが、このような一般的な処理はNode.jsの標準モジュールやサードパーティ製のライブラリを使うことで簡単に実装できます。
53+ 今回利用する` node:util ` モジュールは、Node.js自体に同梱されている標準モジュールであるため、npmを使って別途インストールする必要はありません。
9354
9455### ECMAScriptモジュールを使う {#esmodule}
9556
96- 今回のユースケースでは、インストールした ` commander ` パッケージを利用するにあたって 、基本文法で学んだ[ ECMAScriptモジュール] [ ] を使います。
97- ` commander ` パッケージはECMAScriptモジュールに対応しているため 、次のように` import ` 文を使って変数や関数などをインポートできます 。
57+ 今回のユースケースでは、` node:util ` モジュールを利用するにあたって 、基本文法で学んだ[ ECMAScriptモジュール] [ ] を使います。
58+ ` node:util ` モジュールは 、次のように` import ` 文を使ってインポートできます 。
9859
9960<!-- doctest:disable -->
10061``` js
101- import { program } from " commander" ;
62+ // `node:util`モジュールを、utilオブジェクトとしてインポートする
63+ import * as util from " node:util" ;
10264```
10365
104- ただし、ECMAScriptモジュールのパッケージをインポートするには、インポート元のファイルもECMAScriptモジュールでなければなりません 。
66+ ただし、ECMAScriptモジュールを扱う場合には、Node.jsに対してJavaScriptファイルがどのモジュール形式であるかを明示する必要があります 。
10567なぜなら、[ Node.js] [ ] は[ CommonJSモジュール] [ ] という別のモジュール形式もサポートしており、CommonJSモジュール形式では` import ` 文は利用できないためです。
106- そのため、これから実行するJavaScriptファイルがどちらの形式であるかをNode.jsに教える必要があります。
10768
10869Node.jsはもっとも近い上位ディレクトリの ` package.json ` が持つ ` type ` フィールドの値によってJavaScriptファイルのモジュール形式を判別します。
109- ` type ` フィールドが ` module ` であればECMAScriptモジュールとして、` type ` フィールドが ` commonjs ` であればCommonJSモジュールとして扱われます。[ ^ 2 ]
70+ ` type ` フィールドが ` module ` であればECMAScriptモジュールとして、` type ` フィールドが ` commonjs ` であればCommonJSモジュールとして扱われます。[ ^ 1 ]
11071また、JavaScriptファイルの拡張子によって明示的に示すこともできます。拡張子が ` .mjs ` である場合はECMAScriptモジュールとして、` .cjs ` である場合はCommonJSモジュールであると判別されます。
11172
112- 今回は ` main.js ` を ECMAScriptモジュールとして判別させるために、次のように ` package.json ` に` type ` フィールドを追加します。
73+ 今回は ` main.js ` を ECMAScriptモジュールとして判別させるために、次のコマンドで ` package.json ` に` type ` フィールドを追加します。
74+ まだ、` package.json ` を作成していない場合は、先に「[ Node.jsプロジェクトのセットアップ] [ ] 」を参照してください。
11375
11476``` shell
115- # npm pkg コマンドで type フィールドの値をセットする
77+ # npm pkg コマンドで、package.jsonの type フィールドの値をセットする
11678$ npm pkg set type=module
11779```
11880
81+ コマンドが実行できたら` package.json ` ファイルに ` type ` フィールドが追加されていることを確認してください。
82+
11983[ import, title:"package.json"] ( src/package.json )
12084
12185#### [ コラム] CommonJSモジュール {#commonjs-module}
@@ -125,7 +89,7 @@ CommonJSモジュールは[ECMAScriptモジュール][]の仕様が策定され
12589
12690現在はNode.jsでもECMAScriptモジュールがサポートされていますが、` fs ` などの標準モジュールはCommonJSモジュールとして提供されています。
12791また、サードパーティ製のライブラリや長く開発が続けられているプロジェクトのソースコードなどでも、CommonJSモジュールを利用する場面は少なくありません。
128- そのため、この2つのモジュール形式が共存する場合には、開発者はモジュール形式間の相互運用性(互いを組み合わせた時の動作)に注意する必要があります。[ ^ 3 ]
92+ そのため、この2つのモジュール形式が共存する場合には、開発者はモジュール形式間の相互運用性(互いを組み合わせた時の動作)に注意する必要があります。[ ^ 2 ]
12993
13094Node.jsはECMAScriptモジュールからCommonJSモジュールをインポートする方向の相互運用性をサポートしています。
13195たとえば、次のようにCommonJSモジュールで` exports ` オブジェクトを使ってエクスポートされたオブジェクトは、ECMAScriptモジュールで` import ` 文を使ってインポートできます。
@@ -146,40 +110,51 @@ import { key } from "./lib.cjs";
146110
147111### コマンドライン引数からファイルパスを取得する {#get-file-path}
148112
149- 先ほどインストールした ` commander ` パッケージを使って 、コマンドライン引数として渡されたファイルパスを取得しましょう。
113+ ` node:util ` モジュールを使って 、コマンドライン引数として渡されたファイルパスを取得しましょう。
150114このCLIアプリケーションでは、処理の対象とするファイルパスを次のようなコマンドの形式で受け取ります。
151115
152116``` shell
153117$ node main.js ./sample.md
154118```
155119
156- commanderでコマンドライン引数をパースするためには、インポートした ` program ` オブジェクトの ` parse ` メソッドにコマンドライン引数を渡します 。
120+ コマンドライン引数をパースするためには、 ` node:util ` モジュールの ` parseArgs ` 関数を利用します 。
157121
158122<!-- doctest:disable -->
159123``` js
160- // commanderモジュールからprogramオブジェクトをインポートする
161- import { program } from " commander" ;
162- // コマンドライン引数をcommanderでパースする
163- program .parse (process .argv );
124+ // `node:util`モジュールを、utilオブジェクトとしてインポートする
125+ import * as util from " node:util" ;
126+
127+ // コマンドライン引数をparseArgs関数でパースする
128+ const {
129+ values ,
130+ positionals
131+ } = util .parseArgs ({
132+ // オプションやフラグ以外の引数を渡すことを許可する
133+ allowPositionals: true
134+ });
135+ console .log (values); // オプションやフラグを含むオブジェクト
136+ console .log (positionals); // フラグ以外の引数の配列
164137```
165138
166- ` parse ` メソッドを呼び出すと、コマンドライン引数をパースした結果を` program ` オブジェクトから取り出せるようになります。
167- 今回の例では、ファイルパスは` program.args ` 配列に格納されています。
168- ` program.args ` 配列には` --key=value ` のようなオプションや` --flag ` のようなフラグを取り除いた残りのコマンドライン引数が順番に格納されています。
139+ ` parseArgs ` 関数は、コマンドライン引数をパースした結果として` values ` と` positionals ` の2つのプロパティを持つオブジェクトを返します。
140+ ` values ` オブジェクトには、` --key=value ` のようなオプションや` --flag ` のようなフラグをパースした結果が保存されています。
141+ ` positionals ` 配列には、オプションやフラグ以外の引数が配列として順番に格納されています。
142+ デフォルトでは、` positionals ` 配列はパース結果には含まれないため、` allowPositionals ` オプションを` true ` にすることで含まれるようになります。
169143
144+ 今回の` main.js ` に渡す` ./sample.md ` 引数はオプションやフラグではないので、` positionals ` 配列に格納されます。
170145それでは` main.js ` を次のように変更し、コマンドライン引数で渡されたファイルパスを取得しましょう。
171146
172147[ import title:"main.js"] ( src/main-2.js )
173148
174- 次のコマンドを実行すると、` program.args ` 配列に格納された ` ./sample.md ` 文字列が取得されてコンソールに出力されます。
175- ` ./sample.md ` は` process.argv ` 配列では3番目に存在していましたが、パース後の` program.args ` 配列では1番目になって扱いやすくなっています。
149+ 次のコマンドを実行すると、` positionals ` 配列の先頭に格納された ` ./sample.md ` 文字列が取得されてコンソールに出力されます。
150+ ` ./sample.md ` は` process.argv ` 配列では3番目に存在していましたが、パース後の` positionals ` 配列では1番目になって扱いやすくなっています。
176151
177152``` shell
178153$ node main.js ./sample.md
179154./sample.md
180155```
181156
182- このように、` process.argv ` 配列を直接扱うよりも、commanderのようなライブラリを使うことで宣言的にコマンドライン引数を定義して処理できます 。
157+ このように、` process.argv ` 配列を直接扱うよりも、` node:util ` の ` parseArgs ` 関数を利用すると、コマンドライン引数をより扱いやすい形にパースできます 。
183158次のセクションではコマンドライン引数から取得したファイルパスを元に、ファイルを読み込む処理を追加していきます。
184159
185160#### [ エラー例] SyntaxError: Cannot use import statement outside a module {#syntax-error-import-statement}
@@ -188,7 +163,7 @@ $ node main.js ./sample.md
188163
189164<!-- doctest:disable -->
190165``` shell
191- import { program } from " commander " ;
166+ import * as util from " node:util " ;
192167^^^^^^
193168
194169SyntaxError: Cannot use import statement outside a module
@@ -199,18 +174,18 @@ SyntaxError: Cannot use import statement outside a module
199174## このセクションのチェックリスト {#section-checklist}
200175
201176- ` process.argv ` 配列に` node ` コマンドのコマンドライン引数が格納されていることを確認した
202- - npmを使ってパッケージをインストールする方法を理解した
203177- ECMAScriptモジュールを使ってパッケージを読み込めることを確認した
204- - commanderを使ってコマンドライン引数をパースできることを確認した
178+ - ` node:util ` モジュールの ` parseArgs ` 関数を使ってコマンドライン引数をパースできることを確認した
205179- コマンドライン引数で渡されたファイルパスを取得してコンソールに出力できた
206180
207- [commander]: https://github.com/tj/commander.js/
208- [npm]: https://www.npmjs.com/
181+
209182[ npmのGitHubリポジトリ ] : https://github.com/npm/npm
210183[ CommonJSモジュール ] : https://nodejs.org/docs/latest/api/modules.html
211184[ Node.js ] : https://nodejs.org/
212185[ アプリケーション開発の準備 ] : ../../setup-local-env/README.md
213186[ ECMAScriptモジュール ] : ../../../basic/module/README.md
214- [^1]: --saveオプションをつけてインストールしたのと同じ意味。npm 5.0.0からは--saveがデフォルトオプションとなりました。
215- [^2]: [package.json and file extensions](https://nodejs.org/api/packages.html#packagejson-and-file-extensions)
216- [^3]: [Interoperability with CommonJS](https://nodejs.org/api/esm.html#interoperability-with-commonjs)
187+ [ `node:util`モジュール ] : https://nodejs.org/api/util.html
188+ [ parseArgs ] : https://nodejs.org/api/util.html#utilparseargsconfig
189+ [ Node.jsプロジェクトのセットアップ ] : ../helloworld/README.md#setup-nodejs-project
190+ [ ^ 1 ] : [ package.json and file extensions] ( https://nodejs.org/api/packages.html#packagejson-and-file-extensions )
191+ [ ^ 2 ] : [ Interoperability with CommonJS] ( https://nodejs.org/api/esm.html#interoperability-with-commonjs )
0 commit comments