#!/bin/sh # The catalogue's TypeScript part of its own check (novox/hq issue 302's `mesh-check-also`), declared in # merge-check.sh's header and run by the build seat in the TypeScript toolchain, after the Go part. The media # catalogue's twin is issue 311. Before this, a TypeScript module's change ran nothing of its own before it # merged: the Go part said "NOT TESTED HERE", and a type error in the forge module would first have shown in # its walk. # # For every touched module that has a package.json, in a copy of it outside the clone: # # 1. its dependencies are the toolchain's own (/app/node_modules: the mesh's SDK, Node's types and what the # runtime carries), copied beside it, never fetched: the check holds no credential for the package # registry. A module that depends on a package the toolchain does not hold is said NOT CHECKED, by name; # 2. every source that is not a test is type-checked with the toolchain's compiler and the flags a build # compiles with — the same check the build makes, before the merge rather than in the walk; # 3. its tests (test/*.test.ts) run under Node's own runner with types stripped, as its own `npm test` does # after its build: the sources are compiled to dist/ and beside themselves first, so a test that imports # the compiled module, or a source that imports its sibling by its .js name, finds it. # # Said, never passed silently: a module with no tests is said NOT TESTED on every run that touches it. # # By hand: MESH_CHECK_MODULES=gitea sh merge-check-typescript.sh, in the toolchain image (it reads # /app/node_modules), or with TOOLCHAIN_MODULES pointing at a node_modules that holds the same. set -eu deps="${TOOLCHAIN_MODULES:-/app/node_modules}" tsc="$deps/typescript/bin/tsc" if [ ! -f "$tsc" ]; then echo "NOT CHECKED: no TypeScript compiler at $tsc — this part runs in the TypeScript toolchain" exit 1 fi # The modules the change reaches: the planner's answer (hq ADR 0238), and every module a changed file is in — # a change to a module's tests alone moves no build (hq ADR 0267), and its tests must still run. touched=$({ printf '%s\n' "${MESH_CHECK_MODULES:-}" | tr ',' '\n' | sed -e 's#^modules/##' -e '/\//d' printf '%s\n' "${MESH_CHECK_CHANGED:-}" | tr ',' '\n' | sed -n 's#^modules/\([^/]*\)/.*#\1#p' } | sed '/^$/d' | sort -u) work=$(mktemp -d) trap 'rm -rf "$work"' EXIT failed="" for m in $touched; do d="modules/$m" [ -f "$d/package.json" ] || continue copy="$work/$m" mkdir -p "$copy" (cd "$d" && tar --exclude=./node_modules --exclude=./dist -cf - .) | (cd "$copy" && tar -xf -) # What it depends on, against what the toolchain holds. missing=$(node -e ' const p = require(process.argv[1]); const have = require("fs").readdirSync(process.argv[2]); const scoped = (n) => n.startsWith("@") ? n.split("/")[0] : n; const names = Object.keys({ ...(p.dependencies || {}), ...(p.devDependencies || {}) }); const held = (n) => have.includes(n) || (n.startsWith("@") && have.includes(scoped(n)) && require("fs").existsSync(process.argv[2] + "/" + n)); console.log(names.filter((n) => n !== "typescript" && !held(n)).join(" "));' "$copy/package.json" "$deps") if [ -n "$missing" ]; then echo "NOT CHECKED: $d depends on $missing, which the toolchain does not hold and the check cannot fetch" continue fi mkdir -p "$copy/node_modules" for p in "$deps"/*; do case "$(basename "$p")" in typescript | esbuild | @esbuild) continue ;; esac cp -r "$p" "$copy/node_modules/" done sources=$(cd "$copy" && find . -name '*.ts' ! -name '*.d.ts' ! -path './test/*' ! -path './node_modules/*' ! -path './dist/*' | sort) if [ -n "$sources" ]; then echo "type-checking $d (TypeScript)" # shellcheck disable=SC2086 # the module's sources, none with a space in its name if ! (cd "$copy" && node "$tsc" --noEmit --module NodeNext --moduleResolution NodeNext --target ES2022 \ --types node $sources); then failed="$failed $m" continue fi else echo "$d has no TypeScript sources of its own to type-check: its tests read its files" fi if ! ls "$copy"/test/*.test.ts >/dev/null 2>&1; then echo "NOT TESTED: $d is TypeScript and has no tests of its own" continue fi if [ -n "$sources" ]; then # shellcheck disable=SC2086 (cd "$copy" && node "$tsc" --module NodeNext --moduleResolution NodeNext --target ES2022 --types node \ --rootDir . --outDir dist $sources && node "$tsc" --module NodeNext --moduleResolution NodeNext \ --target ES2022 --types node --rootDir . --outDir . $sources) fi echo "testing $d (TypeScript)" if ! (cd "$copy" && node --test --test-reporter=spec --experimental-strip-types \ --no-warnings=ExperimentalWarning test/*.test.ts); then failed="$failed $m" fi done if [ -n "$failed" ]; then echo "TypeScript modules that failed:$failed" exit 1 fi