實は esbuild は關係が無い。だうやら大量の file を生成すると webpack --watch
が SIGBUS で落ちるやうだ。esbuild が tsc よりとても速いから一氣に webpack に更新を食はせて、落ちる頻度が随分と上がる、といふ事があった。bundle せず大量の TypeScript file を大量の JavaScript file に build してゐる理由は別途であるので、ここではさういふものだと思って欲しい。
似た issue を見附けた。
この issue を讀み、再生成する file 數を減らせば改善すると假說を立て、少なくとも見かけ上は改善した。斷言せず留保してゐるのは、原因を探ってゐないからだ。
まづ esbuild は glob を解釋しないから、node-glob を使ふ wrapper script を書く。src/
から dist/
へ build するとしよう。esbuild の watch 機能を使ふと、require()
等省略するが、かうなる。
const isWatch = process.argv.includes("--watch"); function isEntryPoint(filename) { return true; // 省略 } function build(entryPoints) { esbuild.build({ entryPoints, format: "cjs", logLevel: "warning", outbase: path.join(__dirname, "src"), outdir: path.join(__dirname, "dist"), watch: isWatch, }); } glob(path.join(__dirname, "src/**/*.{ts,tsx}")) .then(filenames => filenames.filter(isEntryPoint)) .then(build);
これでは file を更新する度に全ての JavaScript file が再生成され、webpack --watch
が SIGBUS で落ちる。これを減らす爲に、自前で watch する。
function watch(dirname, callback) { let queue = []; fs.watch(path.join(__dirname, dirname), { recursive: true }, (eventType, filename) => { queue.push(path.join(__dirname, dirname, filename)); }); const loop = async () => { if (queue.length === 0) { setTimeout(loop, 1000); return; } const filenames = Array.from(new Set(queue)); queue = []; const existFilenames = ( await Promise.all( filenames.map(async filename => { try { await fs.promises.access(filename); } catch (err) { return undefined; } return filename; }) ) ).filter(filename => filename !== undefined); await callback(existFilenames); setImmediate(loop); }; setImmediate(loop); }
1 秒 (1,000 ms) 以内の file 更新は纏めてある。file 削除は考慮してゐない、clean & build でなんとかする。そもそも手元での watch は CI での build に影響しない。
この watch 函數から esbuild を呼び出す。
function build(entryPoints) { esbuild.build({ entryPoints, format: "cjs", logLevel: "warning", outbase: path.join(__dirname, "src"), outdir: path.join(__dirname, "dist"), }); } glob(path.join(__dirname, "src/**/*.{ts,tsx}")) .then(filenames => filenames.filter(isEntryPoint)) .then(build) .then(() => { if (isWatch) { watch("src", filenames => { const entryPoints = filenames.filter(isEntryPoint); if (entryPoints.length === 0) { return; } build(entryPoints); }); } });
何とかなった。