-
Notifications
You must be signed in to change notification settings - Fork 149
Allow "use step" functions to be defined outside of the app directory #708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Allow "use step" functions to be defined outside of the app directory #708
Conversation
🦋 Changeset detectedLatest commit: def292b The changes in this PR will be included in the next version bump. This PR includes changesets to release 10 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🧪 E2E Test Results❌ Some tests failed Summary
❌ Failed Tests💻 Local Development (28 failed)nuxt-stable (28 failed):
🌍 Community Worlds (12 failed)mongodb (1 failed):
redis (1 failed):
starter (9 failed):
turso (1 failed):
Details by Category✅ ▲ Vercel Production
❌ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
❌ 🌍 Community Worlds
❌ Some E2E test jobs failed:
Check the workflow run for details. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
094a1be to
def292b
Compare
📊 Benchmark Results
workflow with no steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Nitro | Express workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Nitro | Express Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|
| // Get output content if not writing to file | ||
| const outputContent = | ||
| !writeToFile && stepsResult.outputFiles?.[0] | ||
| ? stepsResult.outputFiles[0].text | ||
| : undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error check for esbuild output when creating steps bundle in-memory. The TypeScript overload promises a non-optional outputContent: string, but the implementation can return undefined, which will cause runtime errors in the second build pass.
View Details
📝 Patch Details
diff --git a/packages/builders/src/base-builder.ts b/packages/builders/src/base-builder.ts
index 0b37f3f..be5939a 100644
--- a/packages/builders/src/base-builder.ts
+++ b/packages/builders/src/base-builder.ts
@@ -391,6 +391,13 @@ export abstract class BaseBuilder {
// Create .gitignore in .swc directory
await this.createSwcGitignore();
+ // Ensure output files exist when not writing to disk
+ if (!writeToFile) {
+ if (!stepsResult.outputFiles || stepsResult.outputFiles.length === 0) {
+ throw new Error('No output files generated from esbuild');
+ }
+ }
+
// Get output content if not writing to file
const outputContent =
!writeToFile && stepsResult.outputFiles?.[0]
Analysis
Missing error check for esbuild output in createStepsBundle
What fails: The createStepsBundle() method in base-builder.ts has TypeScript overloads that promise a non-optional outputContent: string when the outfile parameter is omitted, but the implementation can return undefined, violating the type contract. This causes the callers in standalone.ts and vercel-build-output-api.ts to use the outputContent directly in esbuild's stdin.contents without null-checking, creating a type-safety issue.
How to reproduce:
- Call
createStepsBundle()without theoutfileparameter (as done instandalone.tsline 54 andvercel-build-output-api.tsline 59) - The TypeScript overload guarantees
outputContent: string - The implementation uses:
!writeToFile && stepsResult.outputFiles?.[0] ? stepsResult.outputFiles[0].text : undefined - If
stepsResult.outputFilesis empty or falsy,outputContentbecomesundefined, violating the contract
Result: TypeScript incorrectly allows undefined to be passed where string is required by the overload contract. While esbuild should always populate outputFiles when write: false succeeds, the lack of explicit error checking creates a defensive programming gap.
Expected: The code should validate that outputFiles exists and is non-empty before extracting content, similar to the error check in createWorkflowsBundle() at line 548-550:
if (!interimBundle.outputFiles || interimBundle.outputFiles.length === 0) {
throw new Error('No output files generated from esbuild');
}Fix: Added explicit error check in createStepsBundle() at line 393-397 to ensure outputFiles exists when writeToFile is false, making the type contract enforceable.

TL;DR
Enable "use step" functions to be defined outside of the app directory with proper path alias resolution.
What changed?
pathsAliasHelperto a proper step function calledpathsAliasStepthat can be imported directlypathsAliasWorkflowto call the external step function directly instead of using a wrapper stepHow to test?
Run the
pathsAliasWorkflowtest which imports a step function from outside the workbench directory using a TypeScript path alias. The workflow should complete successfully and return "pathsAliasStep".Why make this change?
This enhancement provides more flexibility in organizing step functions by allowing them to be defined anywhere in the codebase, not just within the app directory. It enables better code organization and reuse of step functions across different parts of the application while maintaining proper path alias resolution.