Skip to content

Commit

Permalink
feat: support generate api reference doc
Browse files Browse the repository at this point in the history
  • Loading branch information
PanPanZou authored and yndu13 committed Mar 27, 2024
1 parent 4411d58 commit 6932b03
Show file tree
Hide file tree
Showing 9 changed files with 391 additions and 33 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: CI

on:
push:
branches: [ master, 1.x ]
pull_request:
branches: [ master, 1.x ]

jobs:
build:

runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
node-version: [12.x, 14.x, 16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run ci
- uses: codecov/codecov-action@v3
163 changes: 158 additions & 5 deletions lib/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const {
REQUEST, RESPONSE, CORE
} = require('./helper');
const { Tag } = require('@darabonba/parser/lib/tag');

const Annotation = require('@darabonba/annotation-parser');
function avoidReserveName(name) {
const reserves = [
'function'
Expand All @@ -24,6 +24,14 @@ function avoidReserveName(name) {
return name;
}

function getAttr(node, attrName) {
for (let i = 0; i < node.attrs.length; i++) {
if (_name(node.attrs[i].attrName) === attrName) {
return node.attrs[i].attrValue.string || node.attrs[i].attrValue.lexeme;
}
}
}

class Visitor {

static get supportGenerateTest() {
Expand Down Expand Up @@ -231,10 +239,85 @@ class Visitor {
}
let comments = DSL.comment.getFrontComments(this.comments, annotation.index);
this.visitComments(comments, level);
annotation.value.split('\n').forEach((line) => {
this.emit(`${line}`, level);
this.emit(`\n`);
var ast = Annotation.parse(annotation.value);
var description = ast.items.find((item) => {
return item.type === 'description';
});
var summary = ast.items.find((item) => {
return item.type === 'summary';
});
var _return = ast.items.find((item) => {
return item.type === 'return';
});
var deprecated = ast.items.find((item) => {
return item.type === 'deprecated';
});
var params = ast.items.filter((item) => {
return item.type === 'param';
}).map((item) => {
return {
name: item.name.id,
text: item.text.text.trimEnd()
};
});
var throws = ast.items.filter((item) => {
return item.type === 'throws';
}).map((item) => {
return item.text.text.trimEnd();
});

var descriptionText = description ? description.text.text.trimEnd() : '';
var summaryText = summary ? summary.text.text.trimEnd() : '';
var returnText = _return ? _return.text.text.trimEnd() : '';
let hasNextSection = false;

this.emit(`/**\n`, level);
if (summaryText !== '') {
summaryText.split('\n').forEach((line) => {
this.emit(` * ${line}\n`, level);
});
hasNextSection = true;
}
if (descriptionText !== '') {
if (hasNextSection) {
this.emit(` * \n`, level);
}
this.emit(` * @remarks\n`, level);
descriptionText.split('\n').forEach((line) => {
this.emit(` * ${line}\n`, level);
});
hasNextSection = true;
}
if (deprecated) {
if (hasNextSection) {
this.emit(` * \n`, level);
}
if (deprecated.text.text.trimEnd() === '') {
this.emit(` * @deprecated\n`, level);
} else {
this.emit(` * @deprecated ${deprecated.text.text.trimEnd()}\n`, level);
}
hasNextSection = true;
}
if (params.length > 0) {
if (hasNextSection) {
this.emit(` * \n`, level);
}
params.forEach((item) => {
this.emit(` * @param ${item.name} - ${item.text}\n`, level);
});
}
if (returnText !== '') {
this.emit(` * @returns ${returnText}\n`, level);
}
if (throws.length > 0) {
this.emit(` * \n`, level);
throws.forEach((item) => {
this.emit(` * @throws ${item}\n`, level);
});
}
this.emit(` */`, level);
this.emit(`\n`);
}

visitInit(ast, types, level) {
Expand Down Expand Up @@ -521,6 +604,74 @@ class Visitor {
node = ast.nodes[i];
let comments = DSL.comment.getFrontComments(this.comments, node.tokenRange[0]);
this.visitComments(comments, level);
const description = getAttr(node, 'description');
const example = getAttr(node, 'example');
const checkBlank = getAttr(node, 'checkBlank');
const nullable = getAttr(node, 'nullable');
const sensitive = getAttr(node, 'sensitive');
const deprecated = getAttr(node, 'deprecated');
let hasNextSection = false;
if (deprecated === 'true' || description || example || typeof checkBlank !== 'undefined' || typeof nullable !== 'undefined' || typeof sensitive !== 'undefined') {
this.emit('/**\n', level);
if (description) {
this.emit(' * @remarks\n', level);
const descriptions = description.split('\n');
for (let j = 0; j < descriptions.length; j++) {
if (descriptions[j] === '') {
this.emit(` * \n`, level);
} else {
this.emit(` * ${descriptions[j]}\n`, level);
}
}
hasNextSection = true;
}
if (example) {
if (hasNextSection) {
this.emit(` * \n`, level);
}
const examples = example.split('\n');
this.emit(' * @example\n', level);
for (let j = 0; j < examples.length; j++) {
if (examples[j] === '') {
this.emit(` * \n`, level);
} else {
this.emit(` * ${examples[j]}\n`, level);
}
}
hasNextSection = true;
}
if (typeof checkBlank !== 'undefined') {
if (hasNextSection) {
this.emit(` * \n`, level);
}
this.emit(' * **check if is blank:**\n', level);
this.emit(` * ${checkBlank}\n`, level);
hasNextSection = true;
}
if (typeof nullable !== 'undefined') {
if (hasNextSection) {
this.emit(` * \n`, level);
}
this.emit(' * **if can be null:**\n', level);
this.emit(` * ${nullable}\n`, level);
hasNextSection = true;
}
if (typeof sensitive !== 'undefined') {
if (hasNextSection) {
this.emit(` * \n`, level);
}
this.emit(' * **if sensitive:**\n', level);
this.emit(` * ${sensitive}\n`, level);
hasNextSection = true;
}
if (deprecated === 'true') {
if (hasNextSection) {
this.emit(` * \n`, level);
}
this.emit(` * @deprecated\n`, level);
}
this.emit(' */\n', level);
}
this.emit(`${_name(node.fieldName)}${node.required ? '' : '?'}: `, level);
this.visitFieldType(node.fieldValue, level, modelName, _name(node.fieldName));
this.emit(';\n');
Expand Down Expand Up @@ -1177,7 +1328,9 @@ class Visitor {
}

importBefore(level) {
this.emit(`// This file is auto-generated, don't edit it\n`, level);
if (!this.config.editable) {
this.emit(`// This file is auto-generated, don't edit it\n`, level);
}
}

moduleBefore(ast) {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"url": "git@github.com:aliyun/darabonba-typescript-generator.git"
},
"dependencies": {
"@darabonba/annotation-parser": "^1.0.0",
"@darabonba/parser": "^1.2.7"
},
"devDependencies": {
Expand Down
68 changes: 57 additions & 11 deletions test/fixtures/annotation/client.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// This file is auto-generated, don't edit it
/**
top annotation
*/
* @remarks
* top annotation
*/
import * as $tea from '@alicloud/tea-typescript';

/**
TestModel
*/
* @remarks
* TestModel
*/
export class Test extends $tea.Model {
/**
* @remarks
* Alichange app id
*/
test: string;
static names(): { [key: string]: string } {
return {
Expand All @@ -31,14 +36,16 @@ export default class Client {
_a: string;

/**
Init Func
*/
* @remarks
* Init Func
*/
constructor() {
}

/**
testAPI
*/
* @remarks
* testAPI
*/
async testAPI(): Promise<void> {
let _runtime: { [key: string]: any } = { }

Expand Down Expand Up @@ -72,9 +79,48 @@ export default class Client {
}

/**
testFunc
*/
* @remarks
* testFunc
*/
static async testFunc(): Promise<void> {
}

/**
* annotation test summary
*
* @remarks
* annotation test description
* * description1 test for typescript
* * description2 test for typescript
* * test link: [Limits](https://help.aliyun.com/document_detail/25412.html#SecurityGroupQuota).
*
* @param test - string param1
* @param _test - string param2
* @returns void
*
* @throws InternalError Server error. 500 服务器端出现未知异常。
* @throws StackNotFound The Stack (%(stack_name)s) could not be found. 404 资源栈不存在。
*/
static async testFuncWithAnnotation(test: string, _test: string): Promise<void> {
// empty comment1
// empty comment2
}

/**
* annotation test summary
*
* @deprecated annotation test deprecation
*
* @param test - string param1
* @param _test - string param2
* @returns void
*
* @throws InternalError Server error. 500 服务器端出现未知异常。
* @throws StackNotFound The Stack (%(stack_name)s) could not be found. 404 资源栈不存在。
*/
static async testFuncWithAnnotation1(test: string, _test: string): Promise<void> {
// empty comment1
// empty comment2
}

}
34 changes: 34 additions & 0 deletions test/fixtures/annotation/main.dara
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,38 @@ api testAPI(): void {
*/
static async function testFunc(): void {

}

/**
* @description annotation test description
* * description1 test for typescript
* * description2 test for typescript
* * test link: [Limits](https://help.aliyun.com/document_detail/25412.html#SecurityGroupQuota).
*
* @summary annotation test summary
*
* @param test string param1
* @param _test string param2
* @return void
* @throws InternalError Server error. 500 服务器端出现未知异常。
* @throws StackNotFound The Stack (%(stack_name)s) could not be found. 404 资源栈不存在。
*/
static async function testFuncWithAnnotation(test: string, _test: string): void {
// empty comment1
// empty comment2
}

/**
* @summary annotation test summary
* @deprecated annotation test deprecation
*
* @param test string param1
* @param _test string param2
* @return void
* @throws InternalError Server error. 500 服务器端出现未知异常。
* @throws StackNotFound The Stack (%(stack_name)s) could not be found. 404 资源栈不存在。
*/
static async function testFuncWithAnnotation1(test: string, _test: string): void {
// empty comment1
// empty comment2
}
Loading

0 comments on commit 6932b03

Please sign in to comment.