From a0fcb91670122cf5ba16275e976f6a00e47d2313 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 28 May 2026 20:05:04 +0100 Subject: [PATCH] fix(release): keep private test helpers out of npm pack --- scripts/check-plugin-sdk-exports.mjs | 4 ++-- scripts/lib/bundled-plugin-build-entries.mjs | 8 ++++---- scripts/lib/plugin-sdk-entries.d.mts | 2 +- scripts/lib/plugin-sdk-entries.mjs | 6 ++---- scripts/write-plugin-sdk-entry-dts.ts | 17 +++++++++++++++-- .../bundled-plugin-build-entries.test.ts | 6 ++++++ tsdown.config.ts | 16 +++++++++++----- 7 files changed, 41 insertions(+), 18 deletions(-) diff --git a/scripts/check-plugin-sdk-exports.mjs b/scripts/check-plugin-sdk-exports.mjs index 8780275a1a3a..b210c4bb6bd5 100755 --- a/scripts/check-plugin-sdk-exports.mjs +++ b/scripts/check-plugin-sdk-exports.mjs @@ -11,7 +11,7 @@ import { readFileSync, existsSync } from "node:fs"; import { resolve, dirname } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; -import { pluginSdkSubpaths } from "./lib/plugin-sdk-entries.mjs"; +import { publicPluginSdkSubpaths } from "./lib/plugin-sdk-entries.mjs"; const scriptDir = dirname(fileURLToPath(import.meta.url)); const distFile = resolve(scriptDir, "..", "dist", "plugin-sdk", "index.js"); @@ -70,7 +70,7 @@ for (const name of requiredExports) { } } -for (const entry of pluginSdkSubpaths) { +for (const entry of publicPluginSdkSubpaths) { const jsPath = resolve(scriptDir, "..", "dist", "plugin-sdk", `${entry}.js`); const dtsPath = resolve(scriptDir, "..", "dist", "plugin-sdk", `${entry}.d.ts`); if (!existsSync(jsPath)) { diff --git a/scripts/lib/bundled-plugin-build-entries.mjs b/scripts/lib/bundled-plugin-build-entries.mjs index 20770dbf39d9..e3b5cdc83e93 100644 --- a/scripts/lib/bundled-plugin-build-entries.mjs +++ b/scripts/lib/bundled-plugin-build-entries.mjs @@ -12,6 +12,8 @@ const TOP_LEVEL_PUBLIC_SURFACE_EXTENSIONS = new Set([".ts", ".js", ".mts", ".cts export const NON_PACKAGED_BUNDLED_PLUGIN_DIRS = new Set(["qa-channel", "qa-lab", "qa-matrix"]); const EXCLUDED_CORE_BUNDLED_PLUGIN_DIRS = new Set(["qqbot", "whatsapp"]); const BUNDLED_PLUGIN_BUILD_IDS_ENV = "OPENCLAW_BUNDLED_PLUGIN_BUILD_IDS"; +const TOP_LEVEL_PRIVATE_TEST_SURFACE_RE = + /(?:^|[._-])(?:test|spec|test-support|test-helpers|test-fixtures|test-harness|mock-setup)(?:[._-]|$)/u; const toPosixPath = (value) => value.replaceAll("\\", "/"); function parseBundledPluginBuildIdFilter(env = process.env) { @@ -88,8 +90,7 @@ export function collectTopLevelPublicSurfaceEntries(pluginDir) { if ( normalizedName.endsWith(".d.ts") || /^config-api\.(?:[cm]?[jt]s)$/u.test(normalizedName) || - normalizedName.includes(".test.") || - normalizedName.includes(".spec.") || + TOP_LEVEL_PRIVATE_TEST_SURFACE_RE.test(normalizedName) || normalizedName.includes(".fixture.") || normalizedName.includes(".snap") ) { @@ -117,8 +118,7 @@ function collectTopLevelPublicSurfaceEntriesFromFiles(relativeFiles) { if ( normalizedName.endsWith(".d.ts") || /^config-api\.(?:[cm]?[jt]s)$/u.test(normalizedName) || - normalizedName.includes(".test.") || - normalizedName.includes(".spec.") || + TOP_LEVEL_PRIVATE_TEST_SURFACE_RE.test(normalizedName) || normalizedName.includes(".fixture.") || normalizedName.includes(".snap") ) { diff --git a/scripts/lib/plugin-sdk-entries.d.mts b/scripts/lib/plugin-sdk-entries.d.mts index 704593af4473..1042fbbd3bd9 100644 --- a/scripts/lib/plugin-sdk-entries.d.mts +++ b/scripts/lib/plugin-sdk-entries.d.mts @@ -6,7 +6,7 @@ export const publicPluginSdkSubpaths: string[]; export const deprecatedPublicPluginSdkEntrypoints: string[]; export const deprecatedBarrelPluginSdkEntrypoints: string[]; -export function buildPluginSdkEntrySources(): Record; +export function buildPluginSdkEntrySources(entries?: readonly string[]): Record; export function buildPluginSdkPackageExports(): Record< string, { diff --git a/scripts/lib/plugin-sdk-entries.mjs b/scripts/lib/plugin-sdk-entries.mjs index 1ba61caeb39b..433507b0f270 100644 --- a/scripts/lib/plugin-sdk-entries.mjs +++ b/scripts/lib/plugin-sdk-entries.mjs @@ -33,10 +33,8 @@ export const deprecatedBarrelPluginSdkEntrypoints = pluginSdkSubpaths.filter((en deprecatedBarrelPluginSdkSubpathList.includes(entry), ); -export function buildPluginSdkEntrySources() { - return Object.fromEntries( - pluginSdkEntrypoints.map((entry) => [entry, `src/plugin-sdk/${entry}.ts`]), - ); +export function buildPluginSdkEntrySources(entries = pluginSdkEntrypoints) { + return Object.fromEntries(entries.map((entry) => [entry, `src/plugin-sdk/${entry}.ts`])); } export function buildPluginSdkPackageExports() { diff --git a/scripts/write-plugin-sdk-entry-dts.ts b/scripts/write-plugin-sdk-entry-dts.ts index 100c0cfdd215..def3bd3d6bac 100644 --- a/scripts/write-plugin-sdk-entry-dts.ts +++ b/scripts/write-plugin-sdk-entry-dts.ts @@ -2,7 +2,11 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { build } from "tsdown"; -import { buildPluginSdkEntrySources, pluginSdkEntrypoints } from "./lib/plugin-sdk-entries.mjs"; +import { + buildPluginSdkEntrySources, + pluginSdkEntrypoints, + publicPluginSdkEntrypoints, +} from "./lib/plugin-sdk-entries.mjs"; const RUNTIME_SHIMS: Partial> = { "webhook-path": [ @@ -67,6 +71,11 @@ function copyFlatDeclarations(fromDir: string, toDir: string): void { const distPluginSdkDir = path.join(process.cwd(), "dist/plugin-sdk"); const flatDeclarationTempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-sdk-dts-")); +const shouldBuildPrivateQaEntries = process.env.OPENCLAW_BUILD_PRIVATE_QA === "1"; +const flatDeclarationEntrypoints = shouldBuildPrivateQaEntries + ? pluginSdkEntrypoints + : publicPluginSdkEntrypoints; +const flatDeclarationEntrypointSet = new Set(flatDeclarationEntrypoints); try { await build({ @@ -74,7 +83,7 @@ try { config: false, deps: { neverBundle: (id) => isBareImportSpecifier(id) }, dts: true, - entry: buildPluginSdkEntrySources(), + entry: buildPluginSdkEntrySources(flatDeclarationEntrypoints), failOnWarn: false, fixedExtension: false, format: "esm", @@ -96,6 +105,10 @@ try { // The private workspace package keeps source-shaped declaration paths for local // package-boundary projects, so bridge them back to the packaged flat entries. for (const entry of pluginSdkEntrypoints) { + if (!flatDeclarationEntrypointSet.has(entry)) { + continue; + } + const packageTypeOut = path.join( process.cwd(), `packages/plugin-sdk/dist/src/plugin-sdk/${entry}.d.ts`, diff --git a/test/scripts/bundled-plugin-build-entries.test.ts b/test/scripts/bundled-plugin-build-entries.test.ts index 9463367cec3c..d968d43540bb 100644 --- a/test/scripts/bundled-plugin-build-entries.test.ts +++ b/test/scripts/bundled-plugin-build-entries.test.ts @@ -103,6 +103,12 @@ describe("bundled plugin build entries", () => { expect(entries["extensions/telegram/telegram-ingress-worker.runtime"]).toBeUndefined(); }); + it("keeps top-level bundled plugin test helpers out of public-surface entries", () => { + const entries = listBundledPluginBuildEntries(); + + expect(entries["extensions/browser/test-support"]).toBeUndefined(); + }); + it("discovers repo plugin build entries without directory scans", () => { const payload = expectNoNodeFsScans<{ artifacts: number; diff --git a/tsdown.config.ts b/tsdown.config.ts index 978ecfd6a0b6..64d4f226d7c5 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -5,7 +5,11 @@ import { collectBundledPluginBuildEntries, NON_PACKAGED_BUNDLED_PLUGIN_DIRS, } from "./scripts/lib/bundled-plugin-build-entries.mjs"; -import { buildPluginSdkEntrySources } from "./scripts/lib/plugin-sdk-entries.mjs"; +import { + buildPluginSdkEntrySources, + pluginSdkEntrypoints, + publicPluginSdkEntrypoints, +} from "./scripts/lib/plugin-sdk-entries.mjs"; type InputOptionsFactory = Extract, Function>; type InputOptionsArg = InputOptionsFactory extends ( @@ -140,6 +144,9 @@ function nodeBuildConfig(config: UserConfig): UserConfig { const bundledPluginBuildEntries = collectBundledPluginBuildEntries(); const shouldBuildPrivateQaEntries = process.env.OPENCLAW_BUILD_PRIVATE_QA === "1"; +const productionPluginSdkEntrypoints = shouldBuildPrivateQaEntries + ? pluginSdkEntrypoints + : publicPluginSdkEntrypoints; function buildBundledHookEntries(): Record { const hooksRoot = path.join(process.cwd(), "src", "hooks", "bundled"); @@ -357,10 +364,9 @@ function buildUnifiedDistEntries(): Record { // Private bundled Codex helper for app-server user MCP config projection. "plugin-sdk/codex-mcp-projection": "src/plugin-sdk/codex-mcp-projection.ts", ...Object.fromEntries( - Object.entries(buildPluginSdkEntrySources()).map(([entry, source]) => [ - `plugin-sdk/${entry}`, - source, - ]), + Object.entries(buildPluginSdkEntrySources(productionPluginSdkEntrypoints)).map( + ([entry, source]) => [`plugin-sdk/${entry}`, source], + ), ), ...(shouldBuildPrivateQaEntries ? {