diff --git a/sources/@repo/docs/content/reference/bud.before/index.mdx b/sources/@repo/docs/content/reference/bud.before/index.mdx
new file mode 100644
index 0000000000..a375fac16b
--- /dev/null
+++ b/sources/@repo/docs/content/reference/bud.before/index.mdx
@@ -0,0 +1,80 @@
+---
+title: bud.before
+description: Do something before compilation has started
+tags:
+ - facade
+ - lifecycle
+---
+
+import ConfigExample from '@site/src/components/example';
+
+**bud.before** allows you to execute arbitrary code directly prior to the start of the compilation process.
+
+## Usage
+
+Using an asyncronous function:
+
+
+
+```ts
+bud.before(async bud => {
+ // Do something before the compilation has completed
+})
+```
+
+```js
+bud.before(async bud => {
+ // Do something before the compilation has completed
+})
+```
+
+```yml
+before: >
+ => async bud => {
+ // do someting before the compilation has started
+ }
+```
+
+```json
+{
+ "before": "=> async bud => {
+ // do something before the compilation has started
+ }"
+}
+```
+
+
+
+Using a synchronous function:
+
+
+
+
+```ts
+bud.before(bud => {
+ // Do something before the compilation has completed
+})
+```
+
+```js
+bud.before(bud => {
+ // Do something before the compilation has completed
+})
+```
+
+```yml
+before: >
+ => bud => {
+ // do someting before the compilation has started
+ }
+```
+
+```json
+{
+ "before": "=> bud => {
+ // do something before the compilation has started
+ }"
+}
+```
+
+
diff --git a/sources/@roots/bud-framework/src/methods/before/index.ts b/sources/@roots/bud-framework/src/methods/before/index.ts
new file mode 100644
index 0000000000..ded98c10ad
--- /dev/null
+++ b/sources/@roots/bud-framework/src/methods/before/index.ts
@@ -0,0 +1,37 @@
+import {Bud} from '@roots/bud-framework'
+
+declare module '@roots/bud-framework' {
+ interface Bud {
+ /**
+ * Execute a function before compiler has finished
+ */
+ before: before
+ }
+}
+
+export interface before {
+ (
+ callback: ((app: Bud) => Promise) | ((app: Bud) => unknown),
+ onError?: (error: Error) => unknown,
+ ): Bud
+}
+
+/**
+ * Execute a function before compiler has finished
+ */
+export const before: before = function (
+ this: Bud,
+ fn: ((app: Bud) => any) | ((app: Bud) => Promise),
+ onError?: (error: Error) => unknown,
+): Bud {
+ this.hooks.action(`compiler.before`, async bud => {
+ try {
+ await bud.resolvePromises()
+ await fn(bud)
+ } catch (error) {
+ onError ? onError(error) : bud.catch(error)
+ }
+ })
+
+ return this
+}
diff --git a/sources/@roots/bud-framework/src/methods/index.ts b/sources/@roots/bud-framework/src/methods/index.ts
index 019ff71ece..ca5327669d 100644
--- a/sources/@roots/bud-framework/src/methods/index.ts
+++ b/sources/@roots/bud-framework/src/methods/index.ts
@@ -2,6 +2,7 @@ import type {Bud} from '@roots/bud-framework'
import {addConfig} from '@roots/bud-framework/methods/addConfig'
import {after} from '@roots/bud-framework/methods/after'
+import {before} from '@roots/bud-framework/methods/before'
import {bindFacade} from '@roots/bud-framework/methods/bindFacade'
import {close} from '@roots/bud-framework/methods/close'
import {container} from '@roots/bud-framework/methods/container'
@@ -32,6 +33,7 @@ type methods = Partial<{
const methods = {
addConfig,
after,
+ before,
bindFacade,
close,
container,
diff --git a/sources/@roots/bud-framework/test/methods/before/before.test.ts b/sources/@roots/bud-framework/test/methods/before/before.test.ts
new file mode 100644
index 0000000000..0c563d0802
--- /dev/null
+++ b/sources/@roots/bud-framework/test/methods/before/before.test.ts
@@ -0,0 +1,46 @@
+import {type Bud, factory} from '@repo/test-kit'
+import {beforeAll, describe, expect, it, vi} from 'vitest'
+
+import {before as subject} from '../../../src/methods/before/index.js'
+
+describe(`@roots/bud-framework/methods/before`, function () {
+ let before: subject
+ let bud: Bud
+
+ beforeAll(async () => {
+ bud = await factory()
+ before = subject.bind(bud)
+ })
+
+ it(`should be a function`, () => {
+ expect(before).toBeInstanceOf(Function)
+ })
+
+ it(`should return bud`, async () => {
+ const value = before(async () => {})
+ expect(value).toBe(bud)
+ })
+
+ it(`should call the callback`, async () => {
+ const fn = vi.fn(async () => null)
+ before(fn)
+ await bud.hooks.fire(`compiler.before`, bud)
+ expect(fn).toHaveBeenCalled()
+ })
+
+ it(`should work with a synchronous callback`, async () => {
+ const fn = vi.fn()
+ before(fn)
+ await bud.hooks.fire(`compiler.before`, bud)
+ expect(fn).toHaveBeenCalled()
+ })
+
+ it(`should call the error handler if supplied`, async () => {
+ const onError = vi.fn()
+ before(() => {
+ throw new Error(`test`)
+ }, onError)
+ await bud.hooks.fire(`compiler.before`, bud)
+ expect(onError).toHaveBeenCalled()
+ })
+})