
























@@ -1,6 +1,7 @@
11#!/usr/bin/env node
22import { existsSync, readdirSync, readFileSync } from "node:fs";
33import { join, relative, resolve } from "node:path";
4+import { fileURLToPath } from "node:url";
45import {
56pluginSdkEntrypoints,
67publicPluginOwnedSdkEntrypoints,
@@ -48,6 +49,12 @@ type CompatDebtRecord = {
4849 eligibleForRemoval: boolean;
4950};
505152+type WorkspaceTextFile = {
53+file: string;
54+ relativeFile: string;
55+ source: string;
56+};
57+5158type ReservedSdkImport = {
5259file: string;
5360 specifier: string;
@@ -114,6 +121,12 @@ type BoundaryReportSummary = {
114121};
115122};
116123124+export type PluginBoundaryReportResult = {
125+stdout: string;
126+ stderr: string;
127+ exitCode: number;
128+};
129+117130function collectTextFiles(dir: string): string[] {
118131const files: string[] = [];
119132if (!existsSync(dir)) {
@@ -145,6 +158,14 @@ function repoRelative(file: string): string {
145158return relative(REPO_ROOT, file).replaceAll("\\", "/");
146159}
147160161+function collectWorkspaceTextFileSources(): WorkspaceTextFile[] {
162+return collectWorkspaceTextFiles().map((file) => ({
163+ file,
164+relativeFile: repoRelative(file),
165+source: readFileSync(file, "utf8"),
166+}));
167+}
168+148169function isDocsFile(file: string): boolean {
149170return file.startsWith("docs/") || file === "README.md";
150171}
@@ -243,15 +264,13 @@ function extractCompatTokens(record: PluginCompatRecord): string[] {
243264return [...tokens].toSorted();
244265}
245266246-function collectReferenceFiles(files: readonly string[], tokens: readonly string[]) {
267+function collectReferenceFiles(files: readonly WorkspaceTextFile[], tokens: readonly string[]) {
247268const codeReferenceFiles = new Set<string>();
248269const docReferenceFiles = new Set<string>();
249-for (const file of files) {
250-const relativeFile = repoRelative(file);
270+for (const { relativeFile, source } of files) {
251271if (relativeFile === "src/plugins/compat/registry.ts") {
252272continue;
253273}
254-const source = readFileSync(file, "utf8");
255274if (!tokens.some((token) => source.includes(token))) {
256275continue;
257276}
@@ -267,11 +286,18 @@ function collectReferenceFiles(files: readonly string[], tokens: readonly string
267286};
268287}
269288270-function collectCompatDebt(files: readonly string[], today = new Date()): CompatDebtRecord[] {
289+function collectCompatDebt(
290+files: readonly WorkspaceTextFile[],
291+today = new Date(),
292+options: { includeReferenceFiles?: boolean } = {},
293+): CompatDebtRecord[] {
271294return PLUGIN_COMPAT_RECORDS.filter((record) => record.status === "deprecated")
272295.map((record) => {
273296const tokens = extractCompatTokens(record);
274-const references = collectReferenceFiles(files, tokens);
297+const references =
298+options.includeReferenceFiles === false
299+ ? { codeReferenceFiles: [], docReferenceFiles: [] }
300+ : collectReferenceFiles(files, tokens);
275301const eligibleForRemoval = record.removeAfter
276302 ? new Date(`${record.removeAfter}T00:00:00Z`) <= today
277303 : false;
@@ -297,13 +323,11 @@ function collectCompatDebt(files: readonly string[], today = new Date()): Compat
297323);
298324}
299325300-function collectReservedSdkImports(files: readonly string[]): ReservedSdkImport[] {
326+function collectReservedSdkImports(files: readonly WorkspaceTextFile[]): ReservedSdkImport[] {
301327const reserved = new Set<string>(reservedBundledPluginSdkEntrypoints);
302328const pluginIds = collectBundledPluginIds();
303329const imports: ReservedSdkImport[] = [];
304-for (const file of files) {
305-const relativeFile = repoRelative(file);
306-const source = readFileSync(file, "utf8");
330+for (const { relativeFile, source } of files) {
307331for (const match of source.matchAll(PLUGIN_SDK_SPECIFIER_PATTERN)) {
308332const specifier = match[1];
309333const subpath = match[2];
@@ -325,18 +349,18 @@ function collectReservedSdkImports(files: readonly string[]): ReservedSdkImport[
325349);
326350}
327351328-function collectMemoryHostBoundary(files: readonly string[]): BoundaryReport["memoryHostSdk"] {
352+function collectMemoryHostBoundary(
353+files: readonly WorkspaceTextFile[],
354+): BoundaryReport["memoryHostSdk"] {
329355const packageJson = JSON.parse(
330356readFileSync(resolve(REPO_ROOT, "packages/memory-host-sdk/package.json"), "utf8"),
331357) as { private?: boolean; exports?: Record<string, string> };
332358const sourceBridgeFiles: string[] = [];
333359const packageCoreReferenceFiles = new Set<string>();
334-for (const file of files) {
335-const relativeFile = repoRelative(file);
360+for (const { relativeFile, source } of files) {
336361if (!relativeFile.startsWith("packages/memory-host-sdk/src/")) {
337362continue;
338363}
339-const source = readFileSync(file, "utf8");
340364if (source.includes("src/memory-host-sdk/")) {
341365sourceBridgeFiles.push(relativeFile);
342366}
@@ -419,12 +443,12 @@ function buildSummary(report: BoundaryReport, owner?: string): BoundaryReportSum
419443};
420444}
421445422-function buildReport(options: Pick<CliOptions, "owner"> = {}): BoundaryReport {
423-const files = collectWorkspaceTextFiles();
446+function buildReport(options: Pick<CliOptions, "owner" | "summary"> = {}): BoundaryReport {
447+const files = collectWorkspaceTextFileSources();
424448const pluginIds = collectBundledPluginIds();
425-const compatRecords = collectCompatDebt(files).filter((record) =>
426-matchesOwner(options.owner, record.owner),
427-);
449+const compatRecords = collectCompatDebt(files, new Date(), {
450+includeReferenceFiles: !options.summary,
451+}).filter((record) => matchesOwner(options.owner, record.owner));
428452const reservedImports = collectReservedSdkImports(files).filter(
429453(entry) =>
430454matchesOwner(options.owner, entry.owner) || matchesOwner(options.owner, entry.consumerOwner),
@@ -539,35 +563,51 @@ function collectFailures(report: BoundaryReport, options: CliOptions): string[]
539563return failures;
540564}
541565542-let options: CliOptions;
543-try {
544-options = parseArgs(process.argv.slice(2));
545-} catch (error) {
546-const message = error instanceof Error ? error.message : String(error);
547-process.stderr.write(`${message}\n\n${renderHelp()}\n`);
548-process.exitCode = 2;
549-process.exit();
550-}
566+export function createPluginBoundaryReport(args: readonly string[]): PluginBoundaryReportResult {
567+ const options = parseArgs(args);
568+if (options.help) {
569+ return {
570+ stdout: `${renderHelp()}\n`,
571+ stderr: "",
572+ exitCode: 0,
573+ };
574+ }
551575552-if (options.help) {
553-process.stdout.write(`${renderHelp()}\n`);
554-process.exit();
576+const report = buildReport(options);
577+const summary = buildSummary(report, options.owner);
578+const body = options.json
579+ ? JSON.stringify(options.summary ? summary : report, null, 2)
580+ : options.summary
581+ ? renderSummaryText(summary)
582+ : renderText(report, options.owner);
583+const failures = collectFailures(report, options);
584+return {
585+stdout: `${body}\n`,
586+stderr:
587+failures.length > 0
588+ ? `${failures.map((failure) => `plugin-boundary-report: ${failure}`).join("\n")}\n`
589+ : "",
590+exitCode: failures.length > 0 ? 1 : 0,
591+};
555592}
556593557-const report = buildReport(options);
558-const summary = buildSummary(report, options.owner);
559-if (options.json) {
560-process.stdout.write(`${JSON.stringify(options.summary ? summary : report, null, 2)}\n`);
561-} else if (options.summary) {
562-process.stdout.write(`${renderSummaryText(summary)}\n`);
563-} else {
564- process.stdout.write(`${renderText(report, options.owner)}\n`);
594+function runPluginBoundaryReportCli(args: readonly string[]): void {
595+let result: PluginBoundaryReportResult;
596+try {
597+result = createPluginBoundaryReport(args);
598+} catch (error) {
599+const message = error instanceof Error ? error.message : String(error);
600+process.stderr.write(`${message}\n\n${renderHelp()}\n`);
601+process.exitCode = 2;
602+return;
603+}
604+process.stdout.write(result.stdout);
605+if (result.stderr) {
606+process.stderr.write(result.stderr);
607+}
608+process.exitCode = result.exitCode;
565609}
566610567-const failures = collectFailures(report, options);
568-if (failures.length > 0) {
569-process.stderr.write(
570-`${failures.map((failure) => `plugin-boundary-report: ${failure}`).join("\n")}\n`,
571-);
572-process.exitCode = 1;
611+if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
612+runPluginBoundaryReportCli(process.argv.slice(2));
573613}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。