fix: make dev rebuilds dist/ on source changes (closes #332)
make dev passed --watch to a build.js that read no arguments, so it built once and exited. build.js --watch now builds, then builds again after every change to a file under src/, manifest/ or icons/, until interrupted; any other argument fails. Each directory gets its own watcher, because Node's recursive watch on Linux loses a file that an editor saves by renaming a new copy over it. A watch build writes no build receipt and cannot be verified; README.md and the Makefile say so and point to make build. Model: opus-5-5
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
// `make dev` runs `node build.js --watch`
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/332). These drive build.js's
|
||||
// watch() over a temp directory with a stand-in for build(), so nothing here
|
||||
// builds or writes dist/.
|
||||
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
|
||||
const { watch } = require("../build");
|
||||
|
||||
let dir;
|
||||
let watchers = [];
|
||||
|
||||
beforeEach(() => {
|
||||
dir = fs.mkdtempSync(path.join(os.tmpdir(), "autistmask-watch-"));
|
||||
fs.mkdirSync(path.join(dir, "nested"));
|
||||
jest.spyOn(console, "log").mockImplementation(() => {});
|
||||
jest.spyOn(console, "error").mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const watcher of watchers) watcher.close();
|
||||
watchers = [];
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
// Wait until `condition()` holds; fail the test if it has not within 3s.
|
||||
async function until(condition) {
|
||||
const deadline = Date.now() + 3000;
|
||||
while (!condition()) {
|
||||
if (Date.now() > deadline) throw new Error("timed out waiting");
|
||||
await sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the way many editors do: write a new copy, then rename it over the
|
||||
// original, so the file at that path is a different one afterwards.
|
||||
function saveByRename(file, contents) {
|
||||
fs.writeFileSync(`${file}.tmp`, contents);
|
||||
fs.renameSync(`${file}.tmp`, file);
|
||||
}
|
||||
|
||||
test("builds at start, then once per change to a file in a subdirectory", async () => {
|
||||
const file = path.join(dir, "nested", "a.js");
|
||||
fs.writeFileSync(file, "1");
|
||||
let builds = 0;
|
||||
watchers = watch([dir], async () => {
|
||||
builds++;
|
||||
});
|
||||
await until(() => builds === 1);
|
||||
|
||||
fs.writeFileSync(file, "2");
|
||||
await until(() => builds === 2);
|
||||
await sleep(300);
|
||||
expect(builds).toBe(2);
|
||||
});
|
||||
|
||||
test("keeps seeing a file that is saved by renaming a new copy over it", async () => {
|
||||
const file = path.join(dir, "nested", "a.js");
|
||||
fs.writeFileSync(file, "1");
|
||||
let builds = 0;
|
||||
watchers = watch([dir], async () => {
|
||||
builds++;
|
||||
});
|
||||
await until(() => builds === 1);
|
||||
|
||||
saveByRename(file, "2");
|
||||
await until(() => builds === 2);
|
||||
saveByRename(file, "3");
|
||||
await until(() => builds === 3);
|
||||
});
|
||||
|
||||
test("a failed build is reported and watching carries on", async () => {
|
||||
let builds = 0;
|
||||
watchers = watch([dir], async () => {
|
||||
builds++;
|
||||
if (builds === 1) throw new Error("unexpected token");
|
||||
});
|
||||
await until(() => console.error.mock.calls.length === 1);
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
"Build failed: unexpected token",
|
||||
);
|
||||
|
||||
fs.writeFileSync(path.join(dir, "a.js"), "1");
|
||||
await until(() => builds === 2);
|
||||
});
|
||||
|
||||
test("a change made while a build runs causes one more build after it", async () => {
|
||||
let builds = 0;
|
||||
watchers = watch([dir], async () => {
|
||||
builds++;
|
||||
if (builds === 1) {
|
||||
fs.writeFileSync(path.join(dir, "a.js"), "1");
|
||||
await sleep(200);
|
||||
}
|
||||
});
|
||||
await until(() => builds === 2);
|
||||
await sleep(300);
|
||||
expect(builds).toBe(2);
|
||||
});
|
||||
Reference in New Issue
Block a user