162 lines
5.2 KiB
JavaScript
162 lines
5.2 KiB
JavaScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join, resolve } from "node:path";
|
|
import { createRequire } from "node:module";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const {
|
|
findProjectRootUpward,
|
|
findProjectRootForFile,
|
|
discoverProjects,
|
|
isProjectRoot,
|
|
} = require("../out/projectRoot.js");
|
|
|
|
let fixtureRoot;
|
|
let counter = 0;
|
|
|
|
function scratch(rel = "") {
|
|
if (!fixtureRoot) {
|
|
fixtureRoot = mkdtempSync(join(tmpdir(), "ra3-projectroot-"));
|
|
}
|
|
const dir = join(fixtureRoot, String(counter++), rel);
|
|
mkdirSync(dir, { recursive: true });
|
|
return dir;
|
|
}
|
|
|
|
function write(dir, rel, content = "") {
|
|
const file = join(dir, rel);
|
|
mkdirSync(join(file, ".."), { recursive: true });
|
|
writeFileSync(file, content);
|
|
return file;
|
|
}
|
|
|
|
test.after(() => {
|
|
if (fixtureRoot) rmSync(fixtureRoot, { recursive: true, force: true });
|
|
});
|
|
|
|
test("upward discovery: Data folder, subfolders and additionalmaps", () => {
|
|
const root = scratch();
|
|
write(root, "Data/Mod.xml", "<AssetDeclaration/>");
|
|
assert.equal(findProjectRootUpward(join(root, "Data")), resolve(root));
|
|
assert.equal(
|
|
findProjectRootUpward(join(root, "Data", "GlobalData", "Units")),
|
|
resolve(root),
|
|
);
|
|
assert.equal(
|
|
findProjectRootUpward(join(root, "Data", "additionalmaps", "nested")),
|
|
resolve(root),
|
|
);
|
|
});
|
|
|
|
test("upward discovery: mapmetadata-only mod", () => {
|
|
const root = scratch();
|
|
write(root, "Data/additionalmaps/mapmetadata_Global.xml", "<MapMetadata/>");
|
|
assert.equal(
|
|
findProjectRootUpward(join(root, "Data", "additionalmaps")),
|
|
resolve(root),
|
|
);
|
|
assert.equal(findProjectRootUpward(join(root, "Data")), resolve(root));
|
|
assert.equal(isProjectRoot(root), true);
|
|
});
|
|
|
|
test("upward discovery: case-insensitive Data and Mod.xml", () => {
|
|
const root = scratch();
|
|
write(root, "data/mod.xml", "<AssetDeclaration/>");
|
|
assert.equal(findProjectRootUpward(join(root, "Data")), resolve(root));
|
|
assert.equal(findProjectRootUpward(root), resolve(root));
|
|
});
|
|
|
|
test("upward discovery: babproj markers", () => {
|
|
const root = scratch();
|
|
write(root, "mod.babproj", "");
|
|
assert.equal(findProjectRootUpward(root), resolve(root));
|
|
|
|
const root2 = scratch();
|
|
write(root2, "SomeProject.babproj", "");
|
|
assert.equal(findProjectRootUpward(root2), resolve(root2));
|
|
});
|
|
|
|
test("upward discovery: no marker returns null", () => {
|
|
const root = scratch();
|
|
write(root, "random/file.txt", "x");
|
|
assert.equal(findProjectRootUpward(root), null);
|
|
assert.equal(findProjectRootUpward(join(root, "random")), null);
|
|
});
|
|
|
|
test("upward discovery: max depth respected", () => {
|
|
const root = scratch();
|
|
write(root, "Data/Mod.xml", "<AssetDeclaration/>");
|
|
let deep = root;
|
|
for (let i = 0; i < 14; i++) {
|
|
deep = join(deep, `level${i}`);
|
|
mkdirSync(deep);
|
|
}
|
|
assert.equal(findProjectRootUpward(deep, 12), null);
|
|
assert.equal(findProjectRootUpward(deep, 20), resolve(root));
|
|
});
|
|
|
|
test("upward discovery from a single file", () => {
|
|
const root = scratch();
|
|
write(root, "Data/additionalmaps/mapmetadata_Maps.xml", "<MapMetadata/>");
|
|
const file = write(root, "Data/Units/Unit.xml", "<AssetDeclaration/>");
|
|
assert.equal(findProjectRootForFile(file), resolve(root));
|
|
});
|
|
|
|
test("discoverProjects: sibling mods in a container", () => {
|
|
const container = scratch();
|
|
write(container, "ModA/Data/Mod.xml", "<AssetDeclaration/>");
|
|
write(
|
|
container,
|
|
"ModB/Data/additionalmaps/mapmetadata_B.xml",
|
|
"<MapMetadata/>",
|
|
);
|
|
const found = discoverProjects(container).map((p) => resolve(p));
|
|
assert.equal(found.length, 2);
|
|
assert.ok(found.includes(resolve(join(container, "ModA"))));
|
|
assert.ok(found.includes(resolve(join(container, "ModB"))));
|
|
});
|
|
|
|
test("discoverProjects: SDK-style deep layout (mods/mods/corona)", () => {
|
|
const container = scratch();
|
|
write(
|
|
container,
|
|
"mods/mods/corona/Data/Mod.xml",
|
|
"<AssetDeclaration/>",
|
|
);
|
|
const found = discoverProjects(container).map((p) => resolve(p));
|
|
assert.deepEqual(found, [resolve(join(container, "mods", "mods", "corona"))]);
|
|
});
|
|
|
|
test("discoverProjects: skips known non-mod directories", () => {
|
|
const container = scratch();
|
|
write(
|
|
container,
|
|
"node_modules/FakeMod/Data/Mod.xml",
|
|
"<AssetDeclaration/>",
|
|
);
|
|
write(container, ".git/Data/Mod.xml", "<AssetDeclaration/>");
|
|
assert.deepEqual(discoverProjects(container), []);
|
|
});
|
|
|
|
test("discoverProjects: de-duplicates and stops at a root", () => {
|
|
const container = scratch();
|
|
write(container, "ModA/Data/Mod.xml", "<AssetDeclaration/>");
|
|
write(container, "ModA/Inner/Data/Mod.xml", "<AssetDeclaration/>");
|
|
const first = discoverProjects(container);
|
|
const second = discoverProjects(container);
|
|
assert.deepEqual(second, first);
|
|
assert.equal(first.length, 1);
|
|
assert.equal(resolve(first[0]), resolve(join(container, "ModA")));
|
|
});
|
|
|
|
test("nested roots: nearest ancestor wins upward", () => {
|
|
const outer = scratch();
|
|
write(outer, "Data/Mod.xml", "<AssetDeclaration/>");
|
|
const inner = join(outer, "Inner");
|
|
write(inner, "Data/Mod.xml", "<AssetDeclaration/>");
|
|
const file = write(inner, "Data/Units/Unit.xml", "<AssetDeclaration/>");
|
|
assert.equal(findProjectRootForFile(file), resolve(inner));
|
|
});
|