Dependency Patches
Plugins sometimes rely on Astro configuration fields that change between major versions. Rather than forking a dependency or waiting for an upstream release, bun patch lets you apply a minimal fix that survives reinstalls. This guide explains the workflow and documents every active patch in the project.
The patching workflow
Section titled “The patching workflow”Bun’s built-in patching creates a diff file in patches/ and records it in package.json under patchedDependencies. Every bun install re-applies the patch automatically.
Create a patch
Section titled “Create a patch”bun patch <package-name>This prints a path (usually node_modules/<package-name>) and tells you to edit files there.
Edit the source
Section titled “Edit the source”Make your changes directly in node_modules/<package-name>/. Keep edits minimal — touch only the lines that fix the problem.
Commit the patch
Section titled “Commit the patch”bun patch --commit 'node_modules/<package-name>'Bun generates patches/<package-name>@<version>.patch and adds a patchedDependencies entry to package.json. Both files should be committed to version control.
Verify
Section titled “Verify”Run bun install in a clean environment (or delete node_modules and reinstall) to confirm the patch re-applies correctly.
Active patches
Section titled “Active patches”starlight-site-graph — output path detection
Section titled “starlight-site-graph — output path detection”Package: starlight-site-graph@0.5.0
File: integration.ts
Problem: Ghost client/ entries in backlinks
The starlight-site-graph plugin scans built HTML to generate a sitemap with backlinks. It needs to know where Astro writes HTML output. The original code checked config.output === "server" to detect SSR mode and use config.build.client as the output path:
// Original (Astro 4 pattern)} else if (config.adapter?.name === "@astrojs/node" && config.output === "server") { outputPath = fileURLToPath(config.build.client!);}In Astro 5, config.output is always "static" even with an SSR adapter. The condition never matched, so the plugin fell back to config.outDir (dist/). When it walked dist/, it found HTML under dist/client/ and computed paths like client/electron-terminal/... — pages that don’t exist. These ghost entries appeared as broken backlinks in the right sidebar.
Fix: Check for the presence of an adapter and config.build.client rather than testing config.output:
// Patched (works with Astro 5)} else if (config.adapter && config.build.client) { outputPath = fileURLToPath(config.build.client);}This works for any SSR adapter, not just @astrojs/node, and doesn’t depend on the config.output field that Astro 5 removed from its adapter contract.
Verification: After building, check dist/client/sitegraph/sitemap.json for entries starting with client/. A clean build should have none.
starlight-site-graph — process globals in browser
Section titled “starlight-site-graph — process globals in browser”Workaround type: Vite define in astro.config.mjs (no patch file)
Problem: Uncaught ReferenceError: process is not defined
The plugin’s client-side bundle pulls in micromatch, which depends on picomatch. Both libraries reference process.platform and process.version — Node globals that don’t exist in the browser. The error fires at page load and breaks the site graph widget entirely.
Fix: Vite’s define option replaces these references with static values at build time, so the compiled JS never touches process:
vite: { define: { 'process.platform': JSON.stringify(''), 'process.version': JSON.stringify('0.0.0'), },},Empty string for process.platform and a dummy version string are safe here. picomatch only reads these values to detect Windows path separators (\\ vs /). In a browser context path-separator detection is irrelevant — all URLs use forward slashes.
Verification: Run bun run build, then open the site in a browser and check the console for ReferenceError. A clean build produces no process-related errors.
When to patch vs. fork vs. wait
Section titled “When to patch vs. fork vs. wait”Patch when the fix is small (a few lines), the root cause is clear, and the upstream package is otherwise healthy. Patches are easy to maintain and easy to remove once upstream catches up.
Fork when you need structural changes, the upstream is unmaintained, or your patch would span multiple files with interdependencies that a diff can’t express cleanly.
Wait when the issue is cosmetic, the upstream has an open PR addressing it, or the workaround is simpler than maintaining a patch (e.g., a config change or a post-build script).
Removing a patch
Section titled “Removing a patch”When an upstream release fixes the issue:
- Update the package version in
package.json - Delete the corresponding file from
patches/ - Remove the entry from
patchedDependencies - Run
bun installand verify the build