Files
bworld/tools/new_mod.ts
T
2026-09-24 23:28:01 -03:00

59 lines
2.0 KiB
TypeScript

// deno task new-mod <id> ["Display Name"]
// copies templates/mod into mods/<id>, with the template's placeholder id and name replaced
import { NAMESPACE_PATTERN, RESERVED_NAMESPACES } from "$/common/mod_data.ts";
const TEMPLATE = "templates/mod";
const PLACEHOLDER_ID = "example_mod";
const PLACEHOLDER_NAME = "Example Mod";
const TEXT_FILES = /\.(json|md|ts|js)$/;
export function create_mod(id: string, name: string, mods_dir = "mods") {
if (!NAMESPACE_PATTERN.test(id)) {
throw new Error(`"${id}" isn't a valid mod id: use 1-32 characters of a-z, 0-9 and _`);
}
if (RESERVED_NAMESPACES.includes(id)) {
throw new Error(`"${id}" is reserved`);
}
const target = `${mods_dir}/${id}`;
try {
Deno.statSync(target);
throw new Error(`${target} already exists`);
} catch (e) {
if (!(e instanceof Deno.errors.NotFound)) throw e;
}
copy_dir(TEMPLATE, target, (text) => text.replaceAll(PLACEHOLDER_ID, id).replaceAll(PLACEHOLDER_NAME, name));
return target;
}
function copy_dir(from: string, to: string, transform: (text: string) => string) {
Deno.mkdirSync(to, { recursive: true });
for (const entry of Deno.readDirSync(from)) {
const source = `${from}/${entry.name}`;
const destination = `${to}/${entry.name}`;
if (entry.isDirectory) {
copy_dir(source, destination, transform);
} else if (TEXT_FILES.test(entry.name)) {
Deno.writeTextFileSync(destination, transform(Deno.readTextFileSync(source)));
} else {
Deno.copyFileSync(source, destination);
}
}
}
if (import.meta.main) {
const [id, name] = Deno.args;
if (!id) {
console.error('Usage: deno task new-mod <id> ["Display Name"]');
Deno.exit(1);
}
const display_name = name ?? id.split("_").map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
try {
const path = create_mod(id, display_name);
console.log(`Created ${path}. Edit its manifest.json, then run deno task check-mods.`);
console.log("Note: the game can't load mods yet, see the implementation plan in MODS.md.");
} catch (e) {
console.error((e as Error).message);
Deno.exit(1);
}
}