---
sidebarDepth: 2
---
# Plugin develop
## initialize plugin
You can create a umi plugin scaffold in a simple way with [create-umi](https://github.com/umijs/create-umi):
```shell
$ yarn create umi --plugin
```
In umi, the plugin is actually a JS module, you need to define a plugin initialization method and export by default. The following example:
```js
export default (api, opts) => {
// your plugin code here
};
```
It should be noted that if your plugin needs to be published as an npm package, then you need to compile before publishing, to ensure that the code in the release is ES5 code.
The initialization method will receive two parameters, the first parameter `api`, the interface provided by the umi to the plugin is exposed through it. The second parameter, `opts`, is filled in by the user when initializing the plugin.
## Introduction to the plugin interface
All of umi's plugin interfaces are provided through the api when the plugin is initialized. Divided into the following categories:
- Environment variables, some environment variables that can be used in the plugin
- System-level variables, variables or constants exposed by some plug-in systems
- Tools API, some commonly used tool class methods
- System level API, some core methods exposed by plugin systems
- Event class API, key event points provided by some plugin systems
- Application class API, API for implementing plugin function requirements, there are two methods of direct call and function callback
**Note:** All APIs are used by the `api.[theApiName]` method, and the internal APIs are uniformly prefixed with `_`.
Here's a basic usage example:
```js
export default (api, opts) => {
api.onOptionChange(() => {
api.rebuildTmpFiles();
});
};
```
## Plugin demo
The following is an example of plugin example code refer to `umi-plugin-locale` plugin code. For a complete example, see [source code](https://github.com/umijs/umi/blob/master/packages/umi- Plugin-locale/src/index.js).
```js
export default (api, opts = {}) => {
const { paths } = api;
// Linstening plugin options changes
api.onOptionChange((newOpts) => {
opts = newOpts;
api.rebuildTmpFiles();
});
// add Provider wrapper
api.addRendererWrapperWithComponent(join(__dirname, './locale.js'));
api.addRendererWrapperWithComponent(() => {
if (opts.antd) {
return join(__dirname, './locale-antd.js'));
}
});
// add watcher on locale files
api.addPageWatcher(
join(paths.absSrcPath, config.singular ? 'locale' : 'locales'),
);
};
```
## Plugins order
The execution order of the plugins depends on the `plugins` configuration item configured by the user in the configuration file `.umirc.js` or `config config.js`. The dependent plugin umi will check the order of the plugins through the plugin's `dependence` configuration. A warning is issued, but currently umi does not modify the order of the users.
When the plugin calls `api.applyPlugins` to trigger the hooks of the plugin, the execution order of the hooks corresponds to the order of `plugins`. The order in which hooks are concerned is determined by the corresponding hooks.
## Environmental variable
### NODE_ENV
`process.env.NODE_ENV`, Distinguish between development and production
## System level variable
### config
configuration in `.umirc.js` or `config/config.js`.
### paths
- outputPath
- absOutputPath
- pagesPath
- absPagesPath
- tmpDirPath
- absTmpDirPath
- absSrcPath
- cwd: project root
- absNodeModulesPath
### routes
umi processed routing information. The format is as follows:
```js
const routes = [
{
path: '/xxx/xxx',
component: '',
},
];
```
## System level API
### registerPlugin
Register a plugin, usually used for plugin set.
```js
const demoPlugin = require('./demoPlugin');
api.registerPlugin({
id: 'plugin-id',
apply: demoPlugin,
opts: {},
});
```
### registerMethod
Register a plugin method to add a new method to the plugin for use by other plugins.
```js
// Type usually corresponds to the method name addXxx modifyXxx onXxx afterXxx beforeXxx
api.registerMethod('addDvaRendererWrapperWithComponent', {
type: api.API_TYPE.ADD
type: api.API_TYPE.EVENT
type: api.API_TYPE.MODIFY
apply: () => {} // for custom type
});
```
For plugin methods of type `api.API_TYPE.ADD`, you should return an item or return a multiple of the array, or you can return an empty array, such as:
```js
api.addHTMLMeta({
/* ... */
});
api.addHTMLMeta([
{
/* ... */
},
{
/* ... */
},
]);
api.addHTMLMeta(() => {
if (opt === 'h5') {
return {
/* ... */
};
}
return [];
});
```
The type is a plugin method of `api.API_TYPE.EVENT`, you should pass in a function and don't need to return anything.
The plugin method of type `api.API_TYPE.MODIFY` returns the modified content.
You can also use `apply` to customize the processing function. Your registered method may be used by multiple plugins. When you call `applyPlugins`, the return value of these plugins will be processed by the reduce function inside umi. The `apply` function you define determines how `applyPlugins` handles the result of multiple plugins as its return value. Usually three types of built-in can meet your needs.
### applyPlugins
Trigger a method registered by the app via `registerMethod`.
```js
// If the type is api.API_TYPE.ADD wrappers an array of values returned by each plugin
// EVENT wrapper returns undefined
// MODIFY returns the last modified value
const wrappers = api.applyPlugins('wrapDvaRendererWithComponent');
```
### restart
```js
api.restart('why');
```
rerun `umi dev`.
### rebuildTmpFiles
```js
api.rebuildTmpFiles('config dva changed');
```
regenerate bootstrap file (entryFile), this is the most commonly used method, plugins such as dva and locale will be used.
### refreshBrowser
refresh browser.
### rebuildHTML
Trigger HTML rebuild.
### changePluginOption
Set the options of the plugin, such as when you need to pass the dva configuration of the plugin set to the dva plugin in the umi-plugin-react.
```js
api.changePluginOption('dva-plugin-id', {
immer: true,
});
```
### registerCommand
Register the umi xxx command line, as in the umi internal help command.
```js
api.registerCommand(
'help',
{
hide: true,
},
args => {
// more code...
},
);
```
### \_registerConfig
Register a configuration item, system method, usually do not use.
```js
api._registerConfig(() => {
return () => {
return {
name: 'dva',
validate: validate,
onChange(newConfig, oldConfig) {
api.setPluginDefaultConfig('umi-plugin-dva', config);
},
};
};
});
```
### \_modifyCommand
Modify command name and args.
```js
// A demo for modify block npmClient to cnpm:
api._modifyCommand(({ name, args }) => {
if (name === 'block') {
args.npmClient = args.npmClient || 'cnpm';
}
return { name, args };
});
```
## Tool class API
### log
```js
api.log.success('Done');
api.log.error('Error');
api.log.error(new Error('Error'));
api.log.debug('Hello', 'from', 'L59');
api.log.pending('Write release notes for %s', '1.2.0');
api.log.watch('Recursively watching build directory...');
```
Output various [types](https://github.com/klaussinani/signale/blob/94984998a0e9cb280e68959ddd9db70b49713738/types.js#L4) of logs.
### winPath
```js
api.winPath('/path/to.js');
```
Convert the file path to a path compatible with window to add code such as `require('/xxx/xxx.js')`.
### debug
```js
api.debug('msg');
```
### findJS
xxx -> xxx.js xxx.ts xxx.jsx xxx.tsx
### findCSS
xxx -> xxx.css xxx.less xxx.scss xxx.sass
### compatDirname
Look for the user project directory first, then find the plugin dependencies.
## Event class API
The event class API follows the naming convention of onXxxXxx, beforeXxx, afterXxx and receives a parameter as a callback function.
### beforeDevServer
Before dev server start.
### afterDevServer
After dev server start.
```js
api.afterDevServer(({ serve, devServerPort }) => {
// You can get the actual port number of the service monitor here.
console.log(devServerPort);
});
```
### onStart
Triggered when `umi dev` or `umi build` start.
### onDevCompileDone
Triggered after `umi dev` compilation is complete.
```js
api.onDevCompileDone(({ isFirstCompile, stats }) => {});
```
### onOptionChange
Triggered when the configuration of the plugin changes.
```js
export default (api, defaultOpts = { immer: false }) => {
let opts = defaultOpts;
api.onOptionChange(newOpts => {
opts = newOpts;
api.rebuildFiles();
});
};
```
### beforeBuildCompileAsync
Before Umi call `af-webpack/build` for a compilation
```js
api.beforeBuildCompileAsync(async () => {
yield delay(1000);
});
```
### onBuildSuccess
When the `umi build` was successful. Mainly do some processing of the construction products.
```js
api.onBuildSuccess(({ stats }) => {
// handle with stats
});
```
### onBuildSuccessAsync
The async version of onBuildSuccess.
```js
api.onBuildSuccessAsync(async ({ stats }) => {
await delay(1000);
console.log(stats);
});
```
### onBuildFail
When the `umi build` failed.
### onHTMLRebuild
Triggered when the HTML is rebuilt.
### onGenerateFiles
The routing file is triggered when the entry file is generated.
### onPatchRoute
Triggered when getting the configuration of a single route, you can modify the route configuration `route` here. For example, you can add a component path to `Routes` to add a layer of encapsulation to the route.
```js
api.onPatchRoute({ route } => {
// route:
// {
// path: '/xxx',
// Routes: []
// }
})
```
## Application class API
For the application class API, there are two ways to use: direct calling and function callback.
direct calling:
```js
api.addRendererWrapperWithComponent('/path/to/component.js');
```
function callback:
```js
api.addRendererWrapperWithComponent(() => {
if (opts.antd) {
return '/path/to/component.js';
}
});
```
Below is the specific API.
### modifyDefaultConfig
set umi default configuration.
```js
api.modifyDefaultConfig(memo => {
return {
...memo,
singular: true,
};
});
```
### addPageWatcher
add watching files.
```js
api.addPageWatcher(['xxx.js', '*.mock.js']);
```
### addHTMLMeta
add meta in HTML.
### addHTMLLink
add link in HTML.
### addHTMLStyle
add tyle in HTML.
### addHTMLScript
Add a script at the bottom of the HTML.
```js
api.addHTMLScript({
content: '',
src: '',
// ...attrs
});
```
### addHTMLHeadScript
Add a script to the HTML head.
### modifyHTMLChunks
Modify chunks in HTML, default `['umi']`.
### modifyHTMLWithAST
Modify the HTML, based on cheerio.
Options:
- route, current route
- getChunkPath , get the full path of chunk, including publicPath and hash
e.g.
```js
api.modifyHTMLWithAST(($, { route, getChunkPath }) => {
$('head').append(``);
});
```
### modifyHTMLContext
Modify the environment parameters when html ejs is rendered.
```js
api.modifyHTMLContext((memo, { route }) => {
return {
...memo,
title: route.title, // The title plugin for umi-plugin-react contains similar logic
};
});
```
### modifyRoutes
Modify the routing configuration.
```js
api.modifyRoutes(routes => {
return routes;
});
```
The format of the route configuration is as follows:
```js
const route = {
path: '/xxx',
component: '/path/to/component',
Routes: ['/permissionControl.js'],
};
```
```js
exports.routes = [
{
path: '/xxx',
workspace: false,
},
];
```
```js
//permissionControl.js
export class Control extends Component (props) => {
componentDidount() => {
if(props.route.workspace === false) {
window.AntdCloudNav.set()
}
}
}
```
### addEntryImportAhead
add import at the top of the entry file.
```js
api.addEntryImportAhead({
source: '/path/to/module',
specifier: 'name', // module name with import, can be ignored
});
```
### addEntryPolyfillImports
Same as `addEntryImportAhead`, but as a polyfill, so add it first.
### addEntryImport
Import module in the entry file.
```js
api.addEntryImport({
source: '/modulePath/xxx.js',
specifier: 'moduleName',
});
```
### addEntryCodeAhead
Add code before render.
```js
api.addEntryCodeAhead(`
console.log('addEntryCodeAhead');
`);
```
### addEntryCode
Add code after render.
### addRouterImport
Add a module import to the routing file.
### addRouterImportAhead
Add a module to the header of the routing file to introduce.
### addRendererWrapperWithComponent
Wrapper a component outside the .
### addRendererWrapperWithModule
Excute a module before mount .
### addUmiExports
import from 'umi'
```js
// export all
// genarate:export * from 'dva';
api.addUmiExports([
{
exportAll: true,
source: 'dva',
},
]);
// export certain
// genarate:export { connect } from 'dva';
api.addUmiExports([
{
specifiers: ['connect'],
source: 'dva',
},
]);
// support alias
// genarate:export { default as dva } from 'dva';
api.addUmiExports([
{
specifiers: [{ local: 'default', exported: 'dva' }],
source: 'dva',
},
]);
```
### modifyEntryRender
modifyEntryRender
### modifyEntryHistory
modifyEntryHistory
### modifyRouteComponent
modifyRouteComponent
### modifyRouterRootComponent
modifyRouterRootComponent
### chainWebpackConfig
Modify webpack configuration via [webpack-chain](https://github.com/neutrinojs/webpack-chain).
```js
// demo
api.chainWebpackConfig(memo => {
return memo;
});
```
### modifyAFWebpackOpts
Modify the af-webpack configuration.
```js
// demo
api.modifyAFWebpackOpts(memo => {
return memo;
});
```
### addMiddleware
Append middleware to the dev server.
### addMiddlewareAhead
Add middleware to the front of the development server.
### addMiddlewareBeforeMock
Add middleware before the mock.
### addMiddlewareAfterMock
Add middleware after the mock.
### addVersionInfo
Added version information, displayed in `umi -v` or `umi version`.
### addRuntimePlugin
Add a runtime plugin with parameters as the absolute path to the file.
e.g.
```js
api.addRuntimePlugin(require.resolve('./app.js'));
```
Then in `app.js`:
```
export function render(oldRender) {
setTimeout(oldRender, 1000);
}
```
This implements a 1 second delayed rendering application.
### addRuntimePluginKey
Add a runtime configurable item.