fes.js/docs/reference/plugin/dev/api.md

635 lines
13 KiB
Markdown
Raw Normal View History

2021-03-08 20:45:57 +08:00
# 插件 API
## 属性
### api.paths
一些关键的路径:
- cwd执行命令的绝对路径
- absNodeModulesPathnodeModule的绝对路径
- absOutputPath输出 `build` 产物的绝对路径
- absSrcPath`src` 目录的绝对路径
- absPagesPath`pages`目录的绝对路径
- absTmpPath`.fes`临时文件目录的绝对路径
### api.cwd
执行命令的绝对路径
### api.pkg
`package.json`的内容
### api.configInstance
`config`实例
### userConfig
用户配置
### config
插件配置可被修改,此为最终的配置
### env
process.env
### args
环境变量
## 核心方法
### describe
注册阶段执行,用于描述插件或插件集的 id、key、配置信息、启用方式等。
用法:**describe({ id?: string, key?: string, config?: { default, schema, onChange } }, enableBy?)**
例如:
```js
api.describe({
key: 'esbuild',
config: {
schema(joi) {
return joi.object();
},
default: {}
},
enableBy: api.EnableBy.config,
});
```
注:
- `config.default` 为配置的默认值,用户没有配置时取这个
- `config.schema` 用于声明配置的类型,基于 [joi](https://hapi.dev/module/joi),如果你希望用户进行配置,这个是必须的,否则用户的配置无效
- `config.onChange``dev` 阶段配置被修改后的处理机制,默认会重启 dev 进程,也可以修改为 api.ConfigChangeType.regenerateTmpFiles 只重新生成临时文件,还可以通过函数的格式自定义
- `enableBy` 为启用方式,默认是注册启用,可更改为 `api.EnableBy.config`,还可以用自定义函数的方式决定其启用时机(动态生效)
2022-01-06 12:57:22 +08:00
### register
为 api.applyPlugins 注册可供其使用的 hook。
用法:**register({ key: string, fn: Function, pluginId?: string, before?: string, stage?: number })**
参数:
- key唯一id
- fnhook函数当执行`api.applyPlugins`时,此函数被执行。
- pluginId插件id如果配置了插件id则只有此插件未被禁用时才会执行。
```js
// 可同步
api.register({
key: 'foo',
fn() {
return 'a';
},
});
// 可异步
api.register({
key: 'foo',
async fn() {
await delay(100);
return 'b';
},
});
```
注意:
- fn 支持同步和异步,异步通过 Promise返回值为 Promise 即为异步
- fn 里的内容需结合 `api.appyPlugins``type` 参数来看,如果是 `api.ApplyPluginsType.add`,需有返回值,这些返回值最终会被合成一个数组。如果是 `api.ApplyPluginsType.modify`需对第一个参数做修改并返回它它会作为下个hook的参数。 如果是 `api.ApplyPluginsType.event`,无需返回值
- stage 和 before 都是用于调整执行顺序的,参考 tapable
- stage 默认是 0设为 -1 或更少会提前执行,设为 1 或更多会后置执行
2021-03-08 20:45:57 +08:00
### applyPlugins
取得 register 注册的 hooks 执行后的数据。
用法:**applyPlugins({ key: string, type: api.ApplyPluginsType, initialValue?: any, args?: any })**
参数:
2022-01-06 12:57:22 +08:00
- key唯一id
- typehook的类型。
- initialValue初始值。
- args参数hook函数执行时args会作为参数传入。
2021-03-08 20:45:57 +08:00
例如:
```js
const foo = await api.applyPlugins({
key: 'foo',
type: api.ApplyPluginsType.add,
initialValue: [],
});
console.log(foo); // ['a', 'b']
```
#### api.ApplyPluginsType
编译时插件hook执行类型enum 类型,包含三个属性:
- compose用于合并执行多个函数函数可决定前序函数的执行时机
- modify用于修改值
- event用于执行事件前面没有依赖关系
2022-01-06 12:57:22 +08:00
### registerMethod
`api` 上注册方法。如果有提供 `fn`,则执行 `fn` 定义的函数。
用法:**registerMethod({ name: string, fn?: Function, exitsError?: boolean })**
例如:
```js
api.registerMethod({
name: 'writeTmpFile',
fn({
path,
content
}) {
assert(
api.stage >= api.ServiceStage.pluginReady,
'api.writeTmpFile() should not execute in register stage.'
);
const absPath = join(api.paths.absTmpPath, path);
api.utils.mkdirp.sync(dirname(absPath));
if (!existsSync(absPath) || readFileSync(absPath, 'utf-8') !== content) {
writeFileSync(absPath, content, 'utf-8');
}
}
});
```
然后在插件中可以使用:
```js
api.writeTmpFile()
```
2021-03-08 20:45:57 +08:00
### registerCommand
注册命令,基于 [commander](https://github.com/tj/commander.js/) 实现的机制。
用法:**registerCommand({ command: string, description: string, fn: Function, options?: Object })**
参数:
- command
- description描述文字输入 `--help` 会打印
- fn命令执行的函数参数有
- rawArgv原始参数
- args参数
- options执行命令时附带的的参数配置
- programcommander对象
- options参数配置基于 [commander](https://github.com/tj/commander.js/) 。
例如:
```js
api.registerCommand({
command: 'webpack',
description: 'inspect webpack configurations',
options: [{
name: '--rule <ruleName>',
description: 'inspect a specific module rule'
}, {
name: '--plugin <pluginName>',
description: 'inspect a specific plugin'
}, {
name: '--rules',
description: 'list all module rule names'
}, {
name: '--plugins',
description: 'list all plugin names'
}, {
name: '--verbose',
description: 'show full function definitions in output'
}],
async fn({ rawArgv, args, options, program}) {
}
})
```
当项目引入此插件后,使用:
```bash
fes webpack
```
### registerPresets
注册插件集,参数为路径数组。
用法:**registerPresets(presets: string[])**
例如:
```js
api.registerPresets([
{ id: 'preset_2', key: 'preset2', apply: () => () => {} },
require.resolve('./preset_3'),
]);
```
### registerPlugins
注册插件,参数为路径数组。
用法:**registerPlugins(plugins: string[])**
例如:
```js
api.registerPlugins([
{ id: 'preset_2', key: 'preset2', apply: () => () => {} },
require.resolve('./preset_3'),
]);
```
### hasPlugins
判断是否有注册某个插件,插件的 id 规则:
- id 默认为包名
- 内置插件以 `@@` 为前缀,比如 `@@/registerMethod`
用法:**hasPlugins(pluginIds: string[])**
例如
```js
// 判断是否有注册 @fesjs/plugin-locale
api.hasPlugins(['@fesjs/plugin-locale']);
```
::: tip
如果在注册阶段使用,只能判断在他之前是否有注册某个插件。
:::
### hasPresets
判断是否有注册某个插件集。
用法:**hasPresets(presetIds: string[])**
例如
```js
// 判断是否有注册
api.hasPlugins(['@fesjs/preset-xxx']);
```
::: tip
如果在注册阶段使用,只能判断在他之前是否有注册某个插件集。
:::
2022-01-06 12:57:22 +08:00
### skipPlugins
声明哪些插件需要被禁用,参数为插件 id 的数组。
用法:**hasPresets(presetIds: string[])**
例如:
```js
// 禁用 plugin-model 插件
api.skipPlugins(['@fesjs/plugin-model']);
```
2021-03-08 20:45:57 +08:00
## 扩展方法
通过 api.registerMethod() 扩展的方法。
### addPluginExports
把插件需要导出的运行时 API 写入`@fesjs/fes`。
```js
api.addPluginExports(() => [
{
specifiers: ['access', 'useAccess'],
source: absoluteFilePath
}
]);
```
这样用户使用时:
```js
import { access, useAccess } from '@fesjs/fes';
```
2022-01-06 12:57:22 +08:00
### addCoreExports
提供给其他插件运行时需要的 API。
```js
api.addCoreExports(() => [
{
specifiers: ['getRoutes', 'getRouter', 'getHistory', 'destroyRouter'],
source: absCoreFilePath
}
]);
```
使用:
```js
import { getHistory, destroyRouter } from '@@/core/coreExports';
```
2021-03-08 20:45:57 +08:00
### addRuntimePlugin
2022-01-06 12:57:22 +08:00
添加运行时插件,返回值格式为表示文件路径的字符串。
2021-03-08 20:45:57 +08:00
例如:
```js
api.addRuntimePlugin(() => join(__dirname, './runtime'));
```
### addRuntimePluginKey
2022-01-06 12:57:22 +08:00
添加插件提供的运行时配置的 key返回值格式为字符串。
2021-03-08 20:45:57 +08:00
例如:
```js
api.addRuntimePluginKey(() => 'some');
2022-01-06 12:57:22 +08:00
```
则用户可以:
```js
// app.js
const some = function(){
return {
}
}
2021-03-08 20:45:57 +08:00
```
### addEntryImportsAhead
在入口文件现有 import 的前面添加 import。
例如:
```js
api.addEntryImportsAhead(() => [{ source: 'anypackage' }]);
```
### addEntryImports
在入口文件现有 import 的后面添加 import。
例如:
```js
api.addEntryImport(() => {
return [
{
source: '/modulePath/xxx.js',
specifier: 'moduleName',
}
]
});
```
### addEntryCodeAhead
在入口文件最前面import 之后)添加代码。
例如:
```js
api.addEntryCodeAhead(
() => `${globalCSSFile
.map(file => `require('${winPath(relative(absTmpPath, file))}');`)
.join('')}`
```
### addEntryCode
在入口文件最后添加代码。
例如:
```js
api.addEntryCode(() => {
return `console.log('works!')`
})
```
2022-01-06 12:57:22 +08:00
### addHTMLHeadScripts
在 HTML 头部添加脚本。
例如:
```js
api.addHTMLHeadScripts(() => {
return [
{
content: '',
src: '',
// ...attrs
},
];
});
```
2021-03-08 20:45:57 +08:00
### addBeforeMiddlewares
添加在 `webpack compiler` 中间件之前的中间件,返回值格式为 `express` 中间件。
例如:
```js
api.addBeforeMiddlewares(() => {
return (req, res, next) => {
if (false) {
res.end('end');
} else {
next();
}
};
});
```
### addMiddlewares
添加在 `webpack compiler` 中间件之后的中间件,返回值格式为 `express` 中间件。
2022-01-06 12:57:22 +08:00
### addTmpGenerateWatcherPaths
添加重新临时文件生成的监听路径。
例如:
```js
api.addTmpGenerateWatcherPaths(() => [
'./app.js',
]);
```
### chainWebpack
通过 [webpack-chain] 的方式修改 webpack 配置。
例如:
```js
api.chainWebpack((memo) => {
memo.resolve.alias.set('vue-i18n', 'vue-i18n/dist/vue-i18n.esm-bundler.js');
});
```
### copyTmpFiles
批量写临时文件。
例如:
```js
api.copyTmpFiles({
namespace,
path: join(__dirname, 'runtime'),
ignore: ['.tpl']
});
```
参数:
- namespace复制到临时文件夹下的目标目录
- path需要复制的文件目录
- ignore需要排除的文件
::: tip
不能在注册阶段使用,通常放在 api.onGenerateFiles() 里,这样能在需要时重新生成临时文件
临时文件的写入做了缓存处理,如果内容一致,不会做写的操作,以减少触发 `webpack` 的重新编译
:::
### getPort
获取端口号dev 时有效。
### getHostname
获取 hostnamedev 时有效。
### getServer
获取 devServerdev 时有效。
### getRoutes
获取 `api.modifyRoutes` 修改过后的路由信息。
### getRoutesJSON
获取格式化后的路由信息
2021-03-08 20:45:57 +08:00
### modifyRoutes
修改路由。
例如:
```js
// 把BaseLayout插入到路由配置中作为根路由
api.modifyRoutes(routes => [
{
path: '/',
component: winPath(
join(api.paths.absTmpPath || '', absFilePath)
),
children: routes
}
]);
```
### modifyBundleConfigOpts
修改获取 bundleConfig 的函数参数。
例如:
```js
api.modifyBundleConfigOpts(memo => {
memo.miniCSSExtractPluginPath = require.resolve('mini-css-extract-plugin');
memo.miniCSSExtractPluginLoaderPath = require.resolve(
'mini-css-extract-plugin/dist/loader',
);
return memo;
});
```
### modifyBundleConfig
修改 bundle 配置。
```js
api.modifyBundleConfig((bundleConfig) => {
// do something
return bundleConfig;
});
```
### modifyBabelOpts
修改 babel 配置项。
例如:
```js
api.modifyBabelOpts((babelOpts) => {
if (api.config.babelPluginImport) {
api.config.babelPluginImport.forEach((config) => {
babelOpts.plugins.push(['import', config]);
});
}
return babelOpts;
});
```
### modifyBabelPresetOpts
修改 babel 插件的配置。
例如:
```js
api.modifyBabelPresetOpts(opts => {
return {
...opts,
import: (opts.import || []).concat([
{ libraryName: 'ant-design-vue', libraryDirectory: 'es', style: true },
]),
};
});
```
### modifyPaths
修改 paths 对象。
2022-01-06 12:57:22 +08:00
### modifyDefaultConfig
修改默认配置。
例如:
```js
api.modifyDefaultConfig((memo) => {
return {
...memo,
...defaultOptions,
};
});
```
2021-03-08 20:45:57 +08:00
### modifyConfig
修改最终配置。
例如:
```js
api.modifyConfig((memo) => {
return {
...memo,
...defaultOptions,
};
});
```
2022-01-06 12:57:22 +08:00
### modifyPublicPathStr
修改 publicPath 字符串。
2021-03-08 20:45:57 +08:00
例如:
```js
2022-01-06 12:57:22 +08:00
api.modifyPublicPathStr(() => {
return api.config.publicPath || '/';
2021-03-08 20:45:57 +08:00
});
```
2022-01-06 12:57:22 +08:00
### onPluginReady
在插件初始化完成触发。在 onStart 之前,此时还没有 config 和 paths他们尚未解析好。
2021-03-08 20:45:57 +08:00
2022-01-06 12:57:22 +08:00
### onStart
在命令注册函数执行前触发。可以使用 config 和 paths。
### onExit
dev 退出时触发。
### onGenerateFiles
生成临时文件,触发时机在 webpack 编译之前。
### restartServer
重启 devServerdev 时有效。
2021-03-08 20:45:57 +08:00
### writeTmpFile
写临时文件。
例如:
```js
api.writeTmpFile({
path: absoluteFilePath,
content: Mustache.render(
readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'),
{
REPLACE_ROLES: JSON.stringify(roles)
}
)
});
```
参数:
- path相对于临时文件夹的路径
- content文件内容
::: tip
不能在注册阶段使用,通常放在 api.onGenerateFiles() 里,这样能在需要时重新生成临时文件
临时文件的写入做了缓存处理,如果内容一致,不会做写的操作,以减少触发 webpack 的重新编译
:::