Parse a JSON config file
Declare the shape as an interface, read the file with node:fs, and annotate the result of JSON.parse. The annotation is the schema: ChadScript checks the parsed document against it.
The program
// @args: docs/examples/config/config.json
import { existsSync, readFileSync } from "node:fs";
interface Route {
path: string;
target: string;
}
interface Config {
name: string;
port: number;
debug?: boolean;
routes: Route[];
}
function loadConfig(path: string): Config {
if (!existsSync(path)) throw new Error(`no config file at ${path}`);
// The annotation is the schema: a document that does not match Config throws here.
const config: Config = JSON.parse(readFileSync(path, "utf8"));
return config;
}
function loadOrExit(path: string): Config {
try {
return loadConfig(path);
} catch (e) {
console.log(`bad config: ${String(e)}`);
process.exit(1);
}
}
const args = process.argv.slice(2);
const config = loadOrExit(args[0] ?? "config.json");
console.log(`${config.name} on port ${config.port}, debug ${config.debug ?? false}`);
for (const route of config.routes) {
console.log(` ${route.path.padEnd(10)} -> ${route.target}`);
}{
"name": "gateway",
"port": 8080,
"routes": [
{ "path": "/api", "target": "http://localhost:9000" },
{ "path": "/static", "target": "/var/www" }
]
}The @args comment on the first line tells the docs test suite which arguments to pass; it is an ordinary comment to the compiler.
Build and run
bin/chad build main.ts -o config
./config docs/examples/config/config.jsongateway on port 8080, debug false
/api -> http://localhost:9000
/static -> /var/wwwWhen the file does not match
If port were the string "8080", Node would hand back the wrong-shaped object and the program would print port 8080 by accident, or concatenate strings later. The compiled program throws at JSON.parse instead, so a Config value always has Config's fields. This is the one deliberate difference from Node; JSON shows it side by side.
loadOrExit above catches that (and a missing file) where the file is loaded, prints the reason and exits with status 1.
Next: build a CLI tool.