#!/usr/bin/env node import { spawnSync } from "node:child_process"; import { statSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; // IDE diagnostics may contain source literals. Only check reviewed, non-secret inputs. export function checkFile(file, run = spawnSync) { const report = { file: resolve(file), version: null, ok: false, diagnostics: [] }; try { if (!statSync(report.file).isFile()) throw new Error("Target must be a regular file"); const options = { cwd: dirname(report.file), encoding: "utf8", shell: false, timeout: 30_000, maxBuffer: 1_048_576, windowsHide: true, }; function invoke(args) { const process = run("nu", ["--no-config-file", ...args], options); if (process.error || process.status !== 0 || process.signal) { // Do not echo stderr or spawn errors: either can contain sensitive data. throw new Error("Nu failed, was unavailable, or exceeded time/output limits"); } if (process.stderr?.trim()) throw new Error("Nu emitted stderr; validation is incomplete"); return process.stdout; } const version = invoke(["--version"]).trim(); if (!/^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/.test(version)) { throw new Error("Unrecognized Nu version output"); } report.version = version; const output = invoke(["--ide-check", "100", report.file]); for (const line of output.split(/\r?\n/).filter((line) => line.trim())) { let entry; try { entry = JSON.parse(line); } catch { throw new Error("Malformed Nu diagnostic JSON"); } if (entry?.type === "hint") continue; if (entry?.type !== "diagnostic" || typeof entry.message !== "string" || !["Error", "Warning", "Information", "Hint"].includes(entry.severity)) { throw new Error("Unsupported Nu diagnostic output"); } report.diagnostics.push(entry); } report.ok = !report.diagnostics.some((entry) => entry.severity === "Error"); } catch (error) { report.error = error.code ? "Cannot inspect target file" : error.message; } return report; } if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { const args = process.argv.slice(2); const report = args.length === 1 ? checkFile(args[0]) : { ok: false, error: "Usage: node check.mjs " }; console.log(JSON.stringify(report, null, 2)); process.exitCode = args.length !== 1 ? 2 : report.ok ? 0 : 1; }