docs: document script lib runtime package helpers

This commit is contained in:
Peter Steinberger
2026-06-04 23:05:22 -04:00
parent 44cd0ec13f
commit 3cf1bd22f9
5 changed files with 31 additions and 0 deletions

View File

@@ -1,3 +1,4 @@
// Builds package-local runtime dist files for publishable bundled plugins.
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
@@ -20,6 +21,7 @@ function readJsonFile(filePath) {
return JSON.parse(fs.readFileSync(filePath, "utf8"));
}
/** Return whether a plugin package is marked for npm publishing. */
export function isPublishablePluginPackage(packageJson) {
return packageJson.openclaw?.release?.publishToNpm === true;
}
@@ -91,6 +93,7 @@ function packageRelativePathExists(packageDir, relativePath) {
return fs.existsSync(path.join(packageDir, relativePath));
}
/** List extension package dirs whose package metadata enables npm publishing. */
export function listPublishablePluginPackageDirs(params = {}) {
const repoRoot = path.resolve(params.repoRoot ?? ".");
const extensionsRoot = path.join(repoRoot, "extensions");
@@ -107,12 +110,14 @@ export function listPublishablePluginPackageDirs(params = {}) {
.toSorted((left, right) => left.localeCompare(right));
}
/** List package-local runtime output files expected from a runtime build plan. */
export function listPluginNpmRuntimeBuildOutputs(plan) {
return Object.keys(plan.entry)
.map((entryKey) => `./dist/${entryKey}.js`)
.toSorted((left, right) => left.localeCompare(right));
}
/** Resolve package `files` entries needed for runtime build outputs and plugin metadata. */
export function resolvePluginNpmRuntimePackageFiles(plan) {
const merged = new Set(
Array.isArray(plan.packageJson.files)
@@ -158,6 +163,7 @@ function resolveOpenClawPeerRange(packageJson, rootPackageJson) {
);
}
/** Resolve package peer dependency metadata for the OpenClaw plugin API. */
export function resolvePluginNpmRuntimePackagePeerMetadata(plan) {
const openclawPeerRange = resolveOpenClawPeerRange(plan.packageJson, plan.rootPackageJson);
if (!openclawPeerRange) {
@@ -183,6 +189,7 @@ export function resolvePluginNpmRuntimePackagePeerMetadata(plan) {
};
}
/** Resolve the package-local runtime build plan for one publishable plugin package. */
export function resolvePluginNpmRuntimeBuildPlan(params) {
const repoRoot = path.resolve(params.repoRoot ?? ".");
const packageDir = resolvePackageDir(repoRoot, params.packageDir);
@@ -247,6 +254,7 @@ export function resolvePluginNpmRuntimeBuildPlan(params) {
};
}
/** Build package-local runtime files and static assets for one plugin package. */
export async function buildPluginNpmRuntime(params) {
const plan = resolvePluginNpmRuntimeBuildPlan(params);
if (!plan) {

View File

@@ -1,6 +1,8 @@
// Collects runtime dependency specs from bundled plugin packages.
import fs from "node:fs";
import path from "node:path";
/** Collect dependencies and optionalDependencies needed at plugin runtime. */
export function collectRuntimeDependencySpecs(packageJson = {}) {
return new Map(
[
@@ -10,6 +12,7 @@ export function collectRuntimeDependencySpecs(packageJson = {}) {
);
}
/** Extract an npm package name from a bare module specifier. */
export function packageNameFromSpecifier(specifier) {
if (
typeof specifier !== "string" ||
@@ -27,6 +30,7 @@ export function packageNameFromSpecifier(specifier) {
return first.startsWith("@") && second ? `${first}/${second}` : first;
}
/** Collect runtime dependency specs across bundled plugin packages and note conflicts. */
export function collectBundledPluginPackageDependencySpecs(bundledPluginsDir) {
const specs = new Map();

View File

@@ -1,5 +1,7 @@
// Defines the plugin prerelease validation surface and matching test lanes.
import { BUNDLED_PLUGIN_INSTALL_UNINSTALL_SHARDS } from "./docker-e2e-scenarios.mjs";
/** Required behavioral surfaces that plugin prerelease validation must cover. */
export const PLUGIN_PRERELEASE_REQUIRED_SURFACES = Object.freeze([
"package-artifact",
"bundled-lifecycle",
@@ -140,6 +142,7 @@ function coveredSurfaces(entries) {
].toSorted((a, b) => a.localeCompare(b));
}
/** Build the plugin prerelease plan from Docker lanes and static checks. */
export function createPluginPrereleaseTestPlan() {
const dockerLanes = pluginPrereleaseDockerLanes.map((entry) => entry.lane);
const allEntries = [...pluginPrereleaseDockerLanes, ...staticChecks];
@@ -155,6 +158,7 @@ export function createPluginPrereleaseTestPlan() {
};
}
/** Assert that a plugin prerelease plan covers every required surface. */
export function assertPluginPrereleaseTestPlanComplete(plan = createPluginPrereleaseTestPlan()) {
const missing = PLUGIN_PRERELEASE_REQUIRED_SURFACES.filter(
(surface) => !plan.surfaces.includes(surface),

View File

@@ -1,10 +1,13 @@
// Derives plugin SDK entrypoint sets, package exports, and dist artifact paths.
import deprecatedBarrelPluginSdkSubpathList from "./plugin-sdk-deprecated-barrel-subpaths.json" with { type: "json" };
import deprecatedPublicPluginSdkSubpathList from "./plugin-sdk-deprecated-public-subpaths.json" with { type: "json" };
import pluginSdkEntryList from "./plugin-sdk-entrypoints.json" with { type: "json" };
import privateLocalOnlyPluginSdkSubpathList from "./plugin-sdk-private-local-only-subpaths.json" with { type: "json" };
/** All plugin SDK entrypoints, including the package root index. */
export const pluginSdkEntrypoints = [...pluginSdkEntryList];
/** Plugin SDK subpath entrypoints, excluding the package root index. */
export const pluginSdkSubpaths = pluginSdkEntrypoints.filter((entry) => entry !== "index");
const privateLocalOnlyPluginSdkSubpathSet = new Set(
@@ -13,30 +16,37 @@ const privateLocalOnlyPluginSdkSubpathSet = new Set(
),
);
/** Private plugin SDK entrypoints that are built locally but not exported publicly. */
export const privateLocalOnlyPluginSdkEntrypoints = pluginSdkSubpaths.filter((entry) =>
privateLocalOnlyPluginSdkSubpathSet.has(entry),
);
/** Public plugin SDK entrypoints that appear in package exports. */
export const publicPluginSdkEntrypoints = pluginSdkEntrypoints.filter(
(entry) => entry === "index" || !privateLocalOnlyPluginSdkSubpathSet.has(entry),
);
/** Public plugin SDK subpaths, excluding the package root index. */
export const publicPluginSdkSubpaths = publicPluginSdkEntrypoints.filter(
(entry) => entry !== "index",
);
/** Deprecated public plugin SDK subpaths kept for compatibility. */
export const deprecatedPublicPluginSdkEntrypoints = publicPluginSdkSubpaths.filter((entry) =>
deprecatedPublicPluginSdkSubpathList.includes(entry),
);
/** Deprecated barrel entrypoints that should not be expanded further. */
export const deprecatedBarrelPluginSdkEntrypoints = pluginSdkSubpaths.filter((entry) =>
deprecatedBarrelPluginSdkSubpathList.includes(entry),
);
/** Build tsdown entry source paths for plugin SDK entrypoints. */
export function buildPluginSdkEntrySources(entries = pluginSdkEntrypoints) {
return Object.fromEntries(entries.map((entry) => [entry, `src/plugin-sdk/${entry}.ts`]));
}
/** Build package export metadata for public plugin SDK entrypoints. */
export function buildPluginSdkPackageExports() {
return Object.fromEntries(
publicPluginSdkEntrypoints.map((entry) => [
@@ -49,6 +59,7 @@ export function buildPluginSdkPackageExports() {
);
}
/** List public plugin SDK dist artifacts expected in package output. */
export function listPluginSdkDistArtifacts() {
return publicPluginSdkEntrypoints.flatMap((entry) => [
`dist/plugin-sdk/${entry}.js`,
@@ -56,6 +67,7 @@ export function listPluginSdkDistArtifacts() {
]);
}
/** List private local-only plugin SDK dist artifacts expected after local builds. */
export function listPrivateLocalOnlyPluginSdkDistArtifacts() {
return privateLocalOnlyPluginSdkEntrypoints.flatMap((entry) => [
`dist/plugin-sdk/${entry}.js`,

View File

@@ -1,7 +1,10 @@
// Tiny shared value-normalization helpers for script JSON records.
/** Return whether a value is a plain non-array object record. */
export function isRecord(value) {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
/** Trim string values while converting non-strings to an empty string. */
export function trimString(value) {
return typeof value === "string" ? value.trim() : "";
}