#!/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 "$@"