From bf4592821de64708a6ae11a457e4aa8c984ff266 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Fri, 10 Apr 2026 23:29:20 -0700 Subject: [PATCH] fix(builders): restore discovery WeakMap cache for dev rebuilds Move `workflow/runtime` resolution from `createStepsBundle` into `discoverEntries` so callers pass the original `inputFiles` array reference. This restores the WeakMap cache hit when `createWorkflowsBundle` and `createStepsBundle` are called with the same inputFiles, avoiding a redundant second esbuild discovery pass per build that was causing E2E Local Dev Tests to flake on CI. --- .changeset/fix-discovery-weakmap-cache.md | 5 +++ packages/builders/src/base-builder.ts | 45 ++++++++++++----------- 2 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 .changeset/fix-discovery-weakmap-cache.md diff --git a/.changeset/fix-discovery-weakmap-cache.md b/.changeset/fix-discovery-weakmap-cache.md new file mode 100644 index 0000000000..ec4c712bfc --- /dev/null +++ b/.changeset/fix-discovery-weakmap-cache.md @@ -0,0 +1,5 @@ +--- +"@workflow/builders": patch +--- + +Fix discovery WeakMap cache miss causing duplicate esbuild passes during dev rebuilds diff --git a/packages/builders/src/base-builder.ts b/packages/builders/src/base-builder.ts index 25762fa47a..bd84bd77dd 100644 --- a/packages/builders/src/base-builder.ts +++ b/packages/builders/src/base-builder.ts @@ -218,6 +218,23 @@ export abstract class BaseBuilder { }; const discoverStart = Date.now(); + + // Resolve the SDK runtime entry point so that the discovery pass + // traces through it and discovers serde classes (like `Run`) that + // live inside SDK packages. Without this, files like `run.js` are + // only discovered when user code happens to import them. + // This is resolved here (rather than in callers) so that the original + // `inputs` array reference is preserved for WeakMap caching — callers + // like createWorkflowsBundle and createStepsBundle can share the same + // cache entry when they pass the same inputFiles array. + const resolvedWorkflowRuntime = await enhancedResolve( + outdir, + 'workflow/runtime' + ).catch(() => undefined); + const entryPoints = resolvedWorkflowRuntime + ? [...inputs, resolvedWorkflowRuntime] + : inputs; + const effectiveTsconfigPath = tsconfigPath ?? (await this.findTsConfigPath()); const esbuildTsconfigOptions = await getEsbuildTsconfigOptions( @@ -226,7 +243,7 @@ export abstract class BaseBuilder { try { await esbuild.build({ treeShaking: true, - entryPoints: inputs, + entryPoints, plugins: [ createDiscoverEntriesPlugin(state, this.transformProjectRoot), ], @@ -414,29 +431,13 @@ export abstract class BaseBuilder { ); }); - // Resolve the SDK runtime entry point so that the discovery pass - // traces through it and discovers serde classes (like `Run`) that - // live inside SDK packages. Without this, files like `run.js` are - // only discovered when user code happens to import them. - const resolvedWorkflowRuntime = await enhancedResolve( - dirname(outfile), - 'workflow/runtime' - ).catch(() => undefined); - - // These need to handle watching for dev to scan for - // new entries and changes to existing ones. - // Pass inputFiles directly when no extra entries are needed to - // preserve the array reference for discoverEntries() WeakMap caching. - const discoveryInputs = resolvedWorkflowRuntime - ? [...inputFiles, resolvedWorkflowRuntime] - : inputFiles; + // Discovery of workflow/step/serde entries. The SDK runtime entry point + // (workflow/runtime) is resolved inside discoverEntries() itself so that + // callers can pass the original inputFiles reference and benefit from + // WeakMap caching across createWorkflowsBundle / createStepsBundle calls. const discovered = discoveredEntries ?? - (await this.discoverEntries( - discoveryInputs, - dirname(outfile), - tsconfigPath - )); + (await this.discoverEntries(inputFiles, dirname(outfile), tsconfigPath)); const stepFiles = [...discovered.discoveredSteps].sort(); const workflowFiles = [...discovered.discoveredWorkflows].sort(); const serdeFiles = [...discovered.discoveredSerdeFiles].sort();