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 buildfrombuild.commandrunsonPostBuildfunctionality 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:
onPreBuildfunctionality from plugin runsnpm run buildfrombuild.commandrunsonBuildfunctionality from plugin runsonPostBuildfunctionality 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 buildfrombuild.commandruns
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
onPreBuildfunctionality from plugin runsnpm run buildfrombuild.commandrunsonBuildfunctionality from plugin runsonPostBuildfunctionality 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
onPreBuildfunctionality fromplugin-tworuns- Output
foofromplugin-tworeturned.${pluginTwo.outputs.foo}then fully resolved tohello. npm run buildfrombuild.commandruns from inline lifecycleonBuildfunctionality fromplugin-oneruns withbizvalue 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-pluginloads &onInitfunctionality runs with config set tooptOne: hello&optTwo: goodbyeplugin-one&plugin-twoare initialized with config- Then
plugin-one.onPreBuild&plugin-two.onBuildmethods are called - Then output from
plugin-one.onPreBuild&plugin-two.onBuildare referenced in the code oforchestrator-plugin. - Then the build ends