Skip to content
Permalink
Browse files

feat(experimental): decorators

  • Loading branch information...
h13i32maru committed Nov 23, 2016
1 parent e694c2c commit c9419512b7dda5e8f0b906945fb2a91f0b4621b3
@@ -3,6 +3,7 @@ import ParamParser from '../Parser/ParamParser.js';
import ASTUtil from '../Util/ASTUtil.js';
import InvalidCodeLogger from '../Util/InvalidCodeLogger.js';
import ASTNodeContainer from '../Util/ASTNodeContainer.js';
import babelGenerator from 'babel-generator';

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

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

/**
* decide `decorator`.
*/
_$decorator() {
if (!this._node.decorators) return;

this._value.decorators = [];
for (const decorator of this._node.decorators) {
const value = {};
switch (decorator.expression.type) {
case 'Identifier':
value.name = decorator.expression.name;
value.arguments = null;
break;
case 'CallExpression':
value.name = decorator.expression.callee.name;
value.arguments = babelGenerator(decorator.expression).code.replace(/^[^(]+/, '');
break;
default:
throw new Error(`unknown decorator expression type: ${decorator.expression.type}`);
}
this._value.decorators.push(value);
}
}

/**
* find all tags.
* @param {string[]} names - tag names.
@@ -291,6 +291,13 @@ export default class DocFactory {
Reflect.defineProperty(node, 'parent', {value: parentNode});
}

// if node has decorators, leading comments is attached to decorators.
if (node.decorators && node.decorators[0].leadingComments) {
if (!node.leadingComments || !node.leadingComments.length) {
node.leadingComments = node.decorators[0].leadingComments;
}
}

let results;
results = this._traverseComments(parentNode, node, node.leadingComments);
this._results.push(...results);
@@ -66,6 +66,8 @@ export default class ESParser {
if (experimental.functionBind) option.plugins.push('functionBind');
if (experimental.functionSent) option.plugins.push('functionSent');
if (experimental.asyncGenerators) option.plugins.push('asyncGenerators');
if (experimental.asyncGenerators) option.plugins.push('asyncGenerators');
if (experimental.decorators) option.plugins.push('decorators');
}

return option;
@@ -74,6 +74,7 @@ export default class ClassDocBuilder extends DocBuilder {
ice.load('experimental', this._buildExperimentalHTML(doc));
ice.load('see', this._buildDocsLinkHTML(doc.see), 'append');
ice.load('todo', this._buildDocsLinkHTML(doc.todo), 'append');
ice.load('decorator', this._buildDecoratorHTML(doc), 'append');

ice.into('instanceDocs', instanceDocs, (instanceDocs, ice)=>{
ice.loop('instanceDoc', instanceDocs, (i, instanceDoc, ice)=>{
@@ -479,6 +479,7 @@ export default class DocBuilder {
ice.load('see', this._buildDocsLinkHTML(doc.see), 'append');
ice.load('todo', this._buildDocsLinkHTML(doc.todo), 'append');
ice.load('override', this._buildOverrideMethod(doc));
ice.load('decorator', this._buildDecoratorHTML(doc), 'append');

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

_buildDecoratorHTML(doc) {
if (!doc.decorators) return '';

const links = [];
for (const decorator of doc.decorators) {
const link = this._buildDocLinkHTML(decorator.name, decorator.name, false, 'function');
if (decorator.arguments) {
links.push(`<li>${link}${decorator.arguments}</li>`);
} else {
links.push(`<li>${link}</li>`);
}
}

if (!links.length) return '';

return `<ul>${links.join('\n')}</ul>`;
}

// _buildAuthorHTML(doc, separator = '\n') {
// if (!doc.author) return '';
//
@@ -29,6 +29,7 @@ <h1 data-ice="name"></h1>
<div class="deprecated" data-ice="deprecated"></div>
<div class="experimental" data-ice="experimental"></div>
<div class="description" data-ice="description"></div>
<div class="decorator" data-ice="decorator"><h4>Decorators:</h4></div>

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

@@ -73,6 +73,8 @@ <h4>Throw:</h4>
</table>
</div>

<div data-ice="decorator"><h4>Decorators:</h4></div>

<div data-ice="example">
<h4>Example:</h4>
<div class="example-doc" data-ice="exampleDoc">
@@ -35,6 +35,7 @@
* @property {boolean} experimentalProposal.functionBind
* @property {boolean} experimentalProposal.functionSent
* @property {boolean} experimentalProposal.asyncGenerators
* @property {boolean} experimentalProposal.decorators
* @see https://esdoc.org/config.html
*/

@@ -23,6 +23,7 @@
},
"experimentalProposal": {
"classProperties": true,
"objectRestSpread": true
"objectRestSpread": true,
"decorators": true
}
}
@@ -0,0 +1,42 @@
/**
* this is TestDecoratorDefinition.
*/
@testDecoratorAnnotation1
export default class TestDecoratorDefinition {
/**
* this is static method1.
*/
@testDecoratorAnnotation1
static method1(){}

/**
* this is get value1.
* @type {number}
*/
@testDecoratorAnnotation1
get value1(){}

/**
* this is set value2.
* @type {number}
*/
@testDecoratorAnnotation1
set value2(v){}

/**
* this is method1.
*/
@testDecoratorAnnotation1
@testDecoratorAnnotation2(true)
method1(){}
}

/**
* this is testDecoratorAnnotation1.
*/
export function testDecoratorAnnotation1(){}

/**
* this is testDecoratorAnnotation2.
*/
export function testDecoratorAnnotation2(){}
@@ -7,10 +7,10 @@ describe('test coverage', ()=> {
const badge = fs.readFileSync('./test/fixture/dest/esdoc/badge.svg', {encoding: 'utf8'}).toString();

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

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

assert.equal(count, 117);
assert.equal(count, 118);
});
});
@@ -0,0 +1,40 @@
import {readDoc, assert, find, findParent} from './../../../util.js';

/**
* @test {ClassDocBuilder#_buildClassDoc}
* @test {DocBuilder#_buildDetailDocs}
*/
describe('TestDecoratorDefinition:', ()=> {
const doc = readDoc('class/src/Decorator/Definition.js~TestDecoratorDefinition.html');

it('has decorator at class.', ()=>{
find(doc, '[data-ice="content"] .self-detail', (doc)=>{
assert.includes(doc, '[data-ice="decorator"]', 'testDecoratorAnnotation1');
});
});

it('has decorator at static method.', ()=>{
findParent(doc, '[id="static-method-method1"]', '[data-ice="detail"]', (doc)=>{
assert.includes(doc, '[data-ice="decorator"]', 'testDecoratorAnnotation1');
});
});

it('has decorator at getter.', ()=>{
findParent(doc, '[id="instance-get-value1"]', '[data-ice="detail"]', (doc)=>{
assert.includes(doc, '[data-ice="decorator"]', 'testDecoratorAnnotation1');
});
});

it('has decorator at setter.', ()=>{
findParent(doc, '[id="instance-set-value2"]', '[data-ice="detail"]', (doc)=>{
assert.includes(doc, '[data-ice="decorator"]', 'testDecoratorAnnotation1');
});
});

it('has decorator at method.', ()=>{
findParent(doc, '[id="instance-method-method1"]', '[data-ice="detail"]', (doc)=>{
assert.includes(doc, '[data-ice="decorator"] li:nth-of-type(1)', 'testDecoratorAnnotation1');
assert.includes(doc, '[data-ice="decorator"] li:nth-of-type(2)', 'testDecoratorAnnotation2(true)');
});
});
});

0 comments on commit c941951

Please sign in to comment.
You can’t perform that action at this time.