Skip to content

[APPS] Use separate vite.build() for backend functions, drop Rollup backend support#292

Merged
yoannmoinet merged 9 commits intomasterfrom
sdkennedy2/apps-vite-backend-build
Apr 7, 2026
Merged

[APPS] Use separate vite.build() for backend functions, drop Rollup backend support#292
yoannmoinet merged 9 commits intomasterfrom
sdkennedy2/apps-vite-backend-build

Conversation

@sdkennedy2
Copy link
Copy Markdown
Collaborator

@sdkennedy2 sdkennedy2 commented Mar 20, 2026

Motivation

Backend functions were previously built by injecting virtual modules into the host bundler's build via resolveId/load hooks, with separate Rollup and Vite plugin implementations that duplicated logic. This approach was fragile — it coupled backend function bundling to the host build's configuration (plugins, aliases, output settings) and required maintaining two codepaths (Rollup + Vite).

This PR switches to a standalone vite.build() call that runs after the host build completes, giving full control over the backend build configuration. It also drops Rollup backend support entirely, simplifying to vite-only.

Changes

Replaced the "inject into host build" approach with a dedicated vite.build() for backend functions:

// In closeBundle hook (runs after host build finishes):
const result = await vite.build({
    configFile: false,       // Don't inherit user's vite plugins
    build: {
        write: true,
        outDir: tempDir,     // Isolated output directory
        rollupOptions: {
            input: virtualEntries,
            output: { format: 'es', entryFileNames: '[name].js' },
            treeshake: false,  // Preserve action-catalog bridges intact
        },
    },
    plugins: [/* virtual module resolver */],
});

Why a separate build? Backend functions need different build settings than the frontend — no minification, ES module format, preserved entry signatures, and no interference from the user's vite plugins. Running a standalone vite.build() with configFile: false provides this isolation cleanly.

Key changes:

  • Deleted backend/rollup.ts — Rollup backend support removed
  • Simplified backend/index.ts — removed resolveId/load hooks, now just delegates to the vite plugin
  • New buildBackendFunctions() in backend/vite/index.ts — runs a separate vite.build() via closeBundle hook, writes output to a temp directory, populates backendOutputs map for the upload plugin
  • Refactored virtual-entry.ts — extracted generateMainBody() helper for reuse
  • Updated shared.tsisActionCatalogInstalled() now accepts a fromDir parameter for correct resolution when the plugin is linked, fixed regex escaping in action-catalog bridge
  • Restricted backendSupportedBundlers from ['rollup', 'vite'] to ['vite']

QA Instructions

  1. Scaffold or use an existing high code app with backend functions
  2. Link the local vite plugin and run yarn dev in build-plugins
  3. Run a production build (npx vite build) and verify backend function .js files are generated and included in the upload archive
  4. Confirm the build works without the user's vite.config.ts plugins interfering with the backend build

Blast Radius

  • Only affects vite production builds when apps.backendDir is configured
  • Rollup users with backend functions will now see a warning that backend functions are not supported for their bundler (previously attempted but was untested)
  • No impact on webpack, esbuild, or rspack users

Documentation

@sdkennedy2 sdkennedy2 requested a review from yoannmoinet as a code owner March 20, 2026 20:38
Copy link
Copy Markdown
Collaborator Author

sdkennedy2 commented Mar 20, 2026

@datadog-datadog-prod-us1
Copy link
Copy Markdown

datadog-datadog-prod-us1 bot commented Mar 26, 2026

✅ Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 5421cb8 | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback!

* Build all backend functions using a separate vite.build() call.
* Produces one standalone JS file per function in a temp directory.
*/
export async function buildBackendFunctions(
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this here because the logic is largely coupled to vite versus the code in the backend folder.

Comment on lines +94 to +95
// viteBuild always returns RolldownOutput here since we don't set build.watch.
// RolldownWatcher would only be returned if watch mode were enabled.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand this comment.
Can you expand on to why it's clarifying exactly.

Copy link
Copy Markdown
Member

@yoannmoinet yoannmoinet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, well done!!

Copy link
Copy Markdown
Contributor

@sarenji sarenji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a small potential improvement. Otherwise LGTM

backendOutDir = await buildBackendFunctions(
viteBuild,
functions,
backendOutputs,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backendOutputs is potentially something you can instantiate within the plugin and pass to handleUpload.

Note that tests currently have to do a workaround to reset backendOutputs, so test isolation is reduced. However, if you pass backendOutputs to handleUpload then backendOutputs is properly isolated and you can put a spy on handleUpload and still be able to test backendOutputs even outside the function.

@sdkennedy2 sdkennedy2 force-pushed the sdkennedy2/apps-vite-backend-build branch from d2b2a1b to 03c5f9c Compare March 30, 2026 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants