mirror of https://github.com/openclaw/openclaw.git
fix: unblock runtime-api smoke checks
This commit is contained in:
parent
7ffe7e4822
commit
b4e392cf9d
|
|
@ -1,6 +1,7 @@
|
|||
import { execFileSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import ts from "typescript";
|
||||
|
||||
const JITI_EXTENSIONS = [
|
||||
".ts",
|
||||
|
|
@ -16,28 +17,171 @@ const JITI_EXTENSIONS = [
|
|||
] as const;
|
||||
|
||||
const PLUGIN_SDK_SPECIFIER_PREFIX = "openclaw/plugin-sdk/";
|
||||
const SOURCE_MODULE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts"] as const;
|
||||
|
||||
function collectPluginSdkDistAliases(params: {
|
||||
type SourceModuleRef = {
|
||||
specifier: string;
|
||||
typeOnly: boolean;
|
||||
};
|
||||
|
||||
function listPluginSdkExportedSubpaths(root: string): string[] {
|
||||
const packageJsonPath = path.join(root, "package.json");
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as {
|
||||
exports?: Record<string, unknown>;
|
||||
};
|
||||
return Object.keys(packageJson.exports ?? {})
|
||||
.filter((key) => key.startsWith("./plugin-sdk/"))
|
||||
.map((key) => key.slice("./plugin-sdk/".length));
|
||||
}
|
||||
|
||||
function resolvePluginSdkAliasTarget(root: string, subpath: string): string | null {
|
||||
const distCandidate = path.join(root, "dist", "plugin-sdk", `${subpath}.js`);
|
||||
if (existsSync(distCandidate)) {
|
||||
return distCandidate;
|
||||
}
|
||||
|
||||
for (const ext of SOURCE_MODULE_EXTENSIONS) {
|
||||
const srcCandidate = path.join(root, "src", "plugin-sdk", `${subpath}${ext}`);
|
||||
if (existsSync(srcCandidate)) {
|
||||
return srcCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function resolveLocalModulePath(filePath: string, specifier: string): string | null {
|
||||
const basePath = path.resolve(path.dirname(filePath), specifier);
|
||||
const candidates = new Set<string>([basePath]);
|
||||
|
||||
for (const ext of SOURCE_MODULE_EXTENSIONS) {
|
||||
candidates.add(`${basePath}${ext}`);
|
||||
}
|
||||
|
||||
if (/\.[cm]?[jt]sx?$/u.test(basePath)) {
|
||||
const withoutExt = basePath.replace(/\.[cm]?[jt]sx?$/u, "");
|
||||
for (const ext of SOURCE_MODULE_EXTENSIONS) {
|
||||
candidates.add(`${withoutExt}${ext}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const ext of SOURCE_MODULE_EXTENSIONS) {
|
||||
candidates.add(path.join(basePath, `index${ext}`));
|
||||
}
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function collectSourceModuleRefs(filePath: string): SourceModuleRef[] {
|
||||
const sourceText = readFileSync(filePath, "utf8");
|
||||
const sourceFile = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
|
||||
const refs: SourceModuleRef[] = [];
|
||||
|
||||
for (const statement of sourceFile.statements) {
|
||||
if (ts.isImportDeclaration(statement)) {
|
||||
const specifier =
|
||||
statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)
|
||||
? statement.moduleSpecifier.text
|
||||
: undefined;
|
||||
if (specifier) {
|
||||
refs.push({
|
||||
specifier,
|
||||
typeOnly: Boolean(statement.importClause?.isTypeOnly),
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ts.isExportDeclaration(statement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const specifier =
|
||||
statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)
|
||||
? statement.moduleSpecifier.text
|
||||
: undefined;
|
||||
if (!specifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const typeOnly = Boolean(
|
||||
statement.isTypeOnly ||
|
||||
(statement.exportClause &&
|
||||
ts.isNamedExports(statement.exportClause) &&
|
||||
statement.exportClause.elements.length > 0 &&
|
||||
statement.exportClause.elements.every((element) => element.isTypeOnly)),
|
||||
);
|
||||
|
||||
refs.push({ specifier, typeOnly });
|
||||
}
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
function collectPluginSdkAliases(params: {
|
||||
modulePath: string;
|
||||
root: string;
|
||||
}): Record<string, string> {
|
||||
const sourceText = readFileSync(params.modulePath, "utf8");
|
||||
const specifiers = new Set<string>();
|
||||
const realSpecifiers = new Set<string>();
|
||||
const stubSpecifiers = new Set<string>();
|
||||
const visitedFiles = new Set<string>();
|
||||
const stubPath = path.join(params.root, "test", "helpers", "extensions", "plugin-sdk-stub.cjs");
|
||||
|
||||
for (const match of sourceText.matchAll(/["'](openclaw\/plugin-sdk(?:\/[^"']+)?)["']/g)) {
|
||||
const specifier = match[1];
|
||||
if (!specifier?.startsWith(PLUGIN_SDK_SPECIFIER_PREFIX)) {
|
||||
continue;
|
||||
function visitModule(filePath: string, rootModule: boolean): void {
|
||||
if (visitedFiles.has(filePath)) {
|
||||
return;
|
||||
}
|
||||
visitedFiles.add(filePath);
|
||||
|
||||
for (const ref of collectSourceModuleRefs(filePath)) {
|
||||
if (ref.specifier.startsWith(PLUGIN_SDK_SPECIFIER_PREFIX)) {
|
||||
if (rootModule && !ref.typeOnly) {
|
||||
realSpecifiers.add(ref.specifier);
|
||||
const subpath = ref.specifier.slice(PLUGIN_SDK_SPECIFIER_PREFIX.length);
|
||||
const target = resolvePluginSdkAliasTarget(params.root, subpath);
|
||||
if (target?.endsWith(".ts")) {
|
||||
visitModule(target, false);
|
||||
}
|
||||
} else {
|
||||
stubSpecifiers.add(ref.specifier);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ref.specifier.startsWith(".")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const resolved = resolveLocalModulePath(filePath, ref.specifier);
|
||||
if (resolved) {
|
||||
visitModule(resolved, false);
|
||||
}
|
||||
}
|
||||
specifiers.add(specifier);
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Array.from(specifiers, (specifier) => {
|
||||
visitModule(params.modulePath, true);
|
||||
|
||||
const aliasEntries = new Map<string, string>();
|
||||
for (const specifier of listPluginSdkExportedSubpaths(params.root).map(
|
||||
(subpath) => `${PLUGIN_SDK_SPECIFIER_PREFIX}${subpath}`,
|
||||
)) {
|
||||
if (realSpecifiers.has(specifier)) {
|
||||
const subpath = specifier.slice(PLUGIN_SDK_SPECIFIER_PREFIX.length);
|
||||
return [specifier, path.join(params.root, "dist", "plugin-sdk", `${subpath}.js`)];
|
||||
}),
|
||||
);
|
||||
aliasEntries.set(specifier, resolvePluginSdkAliasTarget(params.root, subpath) ?? stubPath);
|
||||
continue;
|
||||
}
|
||||
if (stubSpecifiers.has(specifier)) {
|
||||
aliasEntries.set(specifier, stubPath);
|
||||
}
|
||||
}
|
||||
|
||||
return Object.fromEntries(aliasEntries);
|
||||
}
|
||||
|
||||
export function loadRuntimeApiExportTypesViaJiti(params: {
|
||||
|
|
@ -47,7 +191,7 @@ export function loadRuntimeApiExportTypesViaJiti(params: {
|
|||
}): Record<string, string> {
|
||||
const root = process.cwd();
|
||||
const alias = {
|
||||
...collectPluginSdkDistAliases({ modulePath: params.modulePath, root }),
|
||||
...collectPluginSdkAliases({ modulePath: params.modulePath, root }),
|
||||
...params.additionalAliases,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
"use strict";
|
||||
|
||||
let stub;
|
||||
|
||||
stub = new Proxy(
|
||||
function pluginSdkStub() {
|
||||
return stub;
|
||||
},
|
||||
{
|
||||
apply() {
|
||||
return stub;
|
||||
},
|
||||
construct() {
|
||||
return stub;
|
||||
},
|
||||
get(_target, prop) {
|
||||
if (prop === "__esModule") {
|
||||
return true;
|
||||
}
|
||||
if (prop === "default") {
|
||||
return stub;
|
||||
}
|
||||
if (prop === "then") {
|
||||
return undefined;
|
||||
}
|
||||
if (prop === Symbol.toPrimitive) {
|
||||
return () => "";
|
||||
}
|
||||
if (prop === "toJSON") {
|
||||
return () => undefined;
|
||||
}
|
||||
if (prop === "toString") {
|
||||
return () => "";
|
||||
}
|
||||
if (prop === "valueOf") {
|
||||
return () => 0;
|
||||
}
|
||||
return stub;
|
||||
},
|
||||
ownKeys() {
|
||||
return [];
|
||||
},
|
||||
getOwnPropertyDescriptor(_target, prop) {
|
||||
if (prop === "__esModule") {
|
||||
return {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: true,
|
||||
writable: false,
|
||||
};
|
||||
}
|
||||
if (prop === "default") {
|
||||
return {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: stub,
|
||||
writable: false,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
module.exports = stub;
|
||||
Loading…
Reference in New Issue