## 自定义App模板

- [工作原理](#create-cloudbase-app自定义App模板工作原理)
- [开发流程](#自定义App模板开发流程)
  - [开发](#开发App模板)
  - [注册](#注册App模板)
  - [示例](#示例)
- [API](#API)
  - [CloudBaseApp](#CloudBaseApp)
  - [renderTpl](#renderTpl)
  - [validPattern](#validPattern)
  - [matchDeps](#matchDeps)
  - [LANGUAGES-自定义类型](#LANGUAGES-自定义类型)
  - [DepsMap-自定义类型](#DepsMap-自定义类型)
### create-cloudbase-app自定义App模板工作原理
![](./images/原理.png)

### 自定义App模板开发流程
自定义App模板完整流程包括两大部分：
1. 开发并发布App模板包；
2. 注册App模板类型。

#### 开发App模板
1. 新建模板项目：
```bash
mkdir cloudbase-app-web-vue&cd "$_"
npm init
```

2. 安装开发者API模块：
```bash
# npm
npm i cloudbase-apptpl -D
# yarn
yarn add cloudbase-apptpl -D
```

3. 创建`index.ts`，并创建`CloudBaseApp`的子类，如下：
```typescript
import CloudBaseApp,{LANGUAGES,KV} from 'cloudbase-apptpl';
import { menu, MenuObject } from './i18n';
import { prompts } from './libs/prompts';

class VueApp extends CloudBaseApp{
  private readonly _menu:MenuObject;
  private readonly _pkgTool:string;
  constructor(appname:string,lang:LANGUAGES,pkgTool:string,options?:KV<any>){
    super(appname,lang,options);
    this._menu = menu[lang];
    this._pkgTool = pkgTool;
  }
  /**
   * @override 
   * @method run
   */
  async run(){
    const appInfo = await prompts(this._menu);
    // 生成项目文件逻辑
    this._installDeps(rootPath,this._pkgTool,deps);
  }
}

export default VueApp;
```

4. 发布
```bash
npm publish
```
#### 注册App模板
1. fork create-cloudbase-app；
2. 打开`create-cloudbase-app/src/templates.ts`，添加刚发布的项目模板：
```typescript
const templates:KV<TemplateSourceInfo> = {
  'vanilla': {
    'desc': '无框架原生webapp',
    'desc_en': 'Vanilla webapp',
    'package-name': 'cloudbase-app-web-vanilla'
  },
  // 新条目
  'vue': {
    'desc': 'Vue webapp',
    'desc_en': 'Vue webapp',
    'package-name': 'cloudbase-app-web-vue'
  }
}
```
其中`desc`和`package-name`是必填字段，`desc_en`可以置空字符串。
3. 发起Pull Request；
4. 等待主仓库管理员合并PR后即可使用。

#### 示例
[cloudbase-app-web-vanilla](https://git.code.oa.com/junpengzhou/cloudbase-app-web-vanilla)

### API
#### CloudBaseApp
```typescript
abstract class CloudBaseApp {
  protected _appname;
  protected _lang;
  protected _options;
  constructor(appname: string, lang: LANGUAGES, options?: KV<any>);
  abstract run(): any;
  protected _installDeps(path: string, packageTool: string, dependencies: string[]): void;
}
```
- `CloudBaseApp`是一个抽象类，子类必须实现抽象方法`run()`。App实例化之后会自动调用`run()`方法；
- `CloudBaseApp`类提供安装依赖的工具方法`_installDeps`；
- `protected`类型的属性和方法可在子类中直接访问。

#### renderTpl()
工具函数，将ejs模板文件渲染并写入目标文件
```typescript
function renderTpl(filePath: string, targetPath: string, locals?: KV<any>): void;
```

#### validPattern()
此函数用来验证`pattern`的合法性，一般用于调试。
```typescript
function validPattern(pattern: string): boolean;
```

`pattern`是匹配依赖的协议字符串，格式如下

```typescript
<pre>|<content>
```
- `pre`指的是工具类模块，比如webpack、rollup等。这类模块与`content`包含的功能模块通常有综合依赖关系，比如webpack与less搭配额外依赖`less-loader`。
- `content`指的是项目依赖的第三方模块，比如`vue`、`vue-router`等等。
`pre`和`content`的格式一致，如下：
```typescript
pre1&pre2|content1&content2
```


#### matchDeps()
工具函数，在depsMap中匹配pattern的依赖模块，以数组的形式返回。
```typescript
function matchDeps(pattern: string, depsMap: DepsMap): string[];
```

#### LANGUAGES-自定义类型
可选语言
```typescript
enum LANGUAGES {
  ZH = "zh",
  EN = "en"
}
```
#### DepsMap-自定义类型
必须包含common值。
```typescript
type DepsMap = KV<string[]> & {
  common: string[];
};
```