Skip to main content

Customizing loaders

The bud.build service allows you to customize the loaders used to process modules.

Adding a rule

The majority of this guide is focused on adding support for additional filetypes to bud.js in a way that is compatible with the rest of the framework and composable with rules added by other extensions.

However, if you just want to add a rule in the context of the bud config file, you might find it easiest to use the build.module.rules.oneOf hook:

export default async bud => {
bud.hooks.on(`build.module.rules.oneOf`, (rules = []) => {
rules.push({
test: /\.example$/,
use: [
{
loader: `babel-loader`,
options: {
presets: [`@babel/preset-env`],
},
},
],
})

return rules
})
}

If you are authoring an extension, you should use the API described below.

Overview

bud.build divides its handling of loaders into three related objects:

  • Loaders — A resolved path to a script which processes source modules
  • Items — A loader accompanied by its options
  • Rules — One or more items and the rules which dictate what modules they apply to

Loaders

A loader is the path to a module which handles a code transformation.

All registered loaders are stored in bud.build.loaders. They are all instances of the bud.js Loader class.

Modifying loaders

loader[`setSrc`]

Modify the source path of a given loader using Loader[`setSrc`]

bud.build.loaders.babel.setSrc('example')

Examples

With automatic resolution:

export default async bud => {
bud.build.setLoader('babel-loader')
}

With explicit resolution:

export default async bud => {
bud.build.setLoader(
'babel-loader',
await bud.module.resolve('babel-loader'),
)
}

Using the callback API:

export default async bud => {
bud.build.setLoader(
'babel-loader',
loader => loader.setSrc('babel-loader')
)
}

Items

An item is a loader and associated options. There may be multiple items using a common loader.

All registered items are stored in bud.build.items. They are all instances of the bud.js Item class.

Modifying items

item[`setOptions`]

Modify the options of a given item using Item[`setOptions`]

bud.build.items.example.setOptions({})

Item[`setLoader`]

The value given references the handle of a registered loader.

bud.build.items.example.setLoader(`example`)

Examples

Declaratively:

export default async bud => {
bud.build.setItem(`babel`, {
loader: `babel-loader`,
options: {
presets: [`@babel/preset-env`],
},
})
}

Using the callback API:

export default async bud => {
bud.build.setItem(`babel`, item =>
item.setLoader(`babel-loader`).setOptions({
presets: [`@babel/preset-env`],
}),
)
}

Rules

A rule describes:

  • a set of conditions that must be met for the rule to be applied
    • test - a regular expression that matches the module's filename
    • include - a regular expression that must match the module's path
    • exclude - a regular expression that must not match the module's path
  • a set items which should process the module if it meets the conditions

All registered rules are stored in bud.build.rules. They are all instances of the bud.js Rule class. There may be multiple rules using a common item.

Modifying rules

rule[`setTest`]

Modify the test of a given rule using Rule[`setTest`]

bud.build.rules.example.setTest(/\.example$/)

rule[`setInclude`]

Modify the include of a given rule using Rule[`setInclude`]

bud.build.rules.example.setInclude(/example/)

rule[`setExclude`]

Modify the exclude of a given rule using Rule[`setExclude`]

bud.build.rules.example.setExclude(/node_modules/)

rule[`setUse`]

Modify the items used by a given rule using Rule[`setUse`].

The value given references the handle of a registered item.

bud.build.rules.js.setUse(items => [...items, 'babel'])

Examples

Declaratively:

export default async bud => {
bud.build.setRule(`js`, {
test: /\.js$/,
use: [`babel`],
})
}

Using the callback API:

export default async bud =>
bud.build.setRule(`js`, rule => rule.setTest(/\.js$/).setUse([`babel`]))

In practice

@roots/bud-typescript

@roots/bud-typescript is an excellent example because it:

bud.build
.setLoader(`ts`, await bud.module.resolve(`ts-loader`))
.setItem(`ts`, {
loader: `ts`,
options: {},
})
.setRule(`ts`, {
test: /\.tsx?$/,
include: [bud.path(`@src`)],
use: [`babel`, `ts`],
})

bud.build.rules.js.setUse([`babel`, `ts`])

@roots/bud-vue

In @roots/bud-vue, the vue-loader is registered, and the js rule is modified to use it. Unlike the ts-loader example, the vue-loader is registered using the chainable API.

bud.build.setLoader(`vue-style-loader`).setItem(`vue-style-loader`)

bud.build.rules.css.setUse(items => [`vue-style-loader`, ...items])
bud.build.rules.sass?.setUse(items => [`vue-style-loader`, ...items])
bud.build.items.precss.setOptions({esModule: false})

bud.build
.makeRule()
.setTest(({hooks}) => hooks.filter(`pattern.vue`))
.setInclude([bud => bud.path(`@src`)])
.setUse(items => [`vue`, ...items])