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() + }) +})