Skip to content

Commit c941951

Browse files
committed
feat(experimental): decorators
1 parent e694c2c commit c941951

File tree

12 files changed

+149
-5
lines changed

12 files changed

+149
-5
lines changed

src/Doc/AbstractDoc.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import ParamParser from '../Parser/ParamParser.js';
33
import ASTUtil from '../Util/ASTUtil.js';
44
import InvalidCodeLogger from '../Util/InvalidCodeLogger.js';
55
import ASTNodeContainer from '../Util/ASTNodeContainer.js';
6+
import babelGenerator from 'babel-generator';
67

78
/**
89
* Abstract Doc Class.
@@ -77,6 +78,7 @@ export default class AbstractDoc {
7778
this._$throws();
7879
this._$emits();
7980
this._$listens();
81+
this._$decorator();
8082
}
8183

8284
/**
@@ -525,6 +527,31 @@ export default class AbstractDoc {
525527
}
526528
}
527529

530+
/**
531+
* decide `decorator`.
532+
*/
533+
_$decorator() {
534+
if (!this._node.decorators) return;
535+
536+
this._value.decorators = [];
537+
for (const decorator of this._node.decorators) {
538+
const value = {};
539+
switch (decorator.expression.type) {
540+
case 'Identifier':
541+
value.name = decorator.expression.name;
542+
value.arguments = null;
543+
break;
544+
case 'CallExpression':
545+
value.name = decorator.expression.callee.name;
546+
value.arguments = babelGenerator(decorator.expression).code.replace(/^[^(]+/, '');
547+
break;
548+
default:
549+
throw new Error(`unknown decorator expression type: ${decorator.expression.type}`);
550+
}
551+
this._value.decorators.push(value);
552+
}
553+
}
554+
528555
/**
529556
* find all tags.
530557
* @param {string[]} names - tag names.

src/Factory/DocFactory.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,13 @@ export default class DocFactory {
291291
Reflect.defineProperty(node, 'parent', {value: parentNode});
292292
}
293293

294+
// if node has decorators, leading comments is attached to decorators.
295+
if (node.decorators && node.decorators[0].leadingComments) {
296+
if (!node.leadingComments || !node.leadingComments.length) {
297+
node.leadingComments = node.decorators[0].leadingComments;
298+
}
299+
}
300+
294301
let results;
295302
results = this._traverseComments(parentNode, node, node.leadingComments);
296303
this._results.push(...results);

src/Parser/ESParser.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export default class ESParser {
6666
if (experimental.functionBind) option.plugins.push('functionBind');
6767
if (experimental.functionSent) option.plugins.push('functionSent');
6868
if (experimental.asyncGenerators) option.plugins.push('asyncGenerators');
69+
if (experimental.asyncGenerators) option.plugins.push('asyncGenerators');
70+
if (experimental.decorators) option.plugins.push('decorators');
6971
}
7072

7173
return option;

src/Publisher/Builder/ClassDocBuilder.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export default class ClassDocBuilder extends DocBuilder {
7474
ice.load('experimental', this._buildExperimentalHTML(doc));
7575
ice.load('see', this._buildDocsLinkHTML(doc.see), 'append');
7676
ice.load('todo', this._buildDocsLinkHTML(doc.todo), 'append');
77+
ice.load('decorator', this._buildDecoratorHTML(doc), 'append');
7778

7879
ice.into('instanceDocs', instanceDocs, (instanceDocs, ice)=>{
7980
ice.loop('instanceDoc', instanceDocs, (i, instanceDoc, ice)=>{

src/Publisher/Builder/DocBuilder.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ export default class DocBuilder {
479479
ice.load('see', this._buildDocsLinkHTML(doc.see), 'append');
480480
ice.load('todo', this._buildDocsLinkHTML(doc.todo), 'append');
481481
ice.load('override', this._buildOverrideMethod(doc));
482+
ice.load('decorator', this._buildDecoratorHTML(doc), 'append');
482483

483484
let isFunction = false;
484485
if (['method', 'constructor', 'function'].indexOf(doc.kind) !== -1) isFunction = true;
@@ -1017,6 +1018,24 @@ export default class DocBuilder {
10171018
return '';
10181019
}
10191020

1021+
_buildDecoratorHTML(doc) {
1022+
if (!doc.decorators) return '';
1023+
1024+
const links = [];
1025+
for (const decorator of doc.decorators) {
1026+
const link = this._buildDocLinkHTML(decorator.name, decorator.name, false, 'function');
1027+
if (decorator.arguments) {
1028+
links.push(`<li>${link}${decorator.arguments}</li>`);
1029+
} else {
1030+
links.push(`<li>${link}</li>`);
1031+
}
1032+
}
1033+
1034+
if (!links.length) return '';
1035+
1036+
return `<ul>${links.join('\n')}</ul>`;
1037+
}
1038+
10201039
// _buildAuthorHTML(doc, separator = '\n') {
10211040
// if (!doc.author) return '';
10221041
//

src/Publisher/Builder/template/class.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ <h1 data-ice="name"></h1>
2929
<div class="deprecated" data-ice="deprecated"></div>
3030
<div class="experimental" data-ice="experimental"></div>
3131
<div class="description" data-ice="description"></div>
32+
<div class="decorator" data-ice="decorator"><h4>Decorators:</h4></div>
3233

3334
<div data-ice="see"><h4>See:</h4></div>
3435

src/Publisher/Builder/template/details.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ <h4>Throw:</h4>
7373
</table>
7474
</div>
7575

76+
<div data-ice="decorator"><h4>Decorators:</h4></div>
77+
7678
<div data-ice="example">
7779
<h4>Example:</h4>
7880
<div class="example-doc" data-ice="exampleDoc">

src/Typedef/typedef.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* @property {boolean} experimentalProposal.functionBind
3636
* @property {boolean} experimentalProposal.functionSent
3737
* @property {boolean} experimentalProposal.asyncGenerators
38+
* @property {boolean} experimentalProposal.decorators
3839
* @see https://esdoc.org/config.html
3940
*/
4041

test/fixture/package/esdoc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
},
2424
"experimentalProposal": {
2525
"classProperties": true,
26-
"objectRestSpread": true
26+
"objectRestSpread": true,
27+
"decorators": true
2728
}
2829
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* this is TestDecoratorDefinition.
3+
*/
4+
@testDecoratorAnnotation1
5+
export default class TestDecoratorDefinition {
6+
/**
7+
* this is static method1.
8+
*/
9+
@testDecoratorAnnotation1
10+
static method1(){}
11+
12+
/**
13+
* this is get value1.
14+
* @type {number}
15+
*/
16+
@testDecoratorAnnotation1
17+
get value1(){}
18+
19+
/**
20+
* this is set value2.
21+
* @type {number}
22+
*/
23+
@testDecoratorAnnotation1
24+
set value2(v){}
25+
26+
/**
27+
* this is method1.
28+
*/
29+
@testDecoratorAnnotation1
30+
@testDecoratorAnnotation2(true)
31+
method1(){}
32+
}
33+
34+
/**
35+
* this is testDecoratorAnnotation1.
36+
*/
37+
export function testDecoratorAnnotation1(){}
38+
39+
/**
40+
* this is testDecoratorAnnotation2.
41+
*/
42+
export function testDecoratorAnnotation2(){}

test/src/HTMLTest/CoverageTest/CoverageTest.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ describe('test coverage', ()=> {
77
const badge = fs.readFileSync('./test/fixture/dest/esdoc/badge.svg', {encoding: 'utf8'}).toString();
88

99
it('has coverage summary', ()=> {
10-
assert(badge.includes('79%'));
10+
assert(badge.includes('80%'));
1111
assert.includes(doc, '[data-ice="coverageBadge"]', './badge.svg', 'src');
12-
assert.includes(doc, '[data-ice="totalCoverageCount"]', '267/335');
13-
assert.equal(doc.find('[data-ice="file"] [data-ice="coverage"]').length, 117);
12+
assert.includes(doc, '[data-ice="totalCoverageCount"]', '274/342');
13+
assert.equal(doc.find('[data-ice="file"] [data-ice="coverage"]').length, 118);
1414
});
1515

1616
/* eslint-disable max-statements */
@@ -36,6 +36,7 @@ describe('test coverage', ()=> {
3636
test('file/src/ClassProperty/Definition.js.html', '100 %3/3');
3737
test('file/src/Computed/Method.js.html', '100 %11/11');
3838
test('file/src/Computed/Property.js.html', '100 %12/12');
39+
test('file/src/Decorator/Definition.js.html', '100 %7/7');
3940
test('file/src/Deprecated/Class.js.html#errorLines=6', '75 %3/4');
4041
test('file/src/Deprecated/Function.js.html', '100 %1/1');
4142
test('file/src/Deprecated/Variable.js.html', '100 %1/1');
@@ -141,6 +142,6 @@ describe('test coverage', ()=> {
141142
test('file/src/Version/Function.js.html', '100 %1/1');
142143
test('file/src/Version/Variable.js.html', '100 %1/1');
143144

144-
assert.equal(count, 117);
145+
assert.equal(count, 118);
145146
});
146147
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {readDoc, assert, find, findParent} from './../../../util.js';
2+
3+
/**
4+
* @test {ClassDocBuilder#_buildClassDoc}
5+
* @test {DocBuilder#_buildDetailDocs}
6+
*/
7+
describe('TestDecoratorDefinition:', ()=> {
8+
const doc = readDoc('class/src/Decorator/Definition.js~TestDecoratorDefinition.html');
9+
10+
it('has decorator at class.', ()=>{
11+
find(doc, '[data-ice="content"] .self-detail', (doc)=>{
12+
assert.includes(doc, '[data-ice="decorator"]', 'testDecoratorAnnotation1');
13+
});
14+
});
15+
16+
it('has decorator at static method.', ()=>{
17+
findParent(doc, '[id="static-method-method1"]', '[data-ice="detail"]', (doc)=>{
18+
assert.includes(doc, '[data-ice="decorator"]', 'testDecoratorAnnotation1');
19+
});
20+
});
21+
22+
it('has decorator at getter.', ()=>{
23+
findParent(doc, '[id="instance-get-value1"]', '[data-ice="detail"]', (doc)=>{
24+
assert.includes(doc, '[data-ice="decorator"]', 'testDecoratorAnnotation1');
25+
});
26+
});
27+
28+
it('has decorator at setter.', ()=>{
29+
findParent(doc, '[id="instance-set-value2"]', '[data-ice="detail"]', (doc)=>{
30+
assert.includes(doc, '[data-ice="decorator"]', 'testDecoratorAnnotation1');
31+
});
32+
});
33+
34+
it('has decorator at method.', ()=>{
35+
findParent(doc, '[id="instance-method-method1"]', '[data-ice="detail"]', (doc)=>{
36+
assert.includes(doc, '[data-ice="decorator"] li:nth-of-type(1)', 'testDecoratorAnnotation1');
37+
assert.includes(doc, '[data-ice="decorator"] li:nth-of-type(2)', 'testDecoratorAnnotation2(true)');
38+
});
39+
});
40+
});

0 commit comments

Comments
 (0)