diff --git a/scripts/check-extension-package-tsc-boundary.mjs b/scripts/check-extension-package-tsc-boundary.mjs index b932994f421..42781a3f90e 100644 --- a/scripts/check-extension-package-tsc-boundary.mjs +++ b/scripts/check-extension-package-tsc-boundary.mjs @@ -193,6 +193,7 @@ function isRelevantCompileInput(filePath) { function collectNewestMtime(entryPath, params = {}) { const includeFile = params.includeFile ?? (() => true); + const skipDistDirectories = params.skipDistDirectories ?? true; let newestMtimeMs = 0; function visit(currentPath) { @@ -202,7 +203,7 @@ function collectNewestMtime(entryPath, params = {}) { const stats = statSync(currentPath); if (stats.isDirectory()) { const basename = path.basename(currentPath); - if (basename === "dist" || basename === "node_modules") { + if ((skipDistDirectories && basename === "dist") || basename === "node_modules") { return; } for (const child of readdirSync(currentPath)) { @@ -241,10 +242,9 @@ export function isBoundaryCompileFresh(extensionId, params = {}) { collectNewestMtime(extensionRoot, { includeFile: isRelevantCompileInput }); const sharedNewestInputMtimeMs = params.sharedNewestInputMtimeMs ?? - Math.max( - collectNewestMtime(resolve(rootDir, "dist/plugin-sdk")), - collectNewestMtime(resolve(rootDir, "packages/plugin-sdk/dist")), - ); + collectNewestMtime(resolve(rootDir, "packages/plugin-sdk/dist"), { + skipDistDirectories: false, + }); const newestInputMtimeMs = Math.max(extensionNewestInputMtimeMs, sharedNewestInputMtimeMs); const oldestOutputMtimeMs = collectOldestMtime([ resolveBoundaryTsStampPath(extensionId, rootDir), @@ -553,13 +553,19 @@ async function runCompileCheck(extensionIds) { process.stdout.write( `preparing plugin-sdk boundary artifacts for ${extensionIds.length} plugins\n`, ); - runNodeStep("plugin-sdk boundary prep", [prepareBoundaryArtifactsBin], 420_000); + runNodeStep( + "plugin-sdk boundary prep", + [prepareBoundaryArtifactsBin, "--mode=package-boundary"], + 420_000, + ); const prepElapsedMs = Date.now() - prepStartedAt; const concurrency = resolveCompileConcurrency(); const verboseFreshLogs = process.env.OPENCLAW_EXTENSION_BOUNDARY_VERBOSE_FRESH === "1"; - const sharedNewestInputMtimeMs = Math.max( - collectNewestMtime(resolve(repoRoot, "dist/plugin-sdk")), - collectNewestMtime(resolve(repoRoot, "packages/plugin-sdk/dist")), + const sharedNewestInputMtimeMs = collectNewestMtime( + resolve(repoRoot, "packages/plugin-sdk/dist"), + { + skipDistDirectories: false, + }, ); process.stdout.write(`compile concurrency ${concurrency}\n`); const compileStartedAt = Date.now(); diff --git a/scripts/prepare-extension-package-boundary-artifacts.mjs b/scripts/prepare-extension-package-boundary-artifacts.mjs index 6202c499007..ef7edee9fe5 100644 --- a/scripts/prepare-extension-package-boundary-artifacts.mjs +++ b/scripts/prepare-extension-package-boundary-artifacts.mjs @@ -7,6 +7,7 @@ const require = createRequire(import.meta.url); const repoRoot = resolve(import.meta.dirname, ".."); const tscBin = require.resolve("typescript/bin/tsc"); const TYPE_INPUT_EXTENSIONS = new Set([".ts", ".tsx", ".d.ts", ".js", ".mjs", ".json"]); +const VALID_MODES = new Set(["all", "package-boundary"]); const ROOT_DTS_INPUTS = [ "tsconfig.json", @@ -36,6 +37,15 @@ function isRelevantTypeInput(filePath) { return TYPE_INPUT_EXTENSIONS.has(path.extname(filePath)); } +export function parseMode(argv = process.argv.slice(2)) { + const modeArg = argv.find((arg) => arg.startsWith("--mode=")); + const mode = modeArg?.slice("--mode=".length) ?? "all"; + if (!VALID_MODES.has(mode)) { + throw new Error(`Unknown mode: ${mode}`); + } + return mode; +} + function collectNewestMtime(paths, params = {}) { const rootDir = params.rootDir ?? repoRoot; const includeFile = params.includeFile ?? (() => true); @@ -199,8 +209,9 @@ export async function runNodeStepsInParallel(steps) { } } -export async function main() { +export async function main(argv = process.argv.slice(2)) { try { + const mode = parseMode(argv); const rootDtsFresh = isArtifactSetFresh({ inputPaths: ROOT_DTS_INPUTS, outputPaths: ["dist/plugin-sdk/.tsbuildinfo"], @@ -221,14 +232,16 @@ export async function main() { }); const pendingSteps = []; - if (!rootDtsFresh) { - pendingSteps.push({ - label: "plugin-sdk boundary dts", - args: [tscBin, "-p", "tsconfig.plugin-sdk.dts.json"], - timeoutMs: 300_000, - }); - } else { - process.stdout.write("[plugin-sdk boundary dts] fresh; skipping\n"); + if (mode === "all") { + if (!rootDtsFresh) { + pendingSteps.push({ + label: "plugin-sdk boundary dts", + args: [tscBin, "-p", "tsconfig.plugin-sdk.dts.json"], + timeoutMs: 300_000, + }); + } else { + process.stdout.write("[plugin-sdk boundary dts] fresh; skipping\n"); + } } if (!packageDtsFresh) { pendingSteps.push({ @@ -244,13 +257,13 @@ export async function main() { await runNodeStepsInParallel(pendingSteps); } - if (!entryShimsFresh || pendingSteps.length > 0) { + if (mode === "all" && (!entryShimsFresh || pendingSteps.length > 0)) { await runNodeStep( "plugin-sdk boundary root shims", ["--import", "tsx", resolve(repoRoot, "scripts/write-plugin-sdk-entry-dts.ts")], 120_000, ); - } else { + } else if (mode === "all") { process.stdout.write("[plugin-sdk boundary root shims] fresh; skipping\n"); } } catch (error) { diff --git a/test/scripts/check-extension-package-tsc-boundary.test.ts b/test/scripts/check-extension-package-tsc-boundary.test.ts index 330c90c4329..d087b5f93b3 100644 --- a/test/scripts/check-extension-package-tsc-boundary.test.ts +++ b/test/scripts/check-extension-package-tsc-boundary.test.ts @@ -206,30 +206,26 @@ describe("check-extension-package-tsc-boundary", () => { ).toBe("skipped 97 fresh plugin compiles\n"); }); - it("treats a plugin compile as fresh only when its outputs are newer than plugin and sdk inputs", () => { + it("treats a plugin compile as fresh only when its outputs are newer than plugin and package sdk inputs", () => { const { rootDir, extensionRoot } = createTempExtensionRoot(); const extensionSourcePath = path.join(extensionRoot, "index.ts"); const extensionTsconfigPath = path.join(extensionRoot, "tsconfig.json"); const stampPath = path.join(extensionRoot, "dist", ".boundary-tsc.stamp"); - const rootSdkBuildInfoPath = path.join(rootDir, "dist", "plugin-sdk", ".tsbuildinfo"); - const packageSdkBuildInfoPath = path.join( + const rootSdkTypePath = path.join(rootDir, "dist", "plugin-sdk", "core.d.ts"); + const packageSdkTypePath = path.join( rootDir, "packages", "plugin-sdk", "dist", - ".tsbuildinfo", - ); - const entryShimStampPath = path.join( - rootDir, - "dist", + "src", "plugin-sdk", - ".boundary-entry-shims.stamp", + "core.d.ts", ); fs.mkdirSync(path.dirname(extensionSourcePath), { recursive: true }); fs.mkdirSync(path.dirname(stampPath), { recursive: true }); - fs.mkdirSync(path.dirname(rootSdkBuildInfoPath), { recursive: true }); - fs.mkdirSync(path.dirname(packageSdkBuildInfoPath), { recursive: true }); + fs.mkdirSync(path.dirname(rootSdkTypePath), { recursive: true }); + fs.mkdirSync(path.dirname(packageSdkTypePath), { recursive: true }); fs.writeFileSync(extensionSourcePath, "export const demo = 1;\n", "utf8"); fs.writeFileSync( @@ -238,26 +234,27 @@ describe("check-extension-package-tsc-boundary", () => { "utf8", ); fs.writeFileSync(stampPath, "ok\n", "utf8"); - fs.writeFileSync(rootSdkBuildInfoPath, "ok\n", "utf8"); - fs.writeFileSync(packageSdkBuildInfoPath, "ok\n", "utf8"); - fs.writeFileSync(entryShimStampPath, "ok\n", "utf8"); + fs.writeFileSync(rootSdkTypePath, "export {};\n", "utf8"); + fs.writeFileSync(packageSdkTypePath, "export {};\n", "utf8"); fs.utimesSync(extensionSourcePath, new Date(1_000), new Date(1_000)); fs.utimesSync(extensionTsconfigPath, new Date(1_000), new Date(1_000)); - fs.utimesSync(rootSdkBuildInfoPath, new Date(2_000), new Date(2_000)); - fs.utimesSync(packageSdkBuildInfoPath, new Date(2_000), new Date(2_000)); - fs.utimesSync(entryShimStampPath, new Date(2_000), new Date(2_000)); + fs.utimesSync(rootSdkTypePath, new Date(500), new Date(500)); + fs.utimesSync(packageSdkTypePath, new Date(2_000), new Date(2_000)); fs.utimesSync(stampPath, new Date(3_000), new Date(3_000)); expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true); - fs.utimesSync(rootSdkBuildInfoPath, new Date(500), new Date(500)); - fs.utimesSync(packageSdkBuildInfoPath, new Date(500), new Date(500)); - fs.utimesSync(entryShimStampPath, new Date(500), new Date(500)); + fs.utimesSync(rootSdkTypePath, new Date(500), new Date(500)); + fs.utimesSync(packageSdkTypePath, new Date(500), new Date(500)); expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true); - fs.utimesSync(rootSdkBuildInfoPath, new Date(4_000), new Date(4_000)); + fs.utimesSync(rootSdkTypePath, new Date(4_000), new Date(4_000)); + + expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true); + + fs.utimesSync(packageSdkTypePath, new Date(4_000), new Date(4_000)); expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(false); }); diff --git a/test/scripts/prepare-extension-package-boundary-artifacts.test.ts b/test/scripts/prepare-extension-package-boundary-artifacts.test.ts index 4ffdfe6bc6b..acf75b650ad 100644 --- a/test/scripts/prepare-extension-package-boundary-artifacts.test.ts +++ b/test/scripts/prepare-extension-package-boundary-artifacts.test.ts @@ -5,6 +5,7 @@ import { afterEach, describe, expect, it } from "vitest"; import { createPrefixedOutputWriter, isArtifactSetFresh, + parseMode, runNodeStepsInParallel, } from "../../scripts/prepare-extension-package-boundary-artifacts.mjs"; @@ -85,4 +86,10 @@ describe("prepare-extension-package-boundary-artifacts", () => { }), ).toBe(false); }); + + it("parses prep mode and rejects unknown values", () => { + expect(parseMode([])).toBe("all"); + expect(parseMode(["--mode=package-boundary"])).toBe("package-boundary"); + expect(() => parseMode(["--mode=nope"])).toThrow("Unknown mode: nope"); + }); });