mirror of https://github.com/openclaw/openclaw.git
118 lines
3.8 KiB
TypeScript
118 lines
3.8 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { discoverOpenClawPlugins } from "../src/plugins/discovery.js";
|
|
import { collectFilesSync, isCodeFile, relativeToCwd } from "./check-file-utils.js";
|
|
|
|
// Match exact monolithic-root specifier in any code path:
|
|
// imports/exports, require/dynamic import, and test mocks (vi.mock/jest.mock).
|
|
const ROOT_IMPORT_PATTERN = /["']openclaw\/plugin-sdk["']/;
|
|
const LEGACY_COMPAT_IMPORT_PATTERN = /["']openclaw\/plugin-sdk\/compat["']/;
|
|
|
|
function hasMonolithicRootImport(content: string): boolean {
|
|
return ROOT_IMPORT_PATTERN.test(content);
|
|
}
|
|
|
|
function hasLegacyCompatImport(content: string): boolean {
|
|
return LEGACY_COMPAT_IMPORT_PATTERN.test(content);
|
|
}
|
|
|
|
function collectPluginSourceFiles(rootDir: string): string[] {
|
|
const srcDir = path.join(rootDir, "src");
|
|
if (!fs.existsSync(srcDir)) {
|
|
return [];
|
|
}
|
|
return collectFilesSync(srcDir, {
|
|
includeFile: (filePath) => isCodeFile(filePath),
|
|
skipDirNames: new Set(["node_modules", "dist", ".git", "coverage"]),
|
|
});
|
|
}
|
|
|
|
function collectSharedExtensionSourceFiles(): string[] {
|
|
return collectPluginSourceFiles(path.join(process.cwd(), "extensions", "shared"));
|
|
}
|
|
|
|
function collectBundledExtensionSourceFiles(): string[] {
|
|
const extensionsDir = path.join(process.cwd(), "extensions");
|
|
let entries: fs.Dirent[] = [];
|
|
try {
|
|
entries = fs.readdirSync(extensionsDir, { withFileTypes: true });
|
|
} catch {
|
|
return [];
|
|
}
|
|
|
|
const files: string[] = [];
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory() || entry.name === "shared") {
|
|
continue;
|
|
}
|
|
for (const srcFile of collectPluginSourceFiles(path.join(extensionsDir, entry.name))) {
|
|
files.push(srcFile);
|
|
}
|
|
}
|
|
return files;
|
|
}
|
|
|
|
function main() {
|
|
const discovery = discoverOpenClawPlugins({});
|
|
const bundledCandidates = discovery.candidates.filter((c) => c.origin === "bundled");
|
|
const filesToCheck = new Set<string>();
|
|
for (const candidate of bundledCandidates) {
|
|
filesToCheck.add(candidate.source);
|
|
for (const srcFile of collectPluginSourceFiles(candidate.rootDir)) {
|
|
filesToCheck.add(srcFile);
|
|
}
|
|
}
|
|
for (const sharedFile of collectSharedExtensionSourceFiles()) {
|
|
filesToCheck.add(sharedFile);
|
|
}
|
|
for (const extensionFile of collectBundledExtensionSourceFiles()) {
|
|
filesToCheck.add(extensionFile);
|
|
}
|
|
|
|
const monolithicOffenders: string[] = [];
|
|
const legacyCompatOffenders: string[] = [];
|
|
for (const entryFile of filesToCheck) {
|
|
let content = "";
|
|
try {
|
|
content = fs.readFileSync(entryFile, "utf8");
|
|
} catch {
|
|
continue;
|
|
}
|
|
if (hasMonolithicRootImport(content)) {
|
|
monolithicOffenders.push(entryFile);
|
|
}
|
|
if (hasLegacyCompatImport(content)) {
|
|
legacyCompatOffenders.push(entryFile);
|
|
}
|
|
}
|
|
|
|
if (monolithicOffenders.length > 0 || legacyCompatOffenders.length > 0) {
|
|
if (monolithicOffenders.length > 0) {
|
|
console.error("Bundled plugin source files must not import monolithic openclaw/plugin-sdk.");
|
|
for (const file of monolithicOffenders.toSorted()) {
|
|
console.error(`- ${relativeToCwd(file)}`);
|
|
}
|
|
}
|
|
if (legacyCompatOffenders.length > 0) {
|
|
console.error(
|
|
"Bundled plugin source files must not import legacy openclaw/plugin-sdk/compat.",
|
|
);
|
|
for (const file of legacyCompatOffenders.toSorted()) {
|
|
console.error(`- ${relativeToCwd(file)}`);
|
|
}
|
|
}
|
|
if (monolithicOffenders.length > 0 || legacyCompatOffenders.length > 0) {
|
|
console.error(
|
|
"Use openclaw/plugin-sdk/<domain> or openclaw/plugin-sdk/<channel> subpaths for bundled plugins; root and compat are legacy surfaces only.",
|
|
);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(
|
|
`OK: bundled plugin source files use scoped plugin-sdk subpaths (${filesToCheck.size} checked).`,
|
|
);
|
|
}
|
|
|
|
main();
|