Here is a simple plugin with no config and it runs on a single lifecycle event postBuild
// Plugin code
module.exports = function myPlugin(pluginConfig) {
// custom code/init steps
return {
onPostBuild: (pluginAPI) => {
// run thing on post build
}
}
} /* alternative syntax, simple object */
module.exports = {
onPostBuild: (pluginAPI) => {
// run thing on post build
}
} |
# Netlify Config
build:
command: npm run build
publish: build
functions: functions
plugins:
- package: my-simple-plugin |
What happens on build:
npm run build
frombuild.command
runsonPostBuild
functionality from plugin runs
The pluginAPI
passed into methods is as follows
module.exports = function myPluginXyz(pluginConfig) {
return {
/**
* Plugin API
* @param {object} netlifyConfig - Resolved value of Netlify configuration file
* @param {object} pluginConfig - Initial plugin configuration
* @param {object} utils - set of utility functions for working with Netlify
* @param {object} utils.cache - Helper functions for dealing with build cache
* @param {object} utils.git - Helper functions for dealing with git
* @param {object} utils.run - Helper functions for dealing with executables
* @param {object} utils.functions - Helper functions for dealing with Netlify functions
* @param {object} utils.redirects - Helper functions for dealing with Netlify redirects
* @param {object} utils.headers - Helper functions for dealing with Netlify headers
* @param {object} constants - constant values referencing various env paths
* @param {object} constants.CONFIG_PATH - path to netlify config file
* @param {object} constants.BUILD_DIR - path to site build directory
* @param {object} constants.CACHE_DIR - path to cache directory
* @param {object} constants.FUNCTIONS_SRC - path to functions source code directory
* @param {object} constants.FUNCTIONS_DIST - path to functions build directory
* @param {object} api - scoped API instance of Netlify sdk
* @return {object} outputs - output values from call
*/
onPreBuild: ({ netlifyConfig, pluginConfig, utils, constants, api }) => {
// do the thing
},
}
}
Here is a basic plugin with no config and it runs on 3 lifecycle event preBuild
, build
, & postBuild
// Plugin code
module.exports = function myPlugin(pluginConfig) {
return {
onPreBuild: (pluginAPI) => {
// run thing on pre-build
},
onBuild: (pluginAPI) => {
// run thing on build
},
onPostBuild: (pluginAPI) => {
// run thing on post build
}
}
} |
# Netlify Config
build:
command: npm run build
publish: build
functions: functions
plugins:
- package: my-simple-plugin |
What happens on build:
onPreBuild
functionality from plugin runsnpm run build
frombuild.command
runsonBuild
functionality from plugin runsonPostBuild
functionality from plugin runs
Here is a basic plugin that exposes a CLI command. It runs on 0 lifecycle events.
Instead, it exposes a new CLI command netlify foo
// Plugin code
module.exports = function myPlugin(pluginConfig) {
return {
/* Commands exposes new CLI commands */
commands: {
foo: {
usage: 'The foo command does xyz',
options: {
flagone: {
usage: 'This flag sets the thing for the thing',
required: true,
shortcut: 'f'
}
},
method: (pluginAPI) => {
// Logic to run when "netlify foo -f" is executed
}
}
}
}
} |
# Netlify Config
build:
command: npm run build
publish: build
functions: functions
plugins:
- package: my-simple-plugin |
What happens on build:
Plugin contains no lifecycle events so nothing runs from plugin.
npm run build
frombuild.command
runs
Here is a basic plugin with config and it runs on 3 lifecycle event preBuild
, build
, & postBuild
// Plugin code
module.exports = function myPlugin(pluginConfig) {
return {
config: {
fizz: 'string',
pop: {
type: 'string',
required: true,
}
},
onPreBuild: (pluginAPI) => {
// run thing on pre-build
},
onBuild: (pluginAPI) => {
// run thing on build
},
onPostBuild: (pluginAPI) => {
// run thing on post build
}
}
} |
# Netlify Config
build:
command: npm run build
publish: build
functions: functions
plugins:
- package: my-simple-plugin
config:
fizz: hello
pop: there |
What happens on build:
- Required inputs validated
onPreBuild
functionality from plugin runsnpm run build
frombuild.command
runsonBuild
functionality from plugin runsonPostBuild
functionality from plugin runs
Here we use 2 plugins. Plugin one has required inputs and plugin two's output values are used in plugin one.
// Has required config property
module.exports = function pluginOne(pluginConfig) {
return {
config: {
biz: {
type: 'string',
required: true,
}
},
onBuild: ({ pluginConfig }) => {
console.log(pluginConfig.bar)
},
}
} // Exposes 1 output during onPreBuild phase
module.exports = function pluginTwo(pluginConfig) {
return {
/* Plugin outputs */
outputs: {
foo: {
type: 'string',
when: 'onPreBuild'
}
},
onPreBuild: (pluginAPI) => {
// Output value is returned from onPreBuild phase
return {
foo: 'hello'
}
},
}
} |
# Netlify Config
build:
command: npm run build
publish: build
functions: functions
plugins:
- package: plugin-one
config:
biz: ${pluginTwo.outputs.foo}
# ^ output referenced from `id`
# given to plugin-two
- package: plugin-two
id: pluginTwo |
What happens on build:
Inputs & outputs dependancies are ordered up front via a DAG and cycles throw an error.
- Required inputs validated. Output recognized & lifecycle checked to verify ordering works. Re: DAG
onPreBuild
functionality fromplugin-two
runs- Output
foo
fromplugin-two
returned.${pluginTwo.outputs.foo}
then fully resolved tohello
. npm run build
frombuild.command
runs from inline lifecycleonBuild
functionality fromplugin-one
runs withbiz
value set tohello
Here we use 3 plugins. The plugin-one
& plugin-two
are programmatically used in a third plugin called orchestratorPlugin
. Because they are programmatically used, the order of lifecycle methods in plugin-one
& plugin-two
do not matter. Effectively they are used as normal NPM modules.
Programmatic usage from orchestrator plugin. Because these are used programmatically and are not defined in the Netlify config file, they can be called in any order the user wishes. // This plugin is consumer of the 2 other plugins
// plugin-one & plugin-two are not defined in Netlify config file
const pluginOne = require('plugin-one')
const pluginTwo = require('plugin-two')
module.exports = function orchestratorPlugin(config) {
return {
name: 'orchestrator-plugin',
config: {
optOne: {
type: 'string',
required: true,
},
optTwo: {
type: 'string',
required: true,
}
},
onInit: async ({ pluginConfig }) => {
// initialize plugins with config
const one = pluginOne({ biz: pluginConfig.optOne })
const two = pluginTwo({ zaz: pluginConfig.optTwo })
const [outputFromOne, outputFromTwo] = await Promise.all([
one.onPreBuild(pluginAPI),
two.onBuild(pluginAPI),
])
// Do stuff with outputFromOne / outputFromTwo
},
}
} module.exports = function pluginOne(pluginConfig) {
return {
name: 'plugin-one',
// Config needed for plugin
config: {
biz: {
type: 'string',
required: true,
}
},
// outputs returned for DAG resolution
outputs: {
wow: {
type: 'string',
when: 'onBuild'
}
},
onPreBuild: ({ pluginConfig }) => {
console.log(pluginConfig.biz)
return {
wow: 'nice'
}
},
}
} // Has required config property
module.exports = function pluginTwo(pluginConfig) {
return {
name: 'plugin-two',
config: {
zaz: {
type: 'string',
required: true,
}
},
outputs: {
wow: {
type: 'string',
when: 'onBuild'
}
},
onBuild: ({ pluginConfig }) => {
console.log(pluginConfig.zaz)
},
}
} |
build:
publish: build
plugins:
- package: orchestrator-plugin
config:
optOne: hello
optTwo: goodbye
|
What happens on build:
Because they are programmatically used, the order of lifecycle methods in plugin-one
& plugin-two
do not matter.
- Required inputs validated from read config file
orchestrator-plugin
loads &onInit
functionality runs with config set tooptOne: hello
&optTwo: goodbye
plugin-one
&plugin-two
are initialized with config- Then
plugin-one.onPreBuild
&plugin-two.onBuild
methods are called - Then output from
plugin-one.onPreBuild
&plugin-two.onBuild
are referenced in the code oforchestrator-plugin
. - Then the build ends