Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs/plugin dev #3345

Merged
merged 7 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 126 additions & 1 deletion docs/guide/develop/plugin-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ order: 2

插件工程能力通过 `src/index.ts` 定义,结构如下


```javascript
module.exports = ({ context, onGetWebpackConfig, log, onHook }, options) => {
module.exports = ({ context, onGetWebpackConfig, log, onHook, ...rest }, options) => {
// 第一项参数为插件 API 提供的能力
// options:插件自定义参数
};
Expand Down Expand Up @@ -42,6 +43,19 @@ module.exports = ({onGetWebpackConfig, registerTask}) => {
}
```

### onGetJestConfig

通过 `onGetJestConfig` 获取 jest 配置,可对配置进行自定义修改:

```javascript
module.exports = ({onGetJestConfig}) => {
onGetJestConfig((jestConfig) => {
const modifiedJestConfig = modify(jestConfig);
return modifiedJestConfig;
});
};
```

### onHook

通过 onHook 监听命令运行时事件,onHook 注册的函数执行完成后才会执行后续操作,可以用于在命令运行中途插入插件想做的操作:
Expand Down Expand Up @@ -95,6 +109,85 @@ log.verbose('debug');
log.error('exit');
```

### registerUserConfig

为用户配置文件 `build.json` 中添加自定义字段。

```javascript
module.exports = ({registerUserConfig}) => {
registerUserConfig({
name: 'custom-key',
validation: 'boolean' // 可选,支持类型有 string, number, array, object, boolean
});
};
```

### registerClioption

为命令行启动添加自定义参数。

```javascript
module.exports = ({registerClioption}) => {
registerCliOption({
name: 'custom-option', // 参数名
commands: ['start'], // 命令
configWebpack: (arg) => {} // 可选,arg 为命令行参数对应值
});
};
```

### registerMethod

注册自定义方法。通过 `applyMethod` 调用。

```javascript
module.exports = ({registerMethod}) => {
registerMethod(name, func); // name, func 分别为方法名和方法
};
```

### modifyUserConfig

修改用户配置文件。

```javascript
module.exports = ({modifyUserConfig}) => {
modifyUserConfig(key, value); // key, value 分别为用户配置文件键值对
};
```

### registerTask

添加 webpack 配置,配置为 webpack-chain 形式。

```javascript
module.exports = ({registerTask}) => {
registerTask(name, config); // name: Task名, config: webpack-chain 形式的配置
};
```

### getAllTask

获取所有 webpack 配置名称。

```javascript
module.exports = ({getAllTask}) => {
const alltasks = getAlltask();
};
```

### getAllPlugin

获取所有插件。

```javascript
module.exports = ({getAllPlugin}) => {
// 获取所有插件数组
// 类型:() => [{pluginPath, options, name}]
const plugins = getAllPlugin(); ,[]
}
```

## 扩展 API

除了以上由 build-scripts 内置支持的 API,我们还通过 icejs 对插件 API 做了扩展,扩展的 API 需要通过以下方式调用:
Expand Down Expand Up @@ -139,6 +232,31 @@ this.applyMethod('addPageExport', 'Home', { source: './models', 'store' })

与 `addPageExport` 对应

### addIceAppConfigTypes

向 appConfig 添加类型

```javascript
// 第一项参数对应 API 名称,第二项参数对应 API 参数。
//
// API 参数:
// source: 类型声明文件。./foo/types,对应 ICE_TEMP_DIR/foo/types。 ICE_TEMP_DIR,可通过 getValue('ICE_TEMP') 获得。注意:需先将对应类型文件移至 ICE_TEMP_DIR。
// specifier: 导出类型标识符,可选,默认值为 '*'。
// exportName: 添加至 appConfig 类型 IAppConfig 上的导出名。
//
// 结果为:
// // ICE_TEMP_DIR/types.ts
// import { Foo } from './foo/types';
// export interface IAppConfig {
// foo?: Foo
// }
applyMethod('addIceAppConfigTypes', { source: `./foo/types`, specifier: '{ Foo }', exportName: `foo?: Foo` });
```

### removeIceAppConfigTypes

与 `addIceAppConfigTypes` 对应

### getPages

获取 `src/pages` 下的一级页面列表:
Expand Down Expand Up @@ -231,3 +349,10 @@ module.exports = ({getValue}) => {
const projectType = getValue('PROJECT_TYPE'); // ts|js
const iceDirPath = getValue('ICE_TEMP'); // 对应 .ice 的路径
```

## 类型

接口类型通过以下方法引入:
```javascript
import { IPlugin } from '@alib/build-scripts';
```
136 changes: 126 additions & 10 deletions docs/guide/develop/plugin-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ title: 插件开发指南
order: 1
---

icejs 基于工程构建工具 build-scripts 封装,因此在插件能力上也完整继承了 build-scrtips。除了通过插件定制工程能力以外,icejs 还为插件扩展了运行时定制的能力,这让插件拥有更多的想象空间。
icejs 基于工程构建工具 build-scripts 封装,因此在插件能力上也完整继承了 build-scripts。除了通过插件定制工程能力以外,icejs 还为插件扩展了运行时定制的能力,这让插件拥有更多的想象空间。

插件机制是 icejs 的核心之一,当前 icejs 的基础能力都是通过插件来实现。插件机制不但可以保证框架核心足够精简和稳定,还可以通过插件对运行时和编译时的能力进行封装复用,最终打造一个完整的生态。

## 快速开始

使用 [CLI](/docs/guide/start#使用%20CLI) 初始化项目。选择插件模板:

```shell
$ npm init ice <your-plugin-name>
```

## 插件目录

通常情况下,插件通过 npm 包的形式分发,插件初始化目录如下:
Expand All @@ -15,17 +23,17 @@ icejs 基于工程构建工具 build-scripts 封装,因此在插件能力上
.
├── src
│   ├── index.[t,j]s # 插件工程入口
│   └── module.[t,j]s # 插件编译时入口
├── lib/ # 编译目录
├── package.json
├── README.md
└── tsconfig.json
│   └── runtime.[t,j]s # 插件编译时入口
├── tests # 测试文件目录
├── package.json # npm 包配置
├── tsconfig.json # typescript 配置文件
└── README.md # 说明文档
```

这里以 ts 为例,实际上也可以通过 js 编写插件。插件核心有两个文件:
这里以 ts 为例,实际上也可以通过 js 编写插件。 ts 最终应编译为 js 以发布 npm 包。插件核心有两个文件:

1. `index.ts`:通常用于做一些工程相关的事情,比如更改 webpack 配置、构建结束后执行一些其他任务等
2. `runtime.ts`:实现一些运行时能力,比如 config/request 插件。注意: 旧版本的`module.ts`暂时兼容,但在未来不受支持。
1. `index.ts`:通常用于做一些工程相关的事情,比如更改 webpack 配置、构建结束后执行一些其他任务等。需保证该文件作为 npm 包入口。
2. `runtime.ts`:实现一些运行时能力,比如 config/request 插件。注意: 旧版本的`module.ts`暂时兼容,但在未来不受支持。需保证该文件与 `index.ts` 位于同一目录下。

下面也会按照这两个纬度来分别介绍。

Expand All @@ -41,6 +49,114 @@ icejs 基于工程构建工具 build-scripts 封装,因此在插件能力上

关于 `runtime.ts` 应该如何书写请参考下一个章节的文档 [通过插件定制运行时能力](/docs/guide/develop/plugin-runtime.md)。

## 单元测试

使用 [Jest](https://github.com/facebook/jest) 进行单元测试。

### 插件开发示例

以 [`plugin-logger`](https://github.com/alibaba/ice/tree/master/packages/plugin-logger) 为例。该插件采用 typescript 编写,对工程能力及运行时能力均进行了修改。为框架提供了日志功能。

目录结构:

```json
.
├── README.md
├── template
│   └── index.ts // logger 功能实现
LanceZhu marked this conversation as resolved.
Show resolved Hide resolved
├── package.json
├── src
│   ├── index.ts // 工程能力实现
│   ├── runtime.ts // 运行时能力实现
│   └── types
chenbin92 marked this conversation as resolved.
Show resolved Hide resolved
│   └── index.ts // 类型声明文件
└── tsconfig.json
```

#### 类型和扩展

* 类型声明:

```typescript
// src/types/index
export interface ILogger {
level: string;
};
```

* 扩展 appConfig 类型

```typescript
// src/index.ts
import * as path from 'path';
import * as fse from 'fs-extra';
import { IPlugin } from '@alib/build-scripts';

const plugin: IPlugin = async ({ getValue, applyMethod }): Promise<void> => {
const exportName = 'logger';
const distPath = path.join(getValue('ICE_TEMP'), exportName);
await fse.copy(path.join(__dirname, './types'), path.join(distPath, 'types')); // 复制类型声明文件

// 挂载至 appConfig。 appConfig 对应类型为 IAppConfig
// source 为复制后的目录, specifier 为类型标识符,exportName 为 appConfig 类型名
// 得到以下结果
// import { ILogger } from './logger/types'
// export interface IAppConfig {
// logger?: ILogger;
// }
applyMethod('addIceAppConfigTypes', { source: `./${exportName}/types`, specifier: '{ ILogger }', exportName: `${exportName}?: ILogger` });
};
```

#### 工程化能力实现

* logger 功能实现

```typescript
// src/logger/index.ts
import * as logger from 'loglevel';

export default logger;
```

* 导出至 ice

```typescript
// src/index.ts
import * as path from 'path';
import * as fse from 'fs-extra';
import { IPlugin } from '@alib/build-scripts';

const plugin: IPlugin = async ({ getValue, applyMethod, onGetWebpackConfig }): Promise<void> => {
const exportName = 'logger';
const distPath = path.join(getValue('ICE_TEMP'), exportName);
await fse.copy(path.join(__dirname, `../${exportName}`), distPath);
// 导出 logger 功能
// 用户可通过 import { logger } from 'ice'; 使用
applyMethod('addIceExport', { source: `./${exportName}`, exportName });

onGetWebpackConfig((config) => {
// 为 logger 添加 webpack alias,供运行时能力调用
config.resolve.alias.set('$ice/logger', distPath);
});
};
```

#### 运行时能力实现

```typescript
import logger from '$ice/logger'; // $ice/logger 通过工程化能力设置 alias

const module = ({ appConfig }) => {
// 设置运行时 logger 等级
if (appConfig.logger && appConfig.logger.level) {
logger.setLevel(appConfig.logger.level);
}
};

export default module;
```

## 示例插件

官方插件代码:https://github.com/ice-lab/icejs/tree/master/packages
官方插件代码:https://github.com/alibaba/ice/tree/master/packages
Loading