在 Modern.js 中暴露了三类插件:CLI、Runtime、Server。下面列举下各类中的 Hook:
config
modern.config.ts
中的配置之后ParallelWorkflow<void, unknown>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
config: () => {
return {
/** some config */
};
},
};
},
});
这里返回的配置信息,会被收集和统一处理合并。
validateSchema
config
Hook 运行完之后。ParallelWorkflow<void, unknown>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
validateSchema: () => {
return {
// target is field
target: 'foo',
schema: {
type: 'string',
},
};
},
};
},
});
这里返回的 JSON Schema 会用来校验 modern.config.js
中的配置信息。
比如这里返回:
{
"target": "foo",
"schema": {
"type": "string"
}
}
就可以在 modern.config.ts
中这样配置:
export default defineConfig({
foo: 'test',
});
如果是别的类型,校验就不通过会报错,比如这样:
export default defineConfig({
foo: {},
});
就会报错:
$ modern dev
1 | {
> 2 | "foo": {},
| ^^^^^ Property foo is not expected to be here
prepare
AsyncWorkflow<void, void>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
prepare: () => {
// do something
},
};
},
});
afterPrepare
AsyncWorkflow<void, void>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
afterPrepare: () => {
// do something
},
};
},
});
commands
prepare
Hook 运行完之后AsyncWorkflow<{ program: Command; }, void>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
commands: ({ program }) => {
program.command('foo').action(async () => {
// do something
console.log('foo');
});
},
};
},
});
将上面这个插件添加到 modern.config.ts
中:
import myPlugin from './config/plugin/myPlugin';
export default defineConfig({
plugins: [myPlugin()],
});
运行 modern foo
就可以看到控制台输出:
$ modern foo
foo
beforeExit
AsyncWorkflow<void, void>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
beforeExit: () => {
// do something
},
};
},
});
beforeDev
dev
命令运行时,项目开始启动前执行AsyncWorkflow<void, unknown>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
beforeDev: () => {
// do something
},
};
},
});
afterDev
dev
命令运行时,项目启动完成之后执行AsyncWorkflow<void, unknown>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
afterDev: () => {
// do something
},
};
},
});
beforeCreateCompiler
AsyncWorkflow<{ webpackConfigs: Configuration[];}, unknown>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
beforeCreateCompiler: ({ webpackConfigs }) => {
// do something
},
};
},
});
afterCreateCompiler
AsyncWorkflow<{ compiler: Compiler | MultiCompiler | undefined; }, unknown>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
afterCreateCompiler: ({ compiler }) => {
// do something
},
};
},
});
beforePrintInstructions
AsyncWaterfall<{ instructions: string }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
beforePrintInstructions: ({ instructions }) => {
// do something
return {
instructions: [...instructions, 'some new message'],
};
},
};
},
});
beforeBuild
build
命令运行时,项目构建启动前执行AsyncWorkflow<{ webpackConfigs: Configuration[]; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
beforeBuild: () => {
// do something
},
};
},
});
afterBuild
build
命令运行时,项目构建完成之后执行AsyncWorkflow<void, unknown>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
afterBuild: () => {
// do something
},
};
},
});
modifyEntryImports
import
语句prepare
阶段触发AsyncWaterfall<{ imports: ImportStatement[]; entrypoint: Entrypoint; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
modifyEntryImports({ entrypoint, imports }) {
// 添加 `import React from 'React'`
imports.push({
value: 'react',
specifiers: [
{
imported: 'unmountComponentAtNode',
},
],
});
return { entrypoint, imports };
},
};
},
});
modifyEntryExport
export
语句prepare
阶段触发AsyncWaterfall<{ entrypoint: Entrypoint; exportStatement: string; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
modifyEntryImports({ entrypoint, exportStatement }) {
return {
entrypoint,
exportStatement: [`export const foo = 'test'`, exportStatement].join(
'\n',
),
};
},
};
},
});
modifyEntryRuntimePlugins
prepare
阶段触发AsyncWaterfall<{ entrypoint: Entrypoint; plugins: RuntimePlugin[]; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
modifyEntryRuntimePlugins({ entrypoint, plugins }) {
const name = 'customPlugin';
const options = {
/** 可序列化的内容 */
};
return {
plugins: [
...plugins,
{
name,
options: JSON.stringify(options),
},
],
};
},
};
},
});
modifyEntryRenderFunction
render
函数prepare
阶段触发AsyncWaterfall<{ entrypoint: Entrypoint; code: string; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
modifyEntryRenderFunction({ entrypoint, code }) {
const customRender = `/** render function body */`;
return {
entrypoint,
code: customRender,
};
},
};
},
});
modifyFileSystemRoutes
prepare
阶段触发AsyncWaterfall<{ entrypoint: Entrypoint; routes: Route[]; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
modifyFileSystemRoutes({ entrypoint, routes }) {
return {
entrypoint,
routes: [
...routes,
{
path: '/custom_page',
component: require.resolve('./Component'),
exact: true,
},
],
};
},
};
},
});
这样就为前端新增了一个页面路由。
modifyServerRoutes
prepare
阶段触发AsyncWaterfall<{ routes: ServerRoute[]; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
modifyServerRoutes({ routes }) {
return {
routes: [
...routes,
{
urlPath: '/api/foo',
isApi: true,
entryPath: '',
isSPA: false,
isSSR: false,
},
],
};
},
};
},
});
modifyAsyncEntry
prepare
阶段触发AsyncWaterfall<{ entrypoint: Entrypoint; code: string; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
modifyAsyncEntry({ entrypoint, code }) {
const customCode = `console.log('hello');`;
return {
entrypoint,
code: `${customCode}${code}`,
};
},
};
},
});
htmlPartials
prepare
阶段触发AsyncWaterfall<{ entrypoint: Entrypoint; partials: HtmlPartials; }>
import type { CliPlugin } from '@modern-js/core';
export default (): CliPlugin => ({
setup(api) {
return {
async htmlPartials({ entrypoint, partials }) {
partials.head.push('<script>console.log("test")</script>');
return {
entrypoint,
partials,
};
},
};
},
});
这样就为 HTML 模版中新增了一个 Script 标签。
NOTE
目前 Server 插件还未完全开放,API 不保证稳定,使用需谨慎。
应用工程中的 Server 部分也支持了插件。其中的 Hook 将会提供一些特定阶段调用和特殊功能的 Hook。
create
measureOptions
和日志工具配置 loggerOptions
,并返回自定义的指标测量工具 measure
和日志工具配置 logger
AsyncPipeline<ServerInitInput, InitExtension>
import type { ServerPlugin } from '@modern-js/server-core';
export default (): ServerPlugin => ({
setup(api) {
return {
create: ({ measureOptions, loggerOptions }) => {
// do something
},
};
},
});
prepareWebServer
AsyncPipeline<WebServerStartInput, Adapter>
import type { ServerPlugin } from '@modern-js/server-core';
export default (): ServerPlugin => ({
setup(api) {
return {
prepareWebServer: ({ middleware }) => {
// do something
return (req, res) => {
// do response
};
},
};
},
});
prepareApiServer
AsyncPipeline<APIServerStartInput, Adapter>
import type { ServerPlugin } from '@modern-js/server-core';
export default (): ServerPlugin => ({
setup(api) {
return {
prepareApiServer: ({ middleware }) => {
// do something
return (req, res) => {
// do response
};
},
};
},
});
NOTE
目前 Runtime 插件还未完全开放,API 不保证稳定,使用需谨慎。
Runtime 插件主要用于开发者修改需要渲染的组件与 Element 和定制服务器端、客户端的渲染过程。
init
App.init
AsyncPipeline<{ context: RuntimeContext; }, unknown>
import type { Plugin } from '@modern-js/runtime';
export default (): Plugin => ({
setup(api) {
return {
init({ context }, next) {
// do something
return next({ context });
},
};
},
});
hoc
Pipeline<{ App: React.ComponentType<any>; }, React.ComponentType<any>>
import { createContext } from 'react';
import type { Plugin } from '@modern-js/runtime';
export default (): Plugin => ({
setup(api) {
const FooContext = createContext('');
return {
hoc({ App }, next) {
return next({
App: (props: any) => {
return (
<FooContext.Provider store={'test'}>
<App {...props} />
</FooContext.Provider>
);
},
});
},
};
},
});
provide
Pipeline<{ element: JSX.Element; props: AppProps; context: RuntimeContext }, JSX.Element>
import { createContext } from 'react';
import type { Plugin } from '@modern-js/runtime';
export default (): Plugin => ({
setup(api) {
const FooContext = createContext('');
return {
provide: ({ element }) => <div>{element}</div>,
};
},
});
client
AsyncPipeline<{ App: React.ComponentType<any>; context?: RuntimeContext; rootElement: HTMLElement; }, void>
import ReactDOM from 'react-dom';
import type { Plugin } from '@modern-js/runtime';
export default (): Plugin => ({
setup(api) {
return {
client: async ({ App, rootElement }) => {
ReactDOM.render(
React.createElement(App, { context: { foo: 'test' } }),
rootElement,
);
},
};
},
});
server
AsyncPipeline<{ App: React.ComponentType<any>; context?: RuntimeContext; }, string>
import ReactDomServer from 'react-dom/server';
import type { Plugin } from '@modern-js/runtime';
export default (): Plugin => ({
setup(api) {
return {
server({ App, context }) {
return ReactDomServer.renderToString(
React.createElement(App, { context: { foo: 'test' } }),
);
},
};
},
});