#!/bin/sh
# script/build: compile the TypeScript sources into dist/, stamp the version
# script/version prints into it, 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 + ")");
}
'
}

# src/index.ts imports ../package.json for the version, which tsc copies to
# dist/package.json. The version script/version prints is written into that
# copy only; the repo's own package.json is left as it is.
stamp_version() {
    node -e '
const { readFileSync, writeFileSync } = require("node:fs");

const pkg = JSON.parse(readFileSync("dist/package.json", "utf-8"));
pkg.version = process.argv[1];
writeFileSync("dist/package.json", JSON.stringify(pkg, null, 4) + "\n");
' "$1"
}

# Running the built CLI proves the import resolves from dist/ and reports
# the stamped version.
verify_version() {
    built="$(node dist/bin/quak.js --version)"
    if [ "$built" != "$1" ]; then
        echo "build: dist/bin/quak.js reports $built, the build stamped $1" >&2
        exit 1
    fi
    echo "build: dist/bin/quak.js reports version $built"
}

main() {
    cd "$ROOT"
    # Own line, so that a failing script/version stops the build.
    version="$("$ROOT/script/version")"
    yarn run tsc
    stamp_version "$version"
    verify_entrypoints
    verify_version "$version"
}

main "$@"
