mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:20:42 +00:00
perf: cache guard inventory checks
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const scanRoots = resolveSourceRoots(repoRoot, ["src/plugin-sdk", "src/plugins/runtime"]);
|
||||
let architectureSmellsPromise;
|
||||
|
||||
function compareEntries(left, right) {
|
||||
return (
|
||||
@@ -195,21 +196,32 @@ function scanRuntimeServiceLocatorSmells(sourceFile, filePath) {
|
||||
}
|
||||
|
||||
export async function collectArchitectureSmells() {
|
||||
const files = (await collectTypeScriptFilesFromRoots(scanRoots)).toSorted((left, right) =>
|
||||
normalizeRepoPath(repoRoot, left).localeCompare(normalizeRepoPath(repoRoot, right)),
|
||||
);
|
||||
return await collectTypeScriptInventory({
|
||||
ts,
|
||||
files,
|
||||
compareEntries,
|
||||
collectEntries(sourceFile, filePath) {
|
||||
return [
|
||||
...scanPluginSdkExtensionFacadeSmells(sourceFile, filePath),
|
||||
...scanRuntimeTypeImplementationSmells(sourceFile, filePath),
|
||||
...scanRuntimeServiceLocatorSmells(sourceFile, filePath),
|
||||
];
|
||||
},
|
||||
});
|
||||
if (!architectureSmellsPromise) {
|
||||
architectureSmellsPromise = (async () => {
|
||||
const files = (await collectTypeScriptFilesFromRoots(scanRoots)).toSorted((left, right) =>
|
||||
normalizeRepoPath(repoRoot, left).localeCompare(normalizeRepoPath(repoRoot, right)),
|
||||
);
|
||||
return await collectTypeScriptInventory({
|
||||
ts,
|
||||
files,
|
||||
compareEntries,
|
||||
collectEntries(sourceFile, filePath) {
|
||||
return [
|
||||
...scanPluginSdkExtensionFacadeSmells(sourceFile, filePath),
|
||||
...scanRuntimeTypeImplementationSmells(sourceFile, filePath),
|
||||
...scanRuntimeServiceLocatorSmells(sourceFile, filePath),
|
||||
];
|
||||
},
|
||||
});
|
||||
})();
|
||||
try {
|
||||
return await architectureSmellsPromise;
|
||||
} catch (error) {
|
||||
architectureSmellsPromise = undefined;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return await architectureSmellsPromise;
|
||||
}
|
||||
|
||||
function formatInventoryHuman(inventory) {
|
||||
|
||||
@@ -101,9 +101,12 @@ async function collectParsedExtensionSourceFiles() {
|
||||
if (!parsedExtensionSourceFilesPromise) {
|
||||
parsedExtensionSourceFilesPromise = (async () => {
|
||||
const files = await collectExtensionSourceFiles(extensionsRoot);
|
||||
return await Promise.all(
|
||||
const parsed = await Promise.all(
|
||||
files.map(async (filePath) => {
|
||||
const source = await fs.readFile(filePath, "utf8");
|
||||
if (!mayContainModuleSpecifier(source)) {
|
||||
return null;
|
||||
}
|
||||
const scriptKind =
|
||||
filePath.endsWith(".tsx") || filePath.endsWith(".jsx")
|
||||
? ts.ScriptKind.TSX
|
||||
@@ -120,11 +123,20 @@ async function collectParsedExtensionSourceFiles() {
|
||||
};
|
||||
}),
|
||||
);
|
||||
return parsed.filter(Boolean);
|
||||
})();
|
||||
}
|
||||
return await parsedExtensionSourceFilesPromise;
|
||||
}
|
||||
|
||||
function mayContainModuleSpecifier(source) {
|
||||
return (
|
||||
/\bfrom\s*["']/.test(source) ||
|
||||
/\bimport\s*(?:\(|["']|type\b|[\w*{])/.test(source) ||
|
||||
/\bexport\s*(?:type\s+)?(?:\*|{)[^;\n]*\bfrom\s*["']/.test(source)
|
||||
);
|
||||
}
|
||||
|
||||
function resolveExtensionRoot(filePath) {
|
||||
const relativePath = normalizeRepoPath(repoRoot, filePath);
|
||||
const segments = relativePath.split("/");
|
||||
|
||||
@@ -32,40 +32,53 @@ const suspiciousPatterns = [
|
||||
/id:\s*"firecrawl"/,
|
||||
];
|
||||
|
||||
let webFetchProviderViolationsPromise;
|
||||
|
||||
export async function collectWebFetchProviderBoundaryViolations() {
|
||||
const violations = [];
|
||||
const files = await collectSourceFileContents({
|
||||
repoRoot,
|
||||
scanRoots: ["src"],
|
||||
scanExtensions,
|
||||
ignoredDirNames,
|
||||
});
|
||||
for (const { relativeFile, content } of files) {
|
||||
if (
|
||||
allowedFiles.has(relativeFile) ||
|
||||
relativeFile.includes(".test.") ||
|
||||
relativeFile.includes("test-support")
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const lines = content.split(/\r?\n/);
|
||||
for (const [index, line] of lines.entries()) {
|
||||
if (!line.includes("firecrawl") && !line.includes("Firecrawl")) {
|
||||
continue;
|
||||
}
|
||||
if (!suspiciousPatterns.some((pattern) => pattern.test(line))) {
|
||||
continue;
|
||||
}
|
||||
violations.push({
|
||||
file: relativeFile,
|
||||
line: index + 1,
|
||||
reason: "core web-fetch runtime/tooling contains Firecrawl-specific fetch logic",
|
||||
if (!webFetchProviderViolationsPromise) {
|
||||
webFetchProviderViolationsPromise = (async () => {
|
||||
const violations = [];
|
||||
const files = await collectSourceFileContents({
|
||||
repoRoot,
|
||||
scanRoots: ["src"],
|
||||
scanExtensions,
|
||||
ignoredDirNames,
|
||||
});
|
||||
for (const { relativeFile, content } of files) {
|
||||
if (
|
||||
allowedFiles.has(relativeFile) ||
|
||||
relativeFile.includes(".test.") ||
|
||||
relativeFile.includes("test-support")
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const lines = content.split(/\r?\n/);
|
||||
for (const [index, line] of lines.entries()) {
|
||||
if (!line.includes("firecrawl") && !line.includes("Firecrawl")) {
|
||||
continue;
|
||||
}
|
||||
if (!suspiciousPatterns.some((pattern) => pattern.test(line))) {
|
||||
continue;
|
||||
}
|
||||
violations.push({
|
||||
file: relativeFile,
|
||||
line: index + 1,
|
||||
reason: "core web-fetch runtime/tooling contains Firecrawl-specific fetch logic",
|
||||
});
|
||||
}
|
||||
}
|
||||
return violations.toSorted(
|
||||
(left, right) => left.file.localeCompare(right.file) || left.line - right.line,
|
||||
);
|
||||
})();
|
||||
try {
|
||||
return await webFetchProviderViolationsPromise;
|
||||
} catch (error) {
|
||||
webFetchProviderViolationsPromise = undefined;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return violations.toSorted(
|
||||
(left, right) => left.file.localeCompare(right.file) || left.line - right.line,
|
||||
);
|
||||
return await webFetchProviderViolationsPromise;
|
||||
}
|
||||
|
||||
export async function main(argv = process.argv.slice(2), io) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const parsedTypeScriptSourceCache = new Map();
|
||||
const sourceTextCache = new Map();
|
||||
|
||||
export function normalizeRepoPath(repoRoot, filePath) {
|
||||
return path.relative(repoRoot, filePath).split(path.sep).join("/");
|
||||
@@ -118,7 +119,11 @@ export async function collectTypeScriptInventory(params) {
|
||||
const cacheKey = `${scriptKind}:${filePath}`;
|
||||
let sourceFile = parsedTypeScriptSourceCache.get(cacheKey);
|
||||
if (!sourceFile) {
|
||||
const source = await fs.readFile(filePath, "utf8");
|
||||
let source = sourceTextCache.get(filePath);
|
||||
if (source === undefined) {
|
||||
source = await fs.readFile(filePath, "utf8");
|
||||
sourceTextCache.set(filePath, source);
|
||||
}
|
||||
if (params.shouldParseSource && !params.shouldParseSource(source, filePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user