Fix: npm run build has never worked on Windows

Both spawners built their argument with a file URL's .pathname, which on Windows is
"/C:/Users/...". Node resolves that against the drive root, so the build spawned
"C:\C:\Users\...\check-rules.mjs" and aborted on a module it could not find.

update-readme.mjs had the same bug and swallowed it in a bare catch, so instead of
failing it silently wrote a README whose guard box was empty — "0 guard lines" where
there should be four. A Windows build therefore could not happen, and if it had, it
would have quietly deleted the guard output from the README.

Same root as the packs/ CRLF bug: this repository has only ever been built on macOS and
Linux, where .pathname happens to be a usable path.

Also adds check-creatures to the pre-build guards, which it was missing, so the build
and npm run check now refuse the same things.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
slaguru666
2026-08-30 21:15:33 +01:00
co-authored by Claude Opus 5
parent 2c547efe72
commit 4ded53f9b5
2 changed files with 19 additions and 7 deletions
+17 -6
View File
@@ -458,30 +458,41 @@ async function writePack(packName, entries) {
// Refuse to build if a rule has been duplicated. The packs used to be built under
// one rule and played under another; this makes that impossible to ship.
//
// fileURLToPath, not .pathname: on Windows a file URL's pathname is "/C:/Users/...",
// and node resolves that against the drive root, so every one of these spawned
// "C:\C:\Users\...\check-rules.mjs" and the build aborted on a module it could not
// find. npm run build has never worked on Windows. Same root as the packs/ CRLF bug —
// this repository has only ever been built on macOS and Linux.
{
const { execFileSync } = await import("node:child_process");
try {
execFileSync(process.execPath, [new URL("check-rules.mjs", import.meta.url).pathname],
execFileSync(process.execPath, [fileURLToPath(new URL("check-rules.mjs", import.meta.url))],
{ stdio: "inherit" });
// And refuse if a posting is issued kit the catalogue cannot supply.
execFileSync(process.execPath, [new URL("check-kits.mjs", import.meta.url).pathname],
execFileSync(process.execPath, [fileURLToPath(new URL("check-kits.mjs", import.meta.url))],
{ stdio: "inherit" });
// And refuse if the localisation file cannot be loaded, which un-localises
// every sheet in the system and gives no error the user would connect to it.
execFileSync(process.execPath, [new URL("check-lang.mjs", import.meta.url).pathname],
execFileSync(process.execPath, [fileURLToPath(new URL("check-lang.mjs", import.meta.url))],
{ stdio: "inherit" });
// And refuse if a template will not compile. A parse error in a .hbs is a sheet
// that never opens, and no other guard reads templates — npc-sheet.hbs shipped
// broken and every NPC in the system threw on click.
execFileSync(process.execPath, [new URL("check-templates.mjs", import.meta.url).pathname],
execFileSync(process.execPath, [fileURLToPath(new URL("check-templates.mjs", import.meta.url))],
{ stdio: "inherit" });
// And refuse if a rule a play-through already found is broken again. The static
// guards cannot tell whether a rule is READ; these tests can.
execFileSync(process.execPath, [new URL("check-behaviour.mjs", import.meta.url).pathname],
execFileSync(process.execPath, [fileURLToPath(new URL("check-behaviour.mjs", import.meta.url))],
{ stdio: "inherit" });
// And refuse a scenario that tells the GM to roll a skill nobody has. THROUGH TRAIN
// asked for Ride through several drafts and a playtest; there is no Ride skill.
execFileSync(process.execPath, [new URL("check-scenarios.mjs", import.meta.url).pathname],
execFileSync(process.execPath, [fileURLToPath(new URL("check-scenarios.mjs", import.meta.url))],
{ stdio: "inherit" });
// And refuse an actor spec the engine cannot build. buildActor throws on an unknown
// skill or item key, but says nothing about a bad species, a misspelt field or a
// species-locked talent on the wrong species — all of which pack and play.
execFileSync(process.execPath, [fileURLToPath(new URL("check-creatures.mjs", import.meta.url))],
{ stdio: "inherit" });
} catch { console.error("build aborted: pre-build checks failed"); process.exit(1); }
}
+2 -1
View File
@@ -9,6 +9,7 @@
*/
import { readFile, writeFile } from "node:fs/promises";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { SKILL_CATALOGUE, WEAPONS, ARMOURS, GEAR, VEHICLES, NPCS, TALENTS }
from "./content.mjs";
import { ROLES, TRADES } from "../postings.mjs";
@@ -24,7 +25,7 @@ const items = WEAPONS.length + ARMOURS.length + GEAR.length + VEHICLES.length;
// The guard lines are quoted from the guards themselves rather than paraphrased.
const runGuard = name => {
try {
return execFileSync(process.execPath, [new URL(name, import.meta.url).pathname],
return execFileSync(process.execPath, [fileURLToPath(new URL(name, import.meta.url))],
{ encoding: "utf8" }).split("\n").find(l => l.startsWith(name.replace(".mjs", "")));
} catch { return null; }
};