Files
openclaw/scripts/stage-bundled-plugin-runtime.mjs

132 lines
4.0 KiB
JavaScript

import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { removePathIfExists } from "./runtime-postbuild-shared.mjs";
function symlinkType() {
return process.platform === "win32" ? "junction" : "dir";
}
function relativeSymlinkTarget(sourcePath, targetPath) {
const relativeTarget = path.relative(path.dirname(targetPath), sourcePath);
return relativeTarget || ".";
}
function symlinkPath(sourcePath, targetPath, type) {
fs.symlinkSync(relativeSymlinkTarget(sourcePath, targetPath), targetPath, type);
}
function shouldWrapRuntimeJsFile(sourcePath) {
return path.extname(sourcePath) === ".js";
}
function shouldCopyRuntimeFile(sourcePath) {
const relativePath = sourcePath.replace(/\\/g, "/");
return (
relativePath.endsWith("/package.json") ||
relativePath.endsWith("/openclaw.plugin.json") ||
relativePath.endsWith("/.codex-plugin/plugin.json") ||
relativePath.endsWith("/.claude-plugin/plugin.json") ||
relativePath.endsWith("/.cursor-plugin/plugin.json")
);
}
function writeRuntimeModuleWrapper(sourcePath, targetPath) {
const specifier = relativeSymlinkTarget(sourcePath, targetPath).replace(/\\/g, "/");
const normalizedSpecifier = specifier.startsWith(".") ? specifier : `./${specifier}`;
fs.writeFileSync(
targetPath,
[
`export * from ${JSON.stringify(normalizedSpecifier)};`,
`import * as module from ${JSON.stringify(normalizedSpecifier)};`,
"export default module.default;",
"",
].join("\n"),
"utf8",
);
}
function stagePluginRuntimeOverlay(sourceDir, targetDir) {
fs.mkdirSync(targetDir, { recursive: true });
for (const dirent of fs.readdirSync(sourceDir, { withFileTypes: true })) {
if (dirent.name === "node_modules") {
continue;
}
const sourcePath = path.join(sourceDir, dirent.name);
const targetPath = path.join(targetDir, dirent.name);
if (dirent.isDirectory()) {
stagePluginRuntimeOverlay(sourcePath, targetPath);
continue;
}
if (dirent.isSymbolicLink()) {
fs.symlinkSync(fs.readlinkSync(sourcePath), targetPath);
continue;
}
if (!dirent.isFile()) {
continue;
}
if (shouldWrapRuntimeJsFile(sourcePath)) {
writeRuntimeModuleWrapper(sourcePath, targetPath);
continue;
}
if (shouldCopyRuntimeFile(sourcePath)) {
fs.copyFileSync(sourcePath, targetPath);
continue;
}
symlinkPath(sourcePath, targetPath);
}
}
function linkPluginNodeModules(params) {
const runtimeNodeModulesDir = path.join(params.runtimePluginDir, "node_modules");
removePathIfExists(runtimeNodeModulesDir);
if (!fs.existsSync(params.sourcePluginNodeModulesDir)) {
return;
}
fs.symlinkSync(params.sourcePluginNodeModulesDir, runtimeNodeModulesDir, symlinkType());
}
export function stageBundledPluginRuntime(params = {}) {
const repoRoot = params.cwd ?? params.repoRoot ?? process.cwd();
const distRoot = path.join(repoRoot, "dist");
const runtimeRoot = path.join(repoRoot, "dist-runtime");
const sourceExtensionsRoot = path.join(repoRoot, "extensions");
const distExtensionsRoot = path.join(distRoot, "extensions");
const runtimeExtensionsRoot = path.join(runtimeRoot, "extensions");
if (!fs.existsSync(distExtensionsRoot)) {
removePathIfExists(runtimeRoot);
return;
}
removePathIfExists(runtimeRoot);
fs.mkdirSync(runtimeExtensionsRoot, { recursive: true });
for (const dirent of fs.readdirSync(distExtensionsRoot, { withFileTypes: true })) {
if (!dirent.isDirectory()) {
continue;
}
const distPluginDir = path.join(distExtensionsRoot, dirent.name);
const runtimePluginDir = path.join(runtimeExtensionsRoot, dirent.name);
const sourcePluginNodeModulesDir = path.join(sourceExtensionsRoot, dirent.name, "node_modules");
stagePluginRuntimeOverlay(distPluginDir, runtimePluginDir);
linkPluginNodeModules({
runtimePluginDir,
sourcePluginNodeModulesDir,
});
}
}
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
stageBundledPluginRuntime();
}