Node.js には fs.watch という組み込みメソッドがあります。これはファイルを監視して変更があったら、そのファイルパスを通知する、といったメソッドです。
File system | Node.js v16.13.0 Documentation
これだけでもそこそこ使えるのですが、ファイルの書込読込ロック周りでエラーが起きて詰まったり(自分の環境だけやも。他の watch 系メソッドや Promise と組み合わせたりして解決?)、ディレクトリの中の細かい変化がわかりにくいです。gaze を使うとそのあたりがサクっと解決します。
shama/gaze: A globbing fs.watch wrapper built from the best parts of other fine watch libs.
少々古いライブラリですが現在(2021/11/01)の LTS の Node 16 では問題なく動きました。gaze では次の様に監視対象のファイルパスを glob 的に指定し、指定した gaze 関数内に監視イベントなどのハンドリング書いていきます。
gaze('resources/const/*.yaml', (err, watcher) => {
/**
* 第一引数にイベント。イベントは以下
* ready(watcher) ファイルがglobされ、監視が開始された時に発火
* all(event, filepath) 追加、変更、名前変更、削除のイベントが発生した時に発火
* added(filepath) ファイルが監視ディレクトリに追加された時に発火
* changed(filepath) ファイルが変更された時に発火
* deleted(filepath) ファイルが削除された時に発火
* renamed(newPath, oldPath) ファイルの名前が変更された時に発火
* end() watcherが閉じられ、watchが削除された時に発火
* error(err) エラーが発生した時に発火
* nomatch どのファイルもマッチしなかった時に発火
*/
watcher.on('changed', filepath => {
console.log(filepath + ' was changed');
});
});
gaze を使うことによって次の様にあるファイルの変更に応じてあるファイルを書き換える様なことができます。次の例は YAML ファイルを JavaScript ファイルに変換して出力する例です。
const fs = require('fs');
const path = require('path');
// @see https://github.com/eemeli/yaml
const YAML = require('yaml');
// @see https://github.com/shama/gaze
const gaze = require('gaze');
// 実行場所から見て resources/const/ 直下に存在する拡張子 yaml なファイルを全て監視
gaze('resources/const/*.yaml', (err, watcher) => {
// イベント外に変数を用意することによってイベントの度に前回の結果を参照できる様にする
/** YAML の中身のオブジェクト化 */
let yaml;
/** YAML ファイルの素の文字列。ファイル変更の度に変化し、YAML 形式でない文字列も格納される */
let yamlStr;
// ファイルが変化した時に発火
watcher.on('changed', (filepath) => {
// 無用な書き込みを防ぐために前回の読み取り結果を保持
const oldYamlStr = yamlStr;
try {
// YAML ファイルを読み取ってオブジェクト化
yamlStr = fs.readFileSync(filepath).toString();
yaml = YAML.parse(yamlStr);
} catch (e) {
// 正常動作中でも YAML 形式になっていない時が多いのでパースエラーは黙らせる
// なんかうまく動かないと思ったら console.error(e) を記述してデバッグ
}
if (oldYamlStr !== yamlStr) {
// 前回の読み取り結果と違っていたらオブジェクトを JavaScript で解釈できる形式にして書き込み
// ファイル名を元に変数名を生成。この変数名で export する
const varName = path.basename(filepath).replace(/\.yaml/, '');
fs.writeFileSync(
// ${変数名}.js のァイルに対して書き込み
`${varName}.js`
// ↑で定義した変数名で YAML の中身をオブジェクトとして export するファイルを生成
`export const ${varName} = ${JSON.stringify(yaml, null, 2)}`
);
}
});
});
上記例のみならば webpack の yaml-loader を使うなどした方がより楽ですが YAML ファイル以外にも応用がききます。
eemeli/yaml-loader: YAML loader for webpack (converts YAML to JSON)
これを応用すると複数のプログラミング言語のための定数や多言語メッセージを一元管理しつつ各言語に応じた IDE のサポートをつける、なんてこともできます。