## 事象 [[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, }); ```