Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/twelve-guests-sink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@workflow/core": patch
"@workflow/next": patch
"@workflow/web": patch
---

stop esbuild bundling for deferred step route in Next.js
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"mermaid": "^11.12.2",
"motion": "^12.23.25",
"nanoid": "5.1.6",
"next": "16.0.10",
"next": "16.1.6",
"next-themes": "^0.4.6",
"next-validate-link": "^1.6.3",
"radix-ui": "latest",
Expand Down
89 changes: 74 additions & 15 deletions packages/builders/src/base-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,38 @@ export abstract class BaseBuilder {
this.config = config;
}

/**
* Whether informational BaseBuilder logs should be printed.
* Subclasses can override this to silence progress logs while keeping warnings/errors.
*/
protected get shouldLogBaseBuilderInfo(): boolean {
return true;
}

protected logBaseBuilderInfo(...args: unknown[]): void {
if (this.shouldLogBaseBuilderInfo) {
console.log(...args);
}
}

private logCreateWorkflowsBundleInfo(...args: unknown[]): void {
if (!this.config.suppressCreateWorkflowsBundleLogs) {
this.logBaseBuilderInfo(...args);
}
}

private logCreateWebhookBundleInfo(...args: unknown[]): void {
if (!this.config.suppressCreateWebhookBundleLogs) {
this.logBaseBuilderInfo(...args);
}
}

private logCreateManifestInfo(...args: unknown[]): void {
if (!this.config.suppressCreateManifestLogs) {
this.logBaseBuilderInfo(...args);
}
}

/**
* Performs the complete build process for workflows.
* Subclasses must implement this to define their specific build steps.
Expand Down Expand Up @@ -140,7 +172,7 @@ export abstract class BaseBuilder {
});
} catch (_) {}

console.log(
this.logBaseBuilderInfo(
`Discovering workflow directives`,
`${Date.now() - discoverStart}ms`
);
Expand Down Expand Up @@ -204,7 +236,10 @@ export abstract class BaseBuilder {
private logEsbuildMessages(
result: { errors?: any[]; warnings?: any[] },
phase: string,
throwOnError = true
throwOnError = true,
options?: {
suppressWarnings?: boolean;
}
): void {
if (result.errors && result.errors.length > 0) {
console.error(`❌ esbuild errors in ${phase}:`);
Expand All @@ -226,7 +261,11 @@ export abstract class BaseBuilder {
}
}

if (result.warnings && result.warnings.length > 0) {
if (
!options?.suppressWarnings &&
result.warnings &&
result.warnings.length > 0
) {
console.warn(`! esbuild warnings in ${phase}:`);
for (const warning of result.warnings) {
console.warn(` ${warning.text}`);
Expand Down Expand Up @@ -457,7 +496,10 @@ export abstract class BaseBuilder {
const stepsResult = await esbuildCtx.rebuild();

this.logEsbuildMessages(stepsResult, 'steps bundle creation');
console.log('Created steps bundle', `${Date.now() - stepsBundleStart}ms`);
this.logBaseBuilderInfo(
'Created steps bundle',
`${Date.now() - stepsBundleStart}ms`
);

// Handle workflow-only files that may have been tree-shaken from the bundle.
// These files have no steps, so esbuild removes them, but we still need their
Expand Down Expand Up @@ -489,7 +531,7 @@ export abstract class BaseBuilder {
}
} catch (error) {
// Log warning but continue - don't fail build for workflow-only file issues
console.log(
console.warn(
`Warning: Failed to extract workflow metadata from ${workflowFile}:`,
error instanceof Error ? error.message : String(error)
);
Expand Down Expand Up @@ -659,8 +701,15 @@ export abstract class BaseBuilder {
});
const interimBundle = await interimBundleCtx.rebuild();

this.logEsbuildMessages(interimBundle, 'intermediate workflow bundle');
console.log(
this.logEsbuildMessages(
interimBundle,
'intermediate workflow bundle',
true,
{
suppressWarnings: this.config.suppressCreateWorkflowsBundleWarnings,
}
);
this.logCreateWorkflowsBundleInfo(
'Created intermediate workflow bundle',
`${Date.now() - bundleStartTime}ms`
);
Expand Down Expand Up @@ -752,8 +801,15 @@ export const POST = workflowEntrypoint(workflowCode);`;
external: ['@aws-sdk/credential-provider-web-identity'],
});

this.logEsbuildMessages(finalWorkflowResult, 'final workflow bundle');
console.log(
this.logEsbuildMessages(
finalWorkflowResult,
'final workflow bundle',
true,
{
suppressWarnings: this.config.suppressCreateWorkflowsBundleWarnings,
}
);
this.logCreateWorkflowsBundleInfo(
'Created final workflow bundle',
`${Date.now() - bundleStartTime}ms`
);
Expand Down Expand Up @@ -782,8 +838,11 @@ export const POST = workflowEntrypoint(workflowCode);`;
return;
}

console.log('Generating a client library at', this.config.clientBundlePath);
console.log(
this.logBaseBuilderInfo(
'Generating a client library at',
this.config.clientBundlePath
);
this.logBaseBuilderInfo(
'NOTE: The recommended way to use workflow with a framework like NextJS is using the loader/plugin with webpack/turbobpack/rollup'
);

Expand Down Expand Up @@ -885,7 +944,7 @@ export const POST = workflowEntrypoint(workflowCode);`;
outfile: string;
bundle?: boolean;
}): Promise<void> {
console.log('Creating webhook route');
this.logCreateWebhookBundleInfo('Creating webhook route');
await mkdir(dirname(outfile), { recursive: true });

// Create a static route that calls resumeWebhook
Expand Down Expand Up @@ -968,7 +1027,7 @@ export const OPTIONS = handler;`;
});

this.logEsbuildMessages(result, 'webhook bundle creation');
console.log(
this.logCreateWebhookBundleInfo(
'Created webhook bundle',
`${Date.now() - webhookBundleStart}ms`
);
Expand Down Expand Up @@ -1079,7 +1138,7 @@ export const OPTIONS = handler;`;
manifest: WorkflowManifest;
}): Promise<string | undefined> {
const buildStart = Date.now();
console.log('Creating manifest...');
this.logCreateManifestInfo('Creating manifest...');

try {
const workflowGraphs = await extractWorkflowGraphs(workflowBundlePath);
Expand Down Expand Up @@ -1110,7 +1169,7 @@ export const OPTIONS = handler;`;
0
);

console.log(
this.logCreateManifestInfo(
`Created manifest with ${stepCount} ${pluralize('step', 'steps', stepCount)}, ${workflowCount} ${pluralize('workflow', 'workflows', workflowCount)}, and ${classCount} ${pluralize('class', 'classes', classCount)}`,
`${Date.now() - buildStart}ms`
);
Expand Down
13 changes: 13 additions & 0 deletions packages/builders/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ interface BaseWorkflowConfig {
// Optional prefix for debug files (e.g., "_" for Astro to ignore them)
debugFilePrefix?: string;

// Suppress informational logs emitted by createWorkflowsBundle()
// (e.g. intermediate/final workflow bundle timing logs).
suppressCreateWorkflowsBundleLogs?: boolean;

// Suppress esbuild warnings emitted by createWorkflowsBundle().
suppressCreateWorkflowsBundleWarnings?: boolean;

// Suppress informational logs emitted by createWebhookBundle().
suppressCreateWebhookBundleLogs?: boolean;

// Suppress informational logs emitted by createManifest().
suppressCreateManifestLogs?: boolean;

// Node.js runtime version for Vercel Functions (e.g., "nodejs22.x", "nodejs24.x")
runtime?: string;
}
Expand Down
28 changes: 26 additions & 2 deletions packages/core/e2e/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
restoreFiles.length = 0;
});

test('should rebuild on workflow change', { timeout: 30_000 }, async () => {

Check failure on line 84 in packages/core/e2e/dev.test.ts

View workflow job for this annotation

GitHub Actions / E2E Windows Tests

packages/core/e2e/dev.test.ts > dev e2e > should rebuild on workflow change

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ packages/core/e2e/dev.test.ts:84:5
const workflowFile = path.join(appPath, workflowsDir, testWorkflowFile);

const content = await fs.readFile(workflowFile, 'utf8');
Expand Down Expand Up @@ -109,7 +109,7 @@
}
});

test('should rebuild on step change', { timeout: 30_000 }, async () => {

Check failure on line 112 in packages/core/e2e/dev.test.ts

View workflow job for this annotation

GitHub Actions / E2E Windows Tests

packages/core/e2e/dev.test.ts > dev e2e > should rebuild on step change

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ packages/core/e2e/dev.test.ts:112:5
const stepFile = path.join(appPath, workflowsDir, testWorkflowFile);

const content = await fs.readFile(stepFile, 'utf8');
Expand All @@ -125,11 +125,35 @@
`
);
restoreFiles.push({ path: stepFile, content });
const copiedStepDir = path.join(
path.dirname(generatedStep),
'__workflow_step_files__'
);

while (true) {
try {
const workflowContent = await fs.readFile(generatedStep, 'utf8');
expect(workflowContent).toContain('myNewStep');
const stepRouteContent = await fs.readFile(generatedStep, 'utf8');
if (stepRouteContent.includes('myNewStep')) {
break;
}

const copiedStepFileNames = await fs.readdir(copiedStepDir);
const copiedStepContents = await Promise.all(
copiedStepFileNames.map(async (copiedStepFileName) => {
const copiedStepFilePath = path.join(
copiedStepDir,
copiedStepFileName
);
const copiedStepStats = await fs.stat(copiedStepFilePath);
if (!copiedStepStats.isFile()) {
return '';
}
return await fs.readFile(copiedStepFilePath, 'utf8');
})
);
expect(
copiedStepContents.some((content) => content.includes('myNewStep'))
).toBe(true);
break;
} catch (_) {
await new Promise((res) => setTimeout(res, 1_000));
Expand Down
2 changes: 1 addition & 1 deletion packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
"watchpack": "2.4.4"
},
"devDependencies": {
"@workflow/tsconfig": "workspace:*",
"@types/node": "catalog:",
"@types/semver": "7.7.1",
"@types/watchpack": "2.4.4",
"@workflow/tsconfig": "workspace:*",
"next": "16.1.6"
},
"peerDependencies": {
Expand Down
Loading
Loading