* fix(builders): shim __dirname/__filename in fully-bundled ESM output esbuild leaves the CJS globals __dirname/__filename as free identifiers when inlining CJS modules into ESM output, so dependencies that reference them at module scope (google-gax via @google-cloud/pubsub, Prisma's runtime) crash the deployed Vercel function at init with 'ReferenceError: __dirname is not defined in ES module scope' before any workflow code runs. v4 was immune because the Build Output API function was CJS; #1562 switched it to ESM with a banner that shimmed only require(). Extend the ESM banner to define __filename/__dirname from import.meta.url, matching the shim verified live in #2770. * chore(builders): clarify interop banner scope and tighten its regression test Review follow-ups to the __dirname/__filename shim; no behavior change. The ESM banner now declares require, __filename and __dirname, but the flag that suppresses it is still named skipEsmRequireBanner and its JSDoc described it purely in terms of __createRequire. Document the full surface instead of renaming, since BaseBuilder is exported and createStepsBundle is protected, so a rename would break external subclasses in a patch release. The note calls out #3778 specifically: reaching for this flag to silence a duplicate require declaration also drops the dirname shim. Assert the banner's import binding is emitted exactly once. A duplicated banner fails at parse time on the redeclared import, before the var ever runs, so that is the assertion that matches the real failure mode. Also note that createWorkflowsBundle's final wrapper and createWebhookBundle emit the same banner and are not covered here. Finally, record in the changeset that CJS dependencies which feature-detect via `typeof __dirname !== 'undefined'` now take their CJS branch, where __dirname is the function root rather than the dependency's own directory. Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(builders): assert the webhook function bundle carries the ESM interop shim The webhook route is a separately deployed function built through its own esbuild pass (createWebhookBundle); a CJS dependency referencing __dirname reachable from it would have crashed identically, so cover that emit site rather than only noting it as untested. --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@workflow/builders
Shared builder infrastructure for Workflow SDK. This package provides the base builder class and utilities used by framework-specific integrations.
Overview
This package contains the core build logic for transforming workflow source files into deployable bundles. It is used by:
@workflow/cli- For standalone/basic builds@workflow/next- For Next.js integration@workflow/nitro- For Nitro/Nuxt integration
Key components
- BaseBuilder: Abstract base class providing common build logic
- Build plugins: esbuild plugins for workflow transformations
- SWC integration: Compiler plugin integration for workflow directives
Usage
This package is typically not used directly. Instead, use one of the framework-specific packages that extend BaseBuilder:
import { BaseBuilder } from '@workflow/builders';
class MyBuilder extends BaseBuilder {
async build(): Promise<void> {
// Implement builder-specific logic
}
}
Observing transforms
Builder configurations can provide an optional onAfterTransform observer for
tooling that derives metadata from the exact SWC output used by a build:
import type { WorkflowAfterTransformHook } from '@workflow/builders';
// Pass as `onAfterTransform` in the builder configuration.
const onAfterTransform: WorkflowAfterTransformHook = async ({
mode,
filename,
absolutePath,
source,
code,
workflowManifest,
}) => {
// Observe the accepted transform result.
};
The observer is awaited after the transform's manifest entries have been accepted. It cannot replace the generated code, and throwing aborts the build. A source file may be observed multiple times across transform modes, bundles, and watch rebuilds, so consumers should deduplicate results when necessary.
Architecture
The builder system uses:
- esbuild for bundling and tree-shaking
- SWC for transforming workflow directives (
"use workflow","use step") - Enhanced resolve for TypeScript path mapping
License
MIT