Skip to content
Permalink
Browse files

feat(experimental): class properties

  • Loading branch information...
h13i32maru committed Sep 25, 2016
1 parent e2689dd commit c7b4d9b415812cf18c451b1a452c9725593d3891
@@ -0,0 +1,45 @@
import AbstractDoc from './AbstractDoc.js';
import MethodDoc from './MethodDoc.js';
import ParamParser from '../Parser/ParamParser.js';

/**
* Doc Class from ClassProperty AST node.
*/
export default class ClassPropertyDoc extends AbstractDoc {
/**
* apply own tag.
* @private
*/
_apply() {
super._apply();

Reflect.deleteProperty(this._value, 'export');
Reflect.deleteProperty(this._value, 'importPath');
Reflect.deleteProperty(this._value, 'importStyle');
}

/** specify ``member`` to kind. */
_$kind() {
super._$kind();
this._value.kind = 'member';
}

/** take out self name from self node */
_$name() {
super._$name();
this._value.name = this._node.key.name;
}

/** borrow {@link MethodDoc#@_memberof} */
_$memberof() {
Reflect.apply(MethodDoc.prototype._$memberof, this, []);
}

/** if @type is not exists, guess type by using self node */
_$type() {
super._$type();
if (this._value.type) return;

this._value.type = ParamParser.guessType(this._node.value);
}
}
@@ -3,6 +3,7 @@ import CommentParser from '../Parser/CommentParser.js';
import FileDoc from '../Doc/FileDoc.js';
import ClassDoc from '../Doc/ClassDoc.js';
import MethodDoc from '../Doc/MethodDoc.js';
import ClassProperty from '../Doc/ClassPropertyDoc';
import MemberDoc from '../Doc/MemberDoc.js';
import FunctionDoc from '../Doc/FunctionDoc.js';
import VariableDoc from '../Doc/VariableDoc.js';
@@ -374,6 +375,7 @@ export default class DocFactory {
switch (type) {
case 'Class': Clazz = ClassDoc; break;
case 'Method': Clazz = MethodDoc; break;
case 'ClassProperty': Clazz = ClassProperty; break;
case 'Member': Clazz = MemberDoc; break;
case 'Function': Clazz = FunctionDoc; break;
case 'Variable': Clazz = VariableDoc; break;
@@ -416,8 +418,10 @@ export default class DocFactory {
switch (node.type) {
case 'ClassDeclaration':
return this._decideClassDeclarationType(node);
case 'ClassMethod': // for babylon
case 'ClassMethod':
return this._decideMethodDefinitionType(node);
case 'ClassProperty':
return this._decideClassPropertyType(node);
case 'ExpressionStatement':
return this._decideExpressionStatementType(node);
case 'FunctionDeclaration':
@@ -461,6 +465,22 @@ export default class DocFactory {
}
}

/**
* decide Doc type from class property node.
* @param {ASTNode} node - target node that is classs property node.
* @returns {{type: ?string, node: ?ASTNode}} decided type.
* @private
*/
_decideClassPropertyType(node) {
const classNode = this._findUp(node, ['ClassDeclaration', 'ClassExpression']);
if (this._processedClassNodes.includes(classNode)) {
return {type: 'ClassProperty', node: node};
} else {
logger.w('this class property is not in class', node);
return {type: null, node: null};
}
}

/**
* decide Doc type from function declaration node.
* @param {ASTNode} node - target node that is function declaration node.
@@ -16,18 +16,21 @@ export default class ESParser {
* @returns {AST} AST of source code.
*/
static parse(config, filePath) {
return this.parseWithBabylon(config, filePath);
return this._parseWithBabylon(config, filePath);
}

static parseWithBabylon(config, filePath) {
/**
* parse ECMAScript source code with babylon.
* @param {ESDocConfig} config - config of esdoc.
* @param {string} filePath - source code file path.
* @returns {AST} AST of source code.
*/
static _parseWithBabylon(config, filePath) {
let code = fs.readFileSync(filePath, {encode: 'utf8'}).toString();
code = Plugin.onHandleCode(code, filePath);
if (code.charAt(0) === '#') code = code.replace(/^#!/, '//');

const option = {
sourceType: 'module',
plugins: ['jsx']
};
const option = this._buildParserOptionForBabylon(config);

let parser = (code) => {
return babylon.parse(code, option);
@@ -41,4 +44,25 @@ export default class ESParser {

return ast;
}

/**
* build babylon option.
* @param {ESDocConfig} config - config of esdoc
* @returns {{sourceType: string, plugins: string[]}} option of babylon.
* @private
*/
static _buildParserOptionForBabylon(config) {
const option = {
sourceType: 'module',
plugins: ['jsx']
};

const experimental = config.experimentalProposal;

if (experimental) {
if (experimental.classProperties) option.plugins.push('classProperties');
}

return option;
}
}
@@ -28,6 +28,8 @@
* @property {string[]} manual.example
* @property {string[]} manual.faq
* @property {string[]} manual.changelog
* @property {Object} [experimentalProposal]
* @property {boolean} experimentalProposal.classProperties
* @see https://esdoc.org/config.html
*/

@@ -20,5 +20,8 @@
"example": ["./test/fixture/package/manual/example.md"],
"faq": ["./test/fixture/package/manual/faq.md"],
"changelog": ["./test/fixture/package/CHANGELOG.md"]
},
"experimentalProposal": {
"classProperties": true
}
}
@@ -0,0 +1,17 @@
/**
* this is TestClassPropertyDefinition.
* @todo test `Access`, `Deprecated`, `Desc`, `Duplication`, `Example`, `Experimental`, `Guess`, `Ignore`, `Link`, `See`, `Since`, `Todo` and `Version`.
*/
export default class TestClassPropertyDefinition {
/**
* this is static p1.
* @type {number}
*/
static p1 = 123;

/**
* this is p1.
* @type {number}
*/
p1 = 123;
}
@@ -9,8 +9,8 @@ describe('test coverage', ()=> {
it('has coverage summary', ()=> {
assert(badge.includes('79%'));
assert.includes(doc, '[data-ice="coverageBadge"]', './badge.svg', 'src');
assert.includes(doc, '[data-ice="totalCoverageCount"]', '263/330');
assert.equal(doc.find('[data-ice="file"] [data-ice="coverage"]').length, 116);
assert.includes(doc, '[data-ice="totalCoverageCount"]', '266/333');
assert.equal(doc.find('[data-ice="file"] [data-ice="coverage"]').length, 117);
});

/* eslint-disable max-statements */
@@ -33,6 +33,7 @@ describe('test coverage', ()=> {
test('file/src/Async/Function.js.html', '100 %1/1');
test('file/src/Async/Method.js.html', '100 %2/2');
test('file/src/Class/Definition.js.html', '100 %8/8');
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/Deprecated/Class.js.html#errorLines=6', '75 %3/4');
@@ -140,6 +141,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, 116);
assert.equal(count, 117);
});
});
@@ -0,0 +1,48 @@
import {readDoc, assert, find} from './../../../util.js';

/** @test {ClassDocBuilder} */
describe('TestClassPropertyDefinition:', ()=> {
const doc = readDoc('class/src/ClassProperty/Definition.js~TestClassPropertyDefinition.html');

/** @test {ClassDocBuilder#_buildClassDoc} */
describe('in summary', ()=>{
it('has static member', ()=>{
find(doc, '[data-ice="staticMemberSummary"]', (doc)=>{
find(doc, 'table[data-ice="summary"]:nth-of-type(1)', (doc)=>{
assert.includes(doc, '[data-ice="target"]:nth-of-type(1)', 'public static p1: number this is static p1.');
assert.includes(doc, '[data-ice="target"]:nth-of-type(1) [data-ice="name"] a', 'class/src/ClassProperty/Definition.js~TestClassPropertyDefinition.html#static-member-p1', 'href');
});
});
});

it('has member.', ()=>{
find(doc, '[data-ice="memberSummary"]', (doc)=>{
find(doc, 'table[data-ice="summary"]:nth-of-type(1)', (doc)=> {
assert.includes(doc, '[data-ice="target"]:nth-of-type(1)', 'public p1: number this is p1.');
assert.includes(doc, '[data-ice="target"]:nth-of-type(1) [data-ice="name"] a', 'class/src/ClassProperty/Definition.js~TestClassPropertyDefinition.html#instance-member-p1', 'href');
});
});
});
});

/** @test {ClassDocBuilder#_buildClassDoc} */
describe('in detail', ()=>{
it('has static member.', ()=>{
find(doc, '[data-ice="staticMemberDetails"]', (doc)=>{
find(doc, '[data-ice="detail"]:nth-of-type(1)', (doc)=>{
assert.includes(doc, '#static-member-p1', 'public static p1: number');
assert.includes(doc, '[data-ice="description"]', 'this is static p1.');
});
});
});

it('has member.', ()=>{
find(doc, '[data-ice="memberDetails"]', (doc)=>{
find(doc, '[data-ice="detail"]:nth-of-type(1)', (doc)=>{
assert.includes(doc, '#instance-member-p1', 'public p1: number');
assert.includes(doc, '#instance-member-p1 + [data-ice="description"]', 'this is p1.');
});
});
});
});
});

0 comments on commit c7b4d9b

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