Add atlas-spec loader with duplicate handling
loadAtlasDirs walks one level deep into each --atlas-dir, picks up */tiles.atlas.json, and builds bidirectional name/id maps. On duplicate atlas_id across paths the first registration wins and a warning string is returned (callers decide whether to print).
This commit is contained in:
65
src/atlas-loader.js
Normal file
65
src/atlas-loader.js
Normal file
@@ -0,0 +1,65 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
function loadAtlasSpec(specPath) {
|
||||
const raw = fs.readFileSync(specPath, 'utf8');
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch (e) {
|
||||
throw new Error(`${specPath}: invalid JSON (${e.message})`);
|
||||
}
|
||||
if (typeof parsed.atlas_id !== 'string' || parsed.atlas_id.length === 0) {
|
||||
throw new Error(`${specPath}: missing or non-string atlas_id`);
|
||||
}
|
||||
if (!Array.isArray(parsed.tiles)) {
|
||||
throw new Error(`${specPath}: missing tiles[]`);
|
||||
}
|
||||
const tilesByName = {};
|
||||
const tilesById = {};
|
||||
for (const tile of parsed.tiles) {
|
||||
if (typeof tile.id !== 'number' || !Number.isInteger(tile.id)) {
|
||||
throw new Error(`${specPath}: tile missing integer 'id'`);
|
||||
}
|
||||
if (typeof tile.name !== 'string' || tile.name.length === 0) {
|
||||
throw new Error(`${specPath}: tile id=${tile.id} missing 'name'`);
|
||||
}
|
||||
tilesByName[tile.name] = tile.id;
|
||||
tilesById[tile.id] = tile.name;
|
||||
}
|
||||
return { atlas_id: parsed.atlas_id, tilesByName, tilesById, specPath };
|
||||
}
|
||||
|
||||
function loadAtlasDirs(dirs) {
|
||||
const registry = {};
|
||||
const warnings = [];
|
||||
for (const dir of dirs) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
throw new Error(`no such directory: ${dir}`);
|
||||
}
|
||||
const stat = fs.statSync(dir);
|
||||
if (!stat.isDirectory()) {
|
||||
throw new Error(`not a directory: ${dir}`);
|
||||
}
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
const specPath = path.join(dir, entry.name, 'tiles.atlas.json');
|
||||
if (!fs.existsSync(specPath)) continue;
|
||||
const spec = loadAtlasSpec(specPath);
|
||||
if (registry[spec.atlas_id]) {
|
||||
warnings.push(
|
||||
`duplicate atlas_id '${spec.atlas_id}' at ${specPath}; ` +
|
||||
`keeping earlier registration from ${registry[spec.atlas_id].specPath}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
registry[spec.atlas_id] = spec;
|
||||
}
|
||||
}
|
||||
return { registry, warnings };
|
||||
}
|
||||
|
||||
module.exports = { loadAtlasDirs, loadAtlasSpec };
|
||||
Reference in New Issue
Block a user