## 事象
[[Chokidar (JavaScript)|Chokidar]]の `watch` で監視したファイルを[[Neovim]]で直接編集すると、1度目は変更検知されるが、2度目は変更が検知されない。[[esbuild]]で生成している `main.js` は検知される。ただし直接変更の場合は同じく検知されない。
なお、[[Notepad]]で変更した場合は何度でも検知できる。
`esbuild.config.mjs`
```ts
import fs, { existsSync } from "fs";
import path from "path";
import builtins from "builtin-modules";
import chokidar from "chokidar";
import esbuild from "esbuild";
import process, { exit } from "process";
async function loadPluginDir() {
const configName = "carnelianrc.json";
if (!existsSync(configName)) {
console.error(`${configName}がカレントディレクトリに存在しません`);
exit(1);
}
let rcConfig;
try {
rcConfig = await Bun.file(configName).json();
} catch (e) {
console.error(`${configName}のパースに失敗しました`);
exit(1);
}
const vaultPath = rcConfig.vaultPath;
if (!vaultPath) {
console.error("carnelianrc.jsonにVaultのパスが指定されていません");
exit(1);
}
if (!existsSync(vaultPath)) {
console.error(
`carnelianrc.jsonのVaultに指定されたパス ${vaultPath} は存在しません`,
);
exit(1);
}
return path.join(vaultPath, ".obsidian/plugins/carnelian");
}
const pluginDir = await loadPluginDir();
console.log(`📁 ${pluginDir}ディレクトリを作成します(既にある場合は変更なし)`);
fs.mkdirSync(pluginDir, { recursive: true });
const hotreloadPath = path.join(pluginDir, ".hotreload", "");
console.log(`🌶 ${hotreloadPath}ファイルを作成します`);
fs.writeFileSync(hotreloadPath, "");
const FILES = ["main.js", "manifest.json", "styles.css"];
const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = process.argv[2] === "production";
const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["src/main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins,
],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
const watcher = chokidar.watch(FILES, { persistent: true });
watcher
.on("add", (p) => {
fs.copyFileSync(p, path.join(pluginDir, p));
})
.on("change", (p) => {
fs.copyFileSync(p, path.join(pluginDir, p));
});
}
```
### 環境
| 対象 | バージョン |
| ----------------------------------- | ---------- |
| [[Neovim]] | 0.10.2 |
| [[Chokidar (JavaScript)\|Chokidar]] | 4.0.3 |
## 原因
[[Neovim]]が[[アトミックセーブ]]を採用しているためっぽい。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
Only a single `change` event is fired for each file · Issue #237 · paulmillr/chokidar
</div>
<div class="link-card-v2-content">
Here's a very basic example: require('chokidar').watch(process.argv).on("change", function(path) { console.log(p ...
</div>
<img class="link-card-v2-image" src="https://opengraph.githubassets.com/4aa5774f2b64fc4efb0c5273535f1447cfb19a6355cadd442a0b02e968d826ac/paulmillr/chokidar/issues/237" />
<a href="https://github.com/paulmillr/chokidar/issues/237"></a>
</div>
## 解決方法
`usePolling: true` を指定する。
```diff
const watcher = chokidar.watch(FILES, {
- persistent: true,
+ usePolling: true,
});
```