Skip to content

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 ​

ts
// @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}`);
}
json
{
  "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 ​

sh
bin/chad build main.ts -o config
./config docs/examples/config/config.json
text
gateway on port 8080, debug false
  /api       -> http://localhost:9000
  /static    -> /var/www

When 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.