openclaw/scripts/lib/guard-inventory-utils.mjs
Peter Steinberger c8eab36020
refactor: run plugins and tooling without TypeScript 6 (#156829)
* refactor: remove TypeScript 6 from plugin runtime and tooling

Keep source plugin loading on Jiti and native module transforms, move development analysis and declaration builds to TypeScript 7, and preserve captured loader and input ownership contracts. Scaffolds compile to the same entry path without shipping a compiler in production.

* refactor: use native checker for restart preflight detection

* fix: keep test-directory docs out of native helper scans

* fix: preserve native compiler tooling across CI runtimes

Keep compiler-backed tests on the supported Node transport, preserve declared Windows aliases and partial-source analysis, and bind relocated SDK reports to their installed compiler. Retain strict package input admission and the existing fixture assertions.

* test: compare messaging guard diagnostics without sorting

* fix: bound native tooling memory and preserve bootstrap loading

* fix: bound native declaration builds with tsdown scheduling

* fix: retire compiler-only helpers from installed packages

* test: migrate routing import scan to native parser

* test(ui): wait for recovered model catalog receipt
2026-09-23 22:34:20 -07:00

97 lines
3 KiB
JavaScript

// Shared parsing, diffing, and reporting helpers for inventory guard scripts.
import path from "node:path";
import { visitModuleSpecifiers } from "./ts-guard-utils.mts";
/** Convert an absolute file path to a repo-relative POSIX path. */
export function normalizeRepoPath(repoRoot, filePath) {
return path.relative(repoRoot, filePath).split(path.sep).join("/");
}
/** Resolve a relative or absolute module specifier to a repo-relative path. */
export function resolveRepoSpecifier(repoRoot, specifier, importerFile) {
if (specifier.startsWith(".")) {
return normalizeRepoPath(repoRoot, path.resolve(path.dirname(importerFile), specifier));
}
if (specifier.startsWith("/")) {
return normalizeRepoPath(repoRoot, specifier);
}
return null;
}
/** Write one line to a stream without each caller repeating newline handling. */
export function writeLine(stream, text) {
stream.write(`${text}\n`);
}
/**
* Collect module references from a source file owned by the caller's parsing session.
*
* @param {import("typescript/unstable/ast").SourceFile} sourceFile
* @param {{
* acceptSpecifier?: (specifier: string) => boolean;
* }} [options]
* @returns {Array<{ kind: string; line: number; specifier: string }>}
*/
export function collectModuleReferencesFromSource(sourceFile, options = {}) {
const acceptSpecifier = options.acceptSpecifier ?? (() => true);
const references = [];
visitModuleSpecifiers(
sourceFile,
({ kind, specifier, specifierNode }) => {
if (acceptSpecifier(specifier)) {
references.push({
kind,
line:
sourceFile.getLineAndCharacterOfPosition(specifierNode.getStart(sourceFile)).line + 1,
specifier,
});
}
},
{ includeCommonJs: true, includeImportMetaUrl: true, includeImportTypes: true },
);
return references.toSorted(
(left, right) =>
left.line - right.line ||
left.kind.localeCompare(right.kind) ||
left.specifier.localeCompare(right.specifier),
);
}
/** Memoize an async factory while resetting the cache after failures. */
export function createCachedAsync(factory) {
let cachedPromise = null;
return async function getCachedValue() {
if (cachedPromise) {
return cachedPromise;
}
cachedPromise = factory();
try {
return await cachedPromise;
} catch (error) {
cachedPromise = null;
throw error;
}
};
}
/** Format grouped inventory entries for human-readable guard output. */
export function formatGroupedInventoryHuman(params, inventory) {
if (inventory.length === 0) {
return `${params.rule}\n${params.cleanMessage}`;
}
const lines = [params.rule, params.inventoryTitle];
let activeFile = "";
for (const entry of inventory) {
if (entry.file !== activeFile) {
activeFile = entry.file;
lines.push(activeFile);
}
lines.push(` - line ${entry.line} [${entry.kind}] ${entry.reason}`);
lines.push(` specifier: ${entry.specifier}`);
lines.push(` resolved: ${entry.resolvedPath}`);
}
return lines.join("\n");
}