chokidar、fs-extra、fsevents、gaze、node-watch、watchpack は、Node.js 環境でファイルの変更を検知したり、ファイル操作を簡易化したりするためのライブラリ群です。chokidar はクロスプラットフォームなファイル監視のデファクトスタンダードであり、fs-extra は標準の fs モジュールを拡張して直感的な操作を提供します。fsevents は macOS 専用のネイティブ監視モジュールで、通常は chokidar などの依存関係として利用されます。gaze と node-watch は以前広く使われていましたが、現在は保守が停滞しているか、特定の用途に限定されています。watchpack は Webpack 内部で使用される監視ツールで、バンドルツール向けに最適化されています。
Node.js でファイルの変更を検知したり、ファイル操作を自動化したりする際、標準モジュールの fs だけでは不十分な場合があります。chokidar、fs-extra、fsevents、gaze、node-watch、watchpack は、それぞれ異なる目的と歴史を持っています。これらがどのように違い、どのように使い分けるべきかを、実際のコードと動作特性から解説します。
これらのパッケージは大きく分けて「ファイル監視」と「ファイル操作」の 2 つに分類できます。
chokidar はファイル監視のデファクトスタンダードです。
// chokidar: 基本的な監視
const chokidar = require('chokidar');
const watcher = chokidar.watch('src/**/*.js', {
ignored: /node_modules/,
persistent: true
});
watcher.on('change', path => {
console.log(`File ${path} has been changed`);
});
fs-extra はファイル操作を拡張します。
fs モジュールに、ディレクトリ作成やファイルコピーなどの高レベルな関数を追加します。async/await に対応しており、エラーハンドリングが容易です。// fs-extra: ディレクトリ確保とファイルコピー
const fs = require('fs-extra');
async function copyAssets() {
await fs.ensureDir('./dist/assets');
await fs.copy('./src/assets', './dist/assets');
console.log('Assets copied successfully');
}
fsevents は macOS 専用のネイティブ監視です。
chokidar が内部でオプションとして利用します。// fsevents: macOS 専用ネイティブ監視(直接使用は稀)
const fsevents = require('fsevents');
const stop = fsevents.watch(__dirname, (path, flags, id) => {
const info = fsevents.getInfo(path, flags, id);
console.log('Native event:', info);
});
// 後で停止する場合
// stop();
gaze は古いファイル監視ライブラリです。
chokidar への移行が推奨されます。// gaze: 古い監視ライブラリ(非推奨)
const gaze = require('gaze');
const watcher = new gaze.Gaze('**/*.js');
watcher.on('changed', filepath => {
console.log(`${filepath} was changed`);
});
node-watch は fs.watch のラッパーです。
chokidar が無難です。// node-watch: 標準 fs.watch のラッパー
const watch = require('node-watch');
watch('./src', { recursive: true }, (evt, name) => {
console.log('%s changed.', name);
});
watchpack は Webpack 向けの監視ツールです。
// watchpack: Webpack 内部向け監視
const Watchpack = require('watchpack');
const wp = new Watchpack({
aggregateTimeout: 1000
});
wp.watch({
files: ['index.js'],
directories: ['src']
});
wp.on('aggregated', (changes, removals) => {
console.log('Changes:', changes);
});
ファイル監視の仕組みは、OS の機能に依存するため、パッケージごとのアプローチが性能に直結します。
chokidar は状況に応じて最適な手法を選びます。
fsevents を使い、Windows では ReadDirectoryChangesW、Linux では inotify を利用します。fsevents はネイティブバインディングです。
gaze と node-watch は主に fs.watch や fs.watchFile に依存します。
fs.watchFile はポーリングを行うため、ファイル数が増えると重くなります。watchpack は遅延評価と集約に強いです。
ライブラリを選ぶ際、メンテナンス状況はセキュリティと安定性に直結します。
chokidar と fs-extra は活発に保守されています。
gaze は事実上の保守終了状態です。
fsevents は必要な場合にのみインストールされます。
optionalDependencies として扱われることが多く、必須ではありません。watchpack は Webpack エコシステムに縛られます。
chokidar で十分です。ファイル変更を検知してブラウザを更新したい場合。
chokidar// chokidar でホットリロード検知
const watcher = chokidar.watch('src');
watcher.on('change', () => {
reloadBrowser();
});
ビルド前にディレクトリを掃除し、ファイルを配置したい場合。
fs-extraemptyDir や move などの高機能なメソッドが便利です。// fs-extra でビルド準備
await fs.emptyDir('./dist');
await fs.move('./temp/build', './dist/final');
数千のログファイルの変更を監視したい場合。
chokidar (usePolling: false)// chokidar で大量ファイル監視
const watcher = chokidar.watch('/var/logs', {
usePolling: false,
interval: 100
});
バンドルプロセス中にファイル変更を捉えたい場合。
watchpack// watchpack でプラグイン開発用監視
const wp = new Watchpack();
wp.watch({ files: compiler.inputFiles });
| 機能 | chokidar | fs-extra | fsevents | gaze | node-watch | watchpack |
|---|---|---|---|---|---|---|
| 主な用途 | ファイル監視 | ファイル操作 | macOS 監視 | ファイル監視 | ファイル監視 | ビルドツール監視 |
| クロスプラットフォーム | ✅ | ✅ | ❌ (macOS only) | ✅ | ✅ | ✅ |
| 保守状況 | 活発 | 活発 | 活発 | 停滞 | 低調 | 活発 |
| ネイティブ依存 | オプション | なし | 必須 | なし | なし | なし |
| 推奨度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐ | ⭐⭐ | ⭐⭐⭐ |
現代の Node.js 開発において、ファイル監視は chokidar、ファイル操作は fs-extra を選ぶのが最も安全で効率的です。これらはコミュニティでの信頼が厚く、予期せぬトラブルに遭遇するリスクが低いです。
fsevents は意識して選ぶものではなく、chokidar などが勝手に使ってくれるものです。gaze は過去の遺産として扱い、新規採用は避けてください。watchpack は Webpack 関連の特殊な事情がない限り、一般的な用途では chokidar で代用可能です。
最終的な指針:
chokidarfs-extrafs モジュールで十分この 2 つを覚えておけば、ほとんどのファイルシステム関連の要件はカバーできます。
ファイルのコピー、移動、削除などを直感的なメソッドで行いたい場合は fs-extra を使います。標準の fs モジュールに欠けている ensureDir や copy などの便利機能が揃っており、エラーハンドリングも親切です。
クロスプラットフォームで安定したファイル監視が必要な場合は chokidar を選択します。Node.js エコシステムで最も広く支持されており、macOS、Windows、Linux で一貫した動作を保証します。新規プロジェクトのファイル監視ではこれが第一選択肢です。
macOS 環境でネイティブなファイルイベント監視を最適化したい場合に使いますが、単独では使いません。通常は chokidar が自動的に利用するため、明示的に導入するケースは稀です。Windows や Linux では動作しないため注意が必要です。
古いプロジェクトの保守以外では gaze の使用は避けるべきです。保守が停滞しており、現代の Node.js バージョンとの互換性やパフォーマンス面で chokidar に劣ります。新規プロジェクトでの採用は推奨されません。
Node.js 標準の fs.watch をラップして使いやすくしたい場合に検討しますが、機能面で chokidar に劣ります。依存関係を減らしたい軽量なユースケース以外では、より堅牢な chokidar を選ぶのが無難です。
Webpack プラグインやバンドルツール内部のロジックを構築する場合は watchpack を検討します。一般のアプリケーション開発ではなく、ビルドツール開発など特定のインフラ要件がある場合に適しています。
fs-extra adds file system methods that aren't included in the native fs module and adds promise support to the fs methods. It also uses graceful-fs to prevent EMFILE errors. It should be a drop in replacement for fs.
I got tired of including mkdirp, rimraf, and ncp in most of my projects.
npm install fs-extra
fs-extra is a drop in replacement for native fs. All methods in fs are attached to fs-extra. All fs methods return promises if the callback isn't passed.
You don't ever need to include the original fs module again:
const fs = require('fs') // this is no longer necessary
you can now do this:
const fs = require('fs-extra')
or if you prefer to make it clear that you're using fs-extra and not fs, you may want
to name your fs variable fse like so:
const fse = require('fs-extra')
you can also keep both, but it's redundant:
const fs = require('fs')
const fse = require('fs-extra')
NOTE: The deprecated constants fs.F_OK, fs.R_OK, fs.W_OK, & fs.X_OK are not exported on Node.js v24.0.0+; please use their fs.constants equivalents.
There is also an fs-extra/esm import, that supports both default and named exports. However, note that fs methods are not included in fs-extra/esm; you still need to import fs and/or fs/promises seperately:
import { readFileSync } from 'fs'
import { readFile } from 'fs/promises'
import { outputFile, outputFileSync } from 'fs-extra/esm'
Default exports are supported:
import fs from 'fs'
import fse from 'fs-extra/esm'
// fse.readFileSync is not a function; must use fs.readFileSync
but you probably want to just use regular fs-extra instead of fs-extra/esm for default exports:
import fs from 'fs-extra'
// both fs and fs-extra methods are defined
Most methods are async by default. All async methods will return a promise if the callback isn't passed.
Sync methods on the other hand will throw if an error occurs.
Also Async/Await will throw an error if one occurs.
Example:
const fs = require('fs-extra')
// Async with promises:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
.catch(err => console.error(err))
// Async with callbacks:
fs.copy('/tmp/myfile', '/tmp/mynewfile', err => {
if (err) return console.error(err)
console.log('success!')
})
// Sync:
try {
fs.copySync('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
// Async/Await:
async function copyFiles () {
try {
await fs.copy('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
}
copyFiles()
NOTE: You can still use the native Node.js methods. They are promisified and copied over to fs-extra. See notes on fs.read(), fs.write(), & fs.writev()
walk() and walkSync()?They were removed from fs-extra in v2.0.0. If you need the functionality, walk and walkSync are available as separate packages, klaw and klaw-sync.
fse-cli allows you to run fs-extra from a console or from npm scripts.
If you like TypeScript, you can use fs-extra with it: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/fs-extra
If you want to watch for changes to files or directories, then you should use chokidar.
fs-filesystem allows you to read the state of the filesystem of the host on which it is run. It returns information about both the devices and the partitions (volumes) of the system.
Wanna hack on fs-extra? Great! Your help is needed! fs-extra is one of the most depended upon Node.js packages. This project
uses JavaScript Standard Style - if the name or style choices bother you,
you're gonna have to get over it :) If standard is good enough for npm, it's good enough for fs-extra.
What's needed?
Note: If you make any big changes, you should definitely file an issue for discussion first.
fs-extra contains hundreds of tests.
npm run lint: runs the linter (standard)npm run unit: runs the unit testsnpm run unit-esm: runs tests for fs-extra/esm exportsnpm test: runs the linter and all testsWhen running unit tests, set the environment variable CROSS_DEVICE_PATH to the absolute path of an empty directory on another device (like a thumb drive) to enable cross-device move tests.
If you run the tests on the Windows and receive a lot of symbolic link EPERM permission errors, it's
because on Windows you need elevated privilege to create symbolic links. You can add this to your Windows's
account by following the instructions here: http://superuser.com/questions/104845/permission-to-make-symbolic-links-in-windows-7
However, I didn't have much luck doing this.
Since I develop on Mac OS X, I use VMWare Fusion for Windows testing. I create a shared folder that I map to a drive on Windows.
I open the Node.js command prompt and run as Administrator. I then map the network drive running the following command:
net use z: "\\vmware-host\Shared Folders"
I can then navigate to my fs-extra directory and run the tests.
I put a lot of thought into the naming of these functions. Inspired by @coolaj86's request. So he deserves much of the credit for raising the issue. See discussion(s) here:
First, I believe that in as many cases as possible, the Node.js naming schemes should be chosen. However, there are problems with the Node.js own naming schemes.
For example, fs.readFile() and fs.readdir(): the F is capitalized in File and the d is not capitalized in dir. Perhaps a bit pedantic, but they should still be consistent. Also, Node.js has chosen a lot of POSIX naming schemes, which I believe is great. See: fs.mkdir(), fs.rmdir(), fs.chown(), etc.
We have a dilemma though. How do you consistently name methods that perform the following POSIX commands: cp, cp -r, mkdir -p, and rm -rf?
My perspective: when in doubt, err on the side of simplicity. A directory is just a hierarchical grouping of directories and files. Consider that for a moment. So when you want to copy it or remove it, in most cases you'll want to copy or remove all of its contents. When you want to create a directory, if the directory that it's suppose to be contained in does not exist, then in most cases you'll want to create that too.
So, if you want to remove a file or a directory regardless of whether it has contents, just call fs.remove(path). If you want to copy a file or a directory whether it has contents, just call fs.copy(source, destination). If you want to create a directory regardless of whether its parent directories exist, just call fs.mkdirs(path) or fs.mkdirp(path).
fs-extra wouldn't be possible without using the modules from the following authors:
Licensed under MIT
Copyright (c) 2011-2024 JP Richardson