#!/bin/sh
# script/build: compile the TypeScript sources into dist/, then verify that
# the artifacts package.json advertises are among the files the compiler
# actually wrote. tsc reports success by exit status alone and knows nothing
# about the manifest, so without this step a green build can still ship a
# package whose main, types or bin resolve to nothing. Our own extension to
# scripts-to-rule-them-all.
set -eu

ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"

# Reads package.json, requires every declared entrypoint to exist, requires
# each bin entry to have kept its shebang, and makes the bin entries
# executable: tsc copies the shebang through but not the mode bits, and an
# installed CLI has to be runnable.
verify_entrypoints() {
    node -e '
const { readFileSync, statSync, chmodSync } = require("node:fs");

const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
const bins = Object.values(pkg.bin ?? {});
const fail = (message) => {
    console.error("build: " + message);
    process.exit(1);
};

for (const declared of [pkg.main, pkg.types, ...bins]) {
    if (!declared) continue;
    try {
        statSync(declared);
    } catch {
        fail("package.json declares " + declared + ", which the build did not produce");
    }
    console.log("build: verified " + declared);
}

for (const bin of bins) {
    const firstLine = readFileSync(bin, "utf-8").split("\n")[0];
    if (!firstLine.startsWith("#!")) {
        fail(bin + " lost its shebang, so it cannot be executed directly");
    }
    chmodSync(bin, 0o755);
    console.log("build: " + bin + " is executable (" + firstLine + ")");
}
'
}

main() {
    cd "$ROOT"
    yarn run tsc
    verify_entrypoints
}

main "$@"
