Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
550d72c to
788c479
Compare
788c479 to
7b19d11
Compare
|
✅ Tests 🎉 All green!❄️ No new flaky tests detected 🔗 Commit SHA: d682d52 | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback! |
d03ec27 to
254c878
Compare
264286b to
2f9f167
Compare
| log.error(`Backend function "${funcName}" not found.`); | ||
| return null; | ||
| } | ||
| return generateVirtualEntryContent(func.name, func.entryPath); |
There was a problem hiding this comment.
We generate the virtual entry points here
| throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }); | ||
| }); | ||
|
|
||
| const result = discoverBackendFunctions(backendDir, log); |
There was a problem hiding this comment.
We discover all the backend functions here.
|
|
||
| test.each(cases)('Should $description', ({ input, expected }) => { | ||
| const plugin = getBackendPlugin(functions, new Map(), log); | ||
| const resolveId = plugin.resolveId as Function; |
There was a problem hiding this comment.
Why is plugin.resolveId and plugin.load being typecasted? Can we avoid that?
There was a problem hiding this comment.
These casts are test-only — resolveId and load on PluginOptions are typed as unplugin's Hook<> wrapper (a union of function + object-with-handler), not plain callables. We'd need custom type definitions to eliminate these, which felt like more machinery than warranted for 3 test call sites. Happy to revisit if you feel strongly.
| if (Array.isArray(outputOptions)) { | ||
| for (const out of outputOptions) { | ||
| guardManualChunks(out); | ||
| } | ||
| } else if (outputOptions) { | ||
| guardManualChunks(outputOptions); | ||
| } |
There was a problem hiding this comment.
Is this mutating the options? I'd love to avoid that if the expectation is that these are immutable. I've had this result in subtle bugs on different projects.
There was a problem hiding this comment.
outputOptions is Rollup's designated hook for modifying the output config. The expected pattern is to mutate or replace properties on the object and return it. A shallow clone could introduce its own issues here since the array case (multiple output configs) can share nested references. Happy to add a comment in the code clarifying the mutation is intentional if that helps.
packages/plugins/apps/src/index.ts
Outdated
| const allAssets: Asset[] = assets.map((asset) => ({ | ||
| // Exclude backend output files from frontend assets if backend is active. | ||
| const frontendOnly = hasBackend | ||
| ? assets.filter((a) => !new Set(backendOutputs.values()).has(a.absolutePath)) |
There was a problem hiding this comment.
Is filtering on absolutePath the right condition? Don't we already have a very convenient constant for knowing whether something is a backend function?
There was a problem hiding this comment.
BACKEND_VIRTUAL_PREFIX is the constant you may be thinking of, but it only applies to virtual module IDs during build-time resolution/loading (\0dd-backend:myHandler). By the time we reach upload, we are working with resolved output file paths (e.g., /dist/backend/myHandler.js) - the virtual prefix is gone. So filtering against backendOutputs values is the right mechanism here. I did hoist the Set construction out of the filter loop though.
2f9f167 to
d682d52
Compare

Motivation
The apps plugin needs to bundle backend functions (one standalone JS file per function) and include them in the upload archive. The previous approach (
sdkennedy2/apps-backend-functions-upload) programmatically invoked bundlers inasyncTrueEnd— a separate build per function. This was heavy, required bundler-specific code, and added esbuild as a dependency.This PR injects backend functions as additional entry points into the host build, so the user's bundler processes them alongside frontend code in a single build pass. Since the Action Platform sandbox is pure JS (no Node APIs, no network), there's no target conflict.
Changes
Phase 1 implements Rollup/Vite support with shared infrastructure for future bundler support.
Backend Function Upload Lifecycle (High-level)
Backend discovery in plugin init
getPlugins()discovers backend functions frombackendDirbefore build starts.Backend virtual entries are injected
resolveId,load) to provide virtual entry modules.Backend chunks are created during build
buildStart,writeBundle) emit backend chunks and capture their output file paths.Upload includes backend chunks in the archive
asyncTrueEndreceives the captured backend chunk map and packages those chunk outputs under abackend/prefix in the upload zip.Archive structure
QA Instructions
yarn typecheck:all— no type errorsyarn test:unit packages/plugins/apps— all 59 tests passbackend/directory containing function modules — verify the archive contains bothfrontend/andbackend/directories with standalone backend bundlesBlast Radius
@dd/apps-plugin) whenbackendDircontains function files