Add CLI dispatcher and bin entry
Top-level run(argv,{out,err}) accepts an injectable output sink so
handlers can be unit-tested without spawning a subprocess. Handler
map is empty for now; subcommands are wired in as they land.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
bin/mapper.js
Normal file
5
bin/mapper.js
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const { run } = require('../src/cli');
|
||||
process.exit(run(process.argv.slice(2), { out: process.stdout, err: process.stderr }));
|
||||
43
src/cli.js
Normal file
43
src/cli.js
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
const USAGE = `usage: sporel-mapper <subcommand> [args...]
|
||||
|
||||
Subcommands:
|
||||
encode <atlas_idx> <tile_id> [rotation]
|
||||
Print the packed-u32 GID for the given components.
|
||||
|
||||
decode <gid>
|
||||
Print the (atlas, tile, rotation) components of a GID.
|
||||
|
||||
build <spec.txt> <out.json> [--atlas-dir <path> ...]
|
||||
Compile a text-DSL map spec into a v2 map JSON file.
|
||||
|
||||
inspect <map.json> [--atlas-dir <path> ...]
|
||||
Print a human-readable stats report for a v2 map.
|
||||
|
||||
Options:
|
||||
-h, --help Show this message.
|
||||
`;
|
||||
|
||||
function run(argv, { out, err }) {
|
||||
if (argv.length === 0) {
|
||||
err.write(USAGE);
|
||||
return 1;
|
||||
}
|
||||
const [head, ...rest] = argv;
|
||||
if (head === '-h' || head === '--help') {
|
||||
out.write(USAGE);
|
||||
return 0;
|
||||
}
|
||||
const handlers = {
|
||||
// populated in subsequent tasks
|
||||
};
|
||||
const handler = handlers[head];
|
||||
if (!handler) {
|
||||
err.write(`unknown subcommand: ${head}\n${USAGE}`);
|
||||
return 1;
|
||||
}
|
||||
return handler.run(rest, { out, err });
|
||||
}
|
||||
|
||||
module.exports = { run };
|
||||
46
tests/cli.test.js
Normal file
46
tests/cli.test.js
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { run } = require('../src/cli');
|
||||
|
||||
class Sink {
|
||||
constructor() { this.chunks = []; }
|
||||
write(s) { this.chunks.push(s); }
|
||||
get text() { return this.chunks.join(''); }
|
||||
}
|
||||
|
||||
test('cli: no args prints usage to stderr, exit 1', () => {
|
||||
const out = new Sink(); const err = new Sink();
|
||||
const code = run([], { out, err });
|
||||
assert.equal(code, 1);
|
||||
assert.match(err.text, /usage/i);
|
||||
assert.match(err.text, /encode/);
|
||||
assert.match(err.text, /decode/);
|
||||
assert.match(err.text, /build/);
|
||||
assert.match(err.text, /inspect/);
|
||||
assert.equal(out.text, '');
|
||||
});
|
||||
|
||||
test('cli: --help prints usage to stdout, exit 0', () => {
|
||||
const out = new Sink(); const err = new Sink();
|
||||
const code = run(['--help'], { out, err });
|
||||
assert.equal(code, 0);
|
||||
assert.match(out.text, /usage/i);
|
||||
assert.equal(err.text, '');
|
||||
});
|
||||
|
||||
test('cli: -h alias for --help', () => {
|
||||
const out = new Sink(); const err = new Sink();
|
||||
const code = run(['-h'], { out, err });
|
||||
assert.equal(code, 0);
|
||||
assert.match(out.text, /usage/i);
|
||||
});
|
||||
|
||||
test('cli: unknown subcommand goes to stderr, exit 1', () => {
|
||||
const out = new Sink(); const err = new Sink();
|
||||
const code = run(['frobnicate'], { out, err });
|
||||
assert.equal(code, 1);
|
||||
assert.match(err.text, /unknown subcommand/i);
|
||||
assert.match(err.text, /frobnicate/);
|
||||
});
|
||||
Reference in New Issue
Block a user