<p align="center">
    <img src="https://gitee.com/agile-development-system/agds-doc-preset/raw/master/lib/docs/logos/light/1.png" alt="logo">
</p>

# @agds/cli-plugin-doc

**版本** ：1.0.16

agds系统doc文档生成器

## 快速开始

### 安装

```bash
npm i -D @agds/cli-plugin-doc
```

### 命令行使用文档

```
Usage: agds-doc [options]

agds系统doc文档生成器

Options:
  <files...>                          jsdoc入口文件glob格式路径描述(需要用引号包裹避免解析失败)，相对于cwd目录
  -o,--output <output>                doc文档渲染导出的文件名称路径，相对于cwd目录
  -c,--config <config>                配置文件路径，相对于cwd目录，仅支持js文件类型 (default:
                                      "agds.doc.config.js")
  -t,--template <template>            ejs渲染的模板相对于cwd的路径或者绝对路径
  --cd,--codes-dir <codesDir>         glob格式路径，代码演示示例的对应文件夹路径，路径需要到某个具体示例的对应文件夹
  --cf,--codes-files <codesFiles...>  glob格式路径，相对于codesDir的代码演示文件夹的文件路径描述
  --no-default                        禁止使用默认配置，默认配置相对比较通用，大部分情况不需要禁止，当默认配置和你的配置冲突时可以使用此选项
  -v,--version                        查看版本号
  -h, --help                          查看帮助信息

注意：每个包含通配符的路径都需要用引号包裹，否则会被系统提前解析导致意料之外的错误

文档查看：https://gitee.com/agile-development-system/cli-plugin-doc
@agds/cli-plugin-doc@1.0.12 /Users/jinyang/code/ads/cli-plugin-doc/node_modules/@agds/cli-plugin-doc

```

### 配置文件

默认为当前目录下的`agds.doc.config.js`，自动合并[默认配置](#defaultConfig)

可以通过命令行参数`-c --config <config>`或者node api的`options.config` 来指定配置文件名称

可以通过命令行参数`--no-default`或者node api的`options.default=false` 来禁止使用默认配置，默认配置相对比较通用，大部分情况不需要禁止，当默认配置和你的配置冲突时可以使用此选项

配置文件导出类型为[👉`RenderOptions`](#RenderOptions),理论上支持所有的renderOption，由默认模板提供的`helpers`配置请看[👉默认模板支持的helpers](#DefaultHelpers)、[👉默认模板](#defaultTemplate)

## 代码演示

```js

const GenDoc = require('@agds/cli-plugin-doc');
/**
 * render配置生成
 *
 * @param {object} [options={}] 选项
 * @param {boolean} [options.needDirError] 是否需要触发文件路径错误
 * @param {boolean} [options.noFiles] 是否需要去除files选项
 * @param {boolean} [options.noDefault] 是否取消default配置
 * @param {boolean} [options.noCodes] 是否去除codes相关配置
 * @returns {import('../../src/index').RenderOptions}
 */
module.exports = async ({ needDirError, noFiles, noDefault, noCodes } = {}) => {
    const [template, defaultConfig, cliUsages] = (await Promise.all([
        GenDoc.getFilesCode({ dir: './src/template', files: ['*'] }),
        GenDoc.getFilesCode({ dir: './src/utils', files: ['config.js'] }),
        GenDoc.getCliUsages(),
    ]));
    return {
        files: noFiles ? null : ['./src/**/*.js'],
        ...(noCodes
            ? {}
            : {
                codesDir: needDirError ? './aaa' : './exa',
                codesFiles: ['*'],
            }
        ),
        config: './agds.doc.conf.js',
        default: !noDefault,
        jsdocEngineOptions: noDefault && {
            plugins: [
                require.resolve('jsdoc-tsimport-plugin'),
            ],
        },
        helpers: {
            template,
            defaultConfig,
            cliUsages,
        },
    };
};
```
```js
const { expect, test } = require('@jest/globals');
const GenDoc = require('@agds/cli-plugin-doc');
const config = require('../__mock__/index');
const path = require('path');
test('GenDoc render', async () => {
    const res = await GenDoc.render(await config());
    expect(typeof res === 'string').toBe(true);
});

test('GenDoc render output & use agds.doc.config.js', async () => {
    const res = await GenDoc.render({
        output: path.resolve(__dirname, '../../.temp/README.md'),
    });
    expect(typeof res === 'undefined').toBe(true);
});

test('GenDoc render no files', async () => {
    const res = await GenDoc.render(await config({ noFiles: true }));
    expect(typeof res === 'string').toBe(true);
});

test('GenDoc render no codes', async () => {
    const res = await GenDoc.render(await config({ noCodes: true }));
    expect(typeof res === 'string').toBe(true);
});

test('GenDoc render error', async () => {
    try {
        await GenDoc.render(await config({ needDirError: true }));
    } catch (error) {
        expect(error.message).toMatch('未匹配到任何目录，请确认输入路径');
    }
});

test('GenDoc getRenderData nodefault', async () => {
    const res = await GenDoc.getRenderData(await config({ noDefault: true }));
    expect(typeof res === 'object').toBe(true);
});

test('GenDoc getRenderData', async () => {
    const res = await GenDoc.getRenderData(await config(), false);
    expect(typeof res === 'object').toBe(true);
});

test('GenDoc getFileContent', () => {
    const res = GenDoc.getFileContent('./README.md');
    expect(typeof res === 'string').toBe(true);
});
```

## API文档

<a name="GenDoc"></a>

### GenDoc

GenDoc 基于注释和可运行的示例代码自动生成文档的强大工具类

#### 引入

```js
const GenDoc = require('@agds/cli-plugin-doc');
```

**性质**: 类

<a name="GenDoc.render"></a>

#### GenDoc.render(options) ⇒ <code>Promise.&lt;string&gt;</code>

基于ejs，用模板渲染文档

**性质**: [<code>GenDoc</code>](#GenDoc)的静态方法
**返回值**: <code>Promise.&lt;string&gt;</code> - 异步返回基于ejs模板渲染的文档文本

| 参数 | 类型 | 描述 |
| --- | --- | --- |
| options | [<code>RenderOptions</code>](#GenDoc.render_alias_RenderOptions) | 获取用来渲染模板的数据 |


<a name="GenDoc.render_alias_RenderOptions"></a>

##### RenderOptions : <code>object</code>

渲染函数的配置参数

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

**属性**

| 属性 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| files | <code>Array.&lt;string&gt;</code> |  | `jsdoc2mdOptions.files`的别名 |
| output | <code>fs.PathLike</code> |  | doc文档渲染导出的文件名称路径，相对于cwd目录 |
| template | <code>string</code> |  | ejs渲染的模板相对于cwd的路径或者绝对路径 |
| [codesDir] | <code>string</code> |  | `codesOptions.dir`的别名 |
| [codesFiles] | <code>Array.&lt;string&gt;</code> |  | `codesOptions.codesFiles`的别名 |
| [conifg] | <code>fs.PathLike</code> | <code>agds.doc.config.js</code> | 配置文件路径，默认为运行目录下的`agds.doc.config.js`,仅支持`js`文件类型 |
| [default] | <code>boolean</code> |  | 是否合并默认配置，一般我们认为您是需要默认配置的，当默认配置和你的需求冲突时可以设置为`false` |
| [jsdoc2mdOptions] | [<code>Jsdoc2mdOptions</code>](#GenDoc.render_alias_Jsdoc2mdOptions) |  | jsdocToMarkdown配置参数 |
| [codesOptions] | [<code>GetFilesCodeOptions</code>](#GenDoc.render_alias_GetFilesCodeOptions) |  | 获取源代码的文件路径配置参数 |
| [jsdocEngineOptions] | [<code>JsdocEngineOptions</code>](#GenDoc.render_alias_JsdocEngineOptions) |  | jsdoc解析引擎的配置，实际上是`jsdoc.conf.js`的整合， 也可以使用  `RenderOptions.jsdoc2mdOptions.configure`字段来指定本地的jsdoc配置 配置选项[👉参考文档](https://jsdoc.app/about-configuring-jsdoc.html) |
| [helpers] | [<code>DefaultHelpers</code>](#GenDoc.render_alias_DefaultHelpers) |  | 注入ejs模板的`helpers`对象，提供模板使用的帮助函数和变量，配合模板使用 |
| [presets] | [<code>Array.&lt;RenderOptions&gt;</code>](#GenDoc.render_alias_RenderOptions) |  | 基于preset机制实现配置支持预设的功能， 具体[👉参考文档](https://gitee.com/agile-development-system/node-utils#presetutilsgetdeeppresetmergeconfig--config)`PresetUtils.getDeepPresetMerge` |
| [modify] | <code>module:@agds/node-utils~ConfigModify</code> |  | 将默认配置和preset合并后生成的config再次处理的钩子 具体[👉参考文档](https://gitee.com/agile-development-system/node-utils#presetutilsgetdeeppresetmergeconfig--config) |


<a name="GenDoc.render_alias_DefaultHelpers"></a>

##### DefaultHelpers : <code>Object</code>

默认模板所支持的`helpers`属性

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

**属性**

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| [installCode] | <code>string</code> | 安装脚本，bash脚本，默认为`npm i ${pkg.name}`，如不符合要求，可以通过此字段自行修改 |
| [devInstall] | <code>boolean</code> | 是否是作为开发依赖下载，`true`时，默认下载代码自动拼接npm `-D` 参数 |
| [importCode] | <code>string</code> | 引入代码示例，js字符串 |
| [exportCode] | <code>string</code> | 导出代码，js字符串 |
| [cliUsages] | <code>Array.&lt;string&gt;</code> | cli命令行使用帮助文档，格式类似`agds-doc -h`的输出内容 |
| [remark] | <code>string</code> | 文档备注信息，md字符串 |
| [renderCode] | [<code>renderCode</code>](#GenDoc.renderCode) | 将`GenDoc.getFileCodes`的返回值渲染成对应的代码段 |
| [postfixes] | [<code>Array.&lt;Postfix&gt;</code>](#GenDoc.render_alias_Postfix) | 后缀内容数组 |
| [logo] | <code>string</code> | logo |
| [bradges] | [<code>Array.&lt;Badge&gt;</code>](#GenDoc.render_alias_Badge) | 徽标数组 |


<a name="GenDoc.render_alias_Badge"></a>

##### Badge : <code>Object</code>

徽标对象

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

**属性**

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| url | <code>string</code> | 图片链接 |
| [name] | <code>string</code> | 图片名称 |
| [link] | <code>string</code> | 跳转链接 |


<a name="GenDoc.render_alias_Postfix"></a>

##### Postfix : <code>Object</code>

后缀内容类型

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

**属性**

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| [id] | <code>string</code> | 锚点的名称，填写之后可以支持 `href="#${id}"`锚点定位 |
| [title] | <code>string</code> | 内容的标题 |
| [desc] | <code>string</code> | 内容的描述 |
| [content] | <code>string</code> | 内容的正文 |


<a name="GenDoc.render_alias_JsdocEngineOptions"></a>

##### JsdocEngineOptions : <code>Object</code>

jsdoc引擎配置[👉参考文档](https://jsdoc.app/about-configuring-jsdoc.html)

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

**属性**

| 属性 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| plugins | <code>Array.&lt;string&gt;</code> |  | 插件路径数组(建议使用绝对路径) |
| recurseDepth | <code>number</code> |  | 指定递归深度 |
| source | <code>object</code> |  | 指定输入文件 |
| source.include | <code>Array.&lt;string&gt;</code> |  | 路径数组，包含 JSDoc 应为其生成文档的文件 |
| source.exclude | <code>Array.&lt;string&gt;</code> |  | JSDoc 应该忽略的可选路径数组 |
| source.includePattern | <code>string</code> |  | 可选字符串，解释为正则表达式,如果存在，则所有文件名都必须与此正则表达式匹配才能由 JSDoc 处理 |
| source.excludePattern | <code>string</code> |  | 可选字符串，解释为正则表达式。如果存在，任何匹配此正则表达式的文件都将被忽略。 |
| [sourceType] | <code>&#x27;module&#x27;</code> \| <code>&#x27;script&#x27;</code> | <code>&#x27;module&#x27;</code> | 指定源类型,会影响 JSDoc 解析 JavaScript 文件的方式 |
| tags | <code>object</code> |  | 配置标签和标签字典,tags控制允许使用哪些 JSDoc 标签以及如何解释每个标签的选项。 |
| [tags.allowUnknownTags] | <code>boolean</code> \| <code>Array.&lt;string&gt;</code> | <code>true</code> | 影响 JSDoc 处理无法识别的标签的方式 |
| [tags.dictionaries] | <code>Array.&lt;string&gt;</code> | <code>[&quot;jsdoc&quot;,&quot;closure&quot;]</code> | 控制 JSDoc 识别哪些标签，以及 JSDoc 如何解释它识别的标签。 |


<a name="GenDoc.render_alias_GetFilesCodeOptions"></a>

##### GetFilesCodeOptions : <code>object</code>

获取源代码的文件路径配置参数

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

**属性**

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| dir | <code>string</code> | glob路径 |
| files | <code>Array.&lt;string&gt;</code> | glob文件名称数组 |


<a name="GenDoc.render_alias_Jsdoc2mdOptions"></a>

##### Jsdoc2mdOptions : <code>module:jsdoc-to-markdown~JsdocOptions</code> \| <code>module:jsdoc-to-markdown~RenderOptions</code> \| [<code>DataModifySupport</code>](#GenDoc.render_alias_DataModifySupport)

jsdocToMarkdown配置参数，具体可[👉参考文档](https://github.com/jsdoc2md/jsdoc-to-markdown/blob/master/docs/API.md)

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

<a name="GenDoc.render_alias_DataModifySupport"></a>

##### DataModifySupport : <code>object</code>

在原有Jsdoc2mdOptions基础上扩展dataModify属性，支持修改templateData

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

**属性**

| 属性 | 类型 |
| --- | --- |
| dataModify | [<code>DataModify</code>](#GenDoc.render_alias_DataModify) | 


<a name="GenDoc.render_alias_DataModify"></a>

##### DataModify ⇒ <code>Array.&lt;object&gt;</code>

**性质**: [<code>render</code>](#GenDoc.render)的类型声明

| 参数 | 类型 |
| --- | --- |
| doclets | <code>Array.&lt;object&gt;</code> | 


<a name="GenDoc.getRenderData"></a>

#### GenDoc.getRenderData(options, [needMergeConfig]) ⇒ [<code>Promise.&lt;GetRenderDataResult&gt;</code>](#GenDoc.getRenderData_alias_GetRenderDataResult)

获取用来渲染模板的数据（jsdoc生成的文档和示例代码的内容）

**性质**: [<code>GenDoc</code>](#GenDoc)的静态方法

| 参数 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| options | [<code>RenderOptions</code>](#GenDoc.render_alias_RenderOptions) |  | 配置参数 |
| [needMergeConfig] | <code>boolean</code> | <code>true</code> | 是否需要调用`_mergeToDefaultConfig`， options已经是merge处理过的就不需要调用,否则不推荐传入`false` 会导致别名不支持 |


<a name="GenDoc.getRenderData_alias_GetRenderDataResult"></a>

##### GetRenderDataResult : <code>object</code>

函数[GenDoc.getRenderData](#GenDoc.getRenderData)的返回值

**性质**: [<code>getRenderData</code>](#GenDoc.getRenderData)的类型声明

**属性**

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| docs | <code>string</code> | 源码使用jsdoc渲染后的markdown文本 |
| codes | [<code>Array.&lt;GetFilesCodeResult&gt;</code>](#GenDoc.getFilesCode_alias_GetFilesCodeResult) | 获取到的代码内容 |


<a name="GenDoc.getFilesCode"></a>

#### GenDoc.getFilesCode(options) ⇒ <code>Promise.&lt;Array.&lt;GetFilesCodeResult&gt;&gt;</code>

基于glob的文件遍历函数，返回文件对应内容的数组，
以文件夹为单位返回文件内容对象，key是文件的extname

**性质**: [<code>GenDoc</code>](#GenDoc)的静态方法

| 参数 | 类型 | 描述 |
| --- | --- | --- |
| options | [<code>GetFilesCodeOptions</code>](#GenDoc.render_alias_GetFilesCodeOptions) | 获取源代码的文件路径配置参数 |


<a name="GenDoc.getFilesCode_alias_GetFilesCodeResult"></a>

##### GetFilesCodeResult : <code>Object.&lt;string, string&gt;</code>

获取文件的内容的返回值类型，key是文件的extname

**性质**: [<code>getFilesCode</code>](#GenDoc.getFilesCode)的类型声明

<a name="GenDoc.getCliUsages"></a>

#### GenDoc.getCliUsages() ⇒ <code>Promise.&lt;Array.&lt;string&gt;&gt;</code>

获取命令行使用帮助文档
建议提前确保全局使用了最新的脚本
函数为异步函数，注意不能作为ejs帮助函数传入，可以获取返回值后，将返回值作为helpers的变量传入

**性质**: [<code>GenDoc</code>](#GenDoc)的静态方法

<a name="GenDoc.getFileContent"></a>

#### GenDoc.getFileContent(filename) ⇒ <code>string</code>

读取文件内容

**性质**: [<code>GenDoc</code>](#GenDoc)的静态方法

| 参数 | 类型 | 描述 |
| --- | --- | --- |
| filename | <code>string</code> | 相对于运行目录的文件路径 |


<a name="GenDoc.renderCode"></a>

#### GenDoc.renderCode(codes, [extSort], [extTrans]) ⇒ <code>string</code>

将codes渲染成md代码片段

**性质**: [<code>GenDoc</code>](#GenDoc)的静态方法

| 参数 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| codes | [<code>Array.&lt;GetFilesCodeResult&gt;</code>](#GenDoc.getFilesCode_alias_GetFilesCodeResult) |  | `GenDoc.getFilesCode`函数获取到的codes数组 |
| [extSort] | <code>Array.&lt;string&gt;</code> | <code>[&#x27;md&#x27;, &#x27;vue&#x27;, &#x27;jsx&#x27;, &#x27;js&#x27;]</code> | 优先并且按照`extSort`数组顺序获取遍历codes |
| [extTrans] | <code>Object.&lt;string, string&gt;</code> | <code>{vue:&#x27;html&#x27;}</code> | ext转换的映射map 简单示例`{vue:'html'}` |



<a name="defaultTemplate"></a>

## 默认文档渲染模板

```ejs
<%
const {docs, codes, helpers, pkg} = locals
%><% if(helpers.logo) { %><p align="center">
    <img src="<%- helpers.logo %>" alt="logo">
</p>

<% } if(pkg.name){ %># <%- pkg.name %><% } %><% if (helpers.badges && helpers.badges.length > 0) { %>

<%-
helpers.badges.map(item => {
    let badge = `![${item.name}](${item.url})`;
    if (item.link) {
        badge = `[${badge}](${item.link})`;
    }
    return badge;
}).join(' ');
%><% } %><% if(pkg.version){ %>

**版本** ：<%- pkg.version %><% } %><% if(pkg.description){ %>

<%- pkg.description %><% } %><% if(pkg.name||helpers.importCode||helpers.exportCode) { %>

## 快速开始
<% } %><% if(pkg.name){ %>
### 安装

<%  %>```bash
<%- helpers.installCode || 'npm i '+ (helpers.devInstall? '-D ' : '') + pkg.name %>
<%  %>```
<% } %><% if(helpers.importCode) { %>
### 引入

<%  %>```js
<%- helpers.importCode %>
<%  %>```
<% } %><% if(helpers.exportCode) { %>
### 导出

<%  %>```js
<%- helpers.exportCode %>
<%  %>```
<% } %><% if(helpers.cliUsages&&helpers.cliUsages.length) { %>
### 命令行使用文档
<% helpers.cliUsages.forEach(usage=>{ %>
<%  %>```
<%- usage %>
<%  %>```<% }) %><% } %><% if(helpers.remark) { %>

<%- helpers.remark %>
<% } %><% if(codes&&codes.length) { %>
## 代码演示

<%-
    helpers.renderCode
        &&helpers.renderCode(codes)
%><% } %><% if(docs) { %>
## API文档
<%- docs %><% } %><% if(helpers.postfixes&&helpers.postfixes.length) { // 后缀内容 %><% helpers.postfixes.forEach(postfix=>{ %>
<% if(postfix.id) { %>
<a name="<%- postfix.id %>"></a><% } %><% if(postfix.title) { %>

## <%- postfix.title %>
<% } %><% if(postfix.desc) { %>
> <%- postfix.desc %>
<% } %><% if(postfix.content) { %>
<%- postfix.content %><% }
})
} %>


```


<a name="defaultConfig"></a>

## 默认文档渲染配置

> 当前`__dirname`为`@agds/cli-plugin-doc/lib/utils`

```js
const path = require('path');
const defaultTemplate = path.resolve(__dirname, '../template/template.ejs');
const renderCode = require('./renderCode');
const defaultConfig = {
    template: defaultTemplate,
    jsdoc2mdOptions: {
        // 'no-cache': true,
        partial: [path.resolve(__dirname, '../dmdRewrite/partials/*.hbs')],
        helper: [path.resolve(__dirname, '../dmdRewrite/helpers/*.js')],
        'heading-depth': 3,
        // 使用`@alias`标签时优化id的显示，以确保id的唯一性
        dataModify: (doclets) => doclets.map((doclet) => {
            if (doclet.alias) {
                doclet.id = doclet.memberof + '_alias_' + doclet.alias;
            }
            return doclet;
        }),
    },
    // 默认`jsdocEngineOptions`配置一般只能增加无法删除，
    // 但是可以在配置noDefault来去除默认配置
    jsdocEngineOptions: {
        plugins: [
            require.resolve('jsdoc-tsimport-plugin'),
            require.resolve('./jsdoc-plugin-arrowfn'),
            require.resolve('jsdoc-typeof-plugin'),
        ],
    },
    helpers: {
        renderCode,
    },
};

module.exports = defaultConfig;
```


<a name="license"></a>

## 许可证

[MIT License](https://gitee.com/agile-development-system/cli-plugin-doc/blob/master/LICENSE)
Copyright (c) 2021 锦阳

<a name="donate"></a>

## 请维护者喝杯咖啡

<img src="https://gitee.com/agile-development-system/agds-doc-preset/raw/master/lib/docs/qrcode/alipay.jpeg" width="209px" >
<img src="https://gitee.com/agile-development-system/agds-doc-preset/raw/master/lib/docs/qrcode/wechatpay.jpeg" width="237px" >


<a name="dingtalk"></a>

## 加入钉钉群讨论或加入开发

<img src="https://gitee.com/agile-development-system/agds-doc-preset/raw/master/lib/docs/qrcode/dingtalk.jpeg" width="188px" >

