diff --git a/Readme.md b/Readme.md index 9dfedb2c..6ed2bfb1 100644 --- a/Readme.md +++ b/Readme.md @@ -89,6 +89,7 @@ output: "type": "method", "receiver": "exports", "name": "escape", + "params": [ "html" ], "string": "exports.escape()" } } diff --git a/lib/dox.js b/lib/dox.js index 53ed7763..b4ebf2a6 100644 --- a/lib/dox.js +++ b/lib/dox.js @@ -508,12 +508,13 @@ exports.contextPatternMatchers = [ function (str, parentContext) { // class constructor - if (/^\s*constructor\s*\(/.exec(str)) { + if (/^\s*constructor\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'constructor' , constructor: parentContext.name , cons: parentContext.name , name: 'constructor' + , params: splitParams(RegExp.$1) , string: (parentContext && parentContext.name && parentContext.name + '.prototype.' || '') + 'constructor()' }; // class method @@ -521,12 +522,13 @@ exports.contextPatternMatchers = [ }, function (str, parentContext) { - if (/^\s*(static)?\s*(\*)?\s*([\w$]+|\[.*\])\s*\(/.exec(str)) { + if (/^\s*(static)?\s*(\*)?\s*([\w$]+|\[.*\])\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'method' , constructor: parentContext.name , cons: parentContext.name , name: RegExp.$2 + RegExp.$3 + , params: splitParams(RegExp.$4) , string: (parentContext && parentContext.name && parentContext.name + (RegExp.$1 ? '.' : '.prototype.') || '') + RegExp.$2 + RegExp.$3 + '()' }; // named function statement, possibly exported by name or as a default @@ -534,10 +536,11 @@ exports.contextPatternMatchers = [ }, function (str) { - if (/^\s*(export(\s+default)?\s+)?function\s+([\w$]+)\s*\(/.exec(str)) { + if (/^\s*(export(\s+default)?\s+)?function\s+([\w$]+)\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'function' , name: RegExp.$3 + , params: splitParams(RegExp.$4) , string: RegExp.$3 + '()' }; } @@ -545,10 +548,11 @@ exports.contextPatternMatchers = [ function (str) { // anonymous function expression exported as a default - if (/^\s*export\s+default\s+function\s*\(/.exec(str)) { + if (/^\s*export\s+default\s+function\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'function' , name: RegExp.$1 // undefined + , params: splitParams(RegExp.$2) , string: RegExp.$1 + '()' }; } @@ -556,10 +560,11 @@ exports.contextPatternMatchers = [ function (str) { // function expression - if (/^return\s+function(?:\s+([\w$]+))?\s*\(/.exec(str)) { + if (/^return\s+function(?:\s+([\w$]+))?\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'function' , name: RegExp.$1 + , params: splitParams(RegExp.$2) , string: RegExp.$1 + '()' }; } @@ -567,10 +572,11 @@ exports.contextPatternMatchers = [ function (str) { // function expression - if (/^\s*(?:const|let|var)\s+([\w$]+)\s*=\s*function/.exec(str)) { + if (/^\s*(?:const|let|var)\s+([\w$]+)\s*=\s*function(?:\s+([\w$]+))?\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'function' , name: RegExp.$1 + , params: splitParams(RegExp.$3) , string: RegExp.$1 + '()' }; } @@ -578,12 +584,13 @@ exports.contextPatternMatchers = [ function (str, parentContext) { // prototype method - if (/^\s*([\w$.]+)\s*\.\s*prototype\s*\.\s*([\w$]+)\s*=\s*function/.exec(str)) { + if (/^\s*([\w$.]+)\s*\.\s*prototype\s*\.\s*([\w$]+)\s*=\s*function(?:\s+([\w$]+))?\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'method' , constructor: RegExp.$1 , cons: RegExp.$1 , name: RegExp.$2 + , params: splitParams(RegExp.$4) , string: RegExp.$1 + '.prototype.' + RegExp.$2 + '()' }; } @@ -631,12 +638,13 @@ exports.contextPatternMatchers = [ function (str, parentContext) { // inline method - if (/^\s*([\w$.]+)\s*:\s*function/.exec(str)) { + if (/^\s*([\w$.]+)\s*:\s*function(?:\s+([\w$]+))?\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'method' , constructor: parentContext.name , cons: parentContext.name , name: RegExp.$1 + , params: splitParams(RegExp.$3) , string: (parentContext && parentContext.name && parentContext.name + '.prototype.' || '') + RegExp.$1 + '()' }; } @@ -671,11 +679,12 @@ exports.contextPatternMatchers = [ function (str) { // method - if (/^\s*([\w$.]+)\s*\.\s*([\w$]+)\s*=\s*function/.exec(str)) { + if (/^\s*([\w$.]+)\s*\.\s*([\w$]+)\s*=\s*function(?:\s+([\w$]+))?\s*\(([\w,\s$_]*)\)/.exec(str)) { return { type: 'method' , receiver: RegExp.$1 , name: RegExp.$2 + , params: splitParams(RegExp.$4) , string: RegExp.$1 + '.' + RegExp.$2 + '()' }; } @@ -706,3 +715,17 @@ exports.contextPatternMatchers = [ } } ]; + +/** + * Split the signature-part of a function/method/constructor into a list of param names + * @param {string} signature + * @return {string[]} list of params + */ +function splitParams (signature) { + if (signature.length === 0) { + return []; + } + return signature.split(',').map(function(param) { + return param.trim() + }); +} \ No newline at end of file diff --git a/test.js b/test.js new file mode 100644 index 00000000..4440bfbe --- /dev/null +++ b/test.js @@ -0,0 +1,4 @@ +console.log(`getFirst: function getFirstBarItem(){ + return this.bar[0]; +} +`.match(/^\s*([\w$.]+)\s*:\s*function(.*)/)) \ No newline at end of file diff --git a/test/dox.test.js b/test/dox.test.js index 55357cd7..e92d0b20 100644 --- a/test/dox.test.js +++ b/test/dox.test.js @@ -105,6 +105,7 @@ module.exports = { var parseComments = comments.shift(); parseComments.tags.should.have.length(4); parseComments.ctx.type.should.equal('method'); + parseComments.ctx.params.should.eql(['js']) parseComments.ctx.receiver.should.equal('exports'); parseComments.ctx.name.should.equal('parseComments'); parseComments.description.full.should.equal('

Parse comments in the given string of js.

'); @@ -161,6 +162,7 @@ module.exports = { first.ctx.type.should.equal('method'); first.ctx.receiver.should.equal('exports'); first.ctx.name.should.equal('parseTagTypes'); + first.ctx.params.should.eql(['str']) first.code.should.equal('exports.parseTagTypes = function(str) {\n\treturn str\n\t\t.replace(/[{}]/g, \'\')\n\t\t.split(/ *[|,\\/] */);\n};'); first.line.should.equal(2); first.codeStart.should.equal(11); @@ -180,6 +182,7 @@ module.exports = { first.ctx.type.should.equal('method'); first.ctx.receiver.should.equal('exports'); first.ctx.name.should.equal('parseTagTypes'); + first.ctx.params.should.eql(['str']) first.code.should.equal('exports.parseTagTypes = function(str) {\n return str\n .replace(/[{}]/g, \'\')\n .split(/ *[|,\\/] */);\n};'); first.line.should.equal(2); first.codeStart.should.equal(11); @@ -199,6 +202,7 @@ module.exports = { first.ctx.type.should.equal('method'); first.ctx.receiver.should.equal('exports'); first.ctx.name.should.equal('parseTagTypes'); + first.ctx.params.should.eql(['str']); first.code.should.equal('exports.parseTagTypes = function(str) {\n\treturn str\n\t\t.replace(/[{}]/g, \'\')\n\t\t.split(/ *[|,\\/] */);\n};'); first.line.should.equal(2); first.codeStart.should.equal(11); @@ -231,6 +235,7 @@ module.exports = { comments[2].description.full.should.equal('

A method of an instance of Foo

'); comments[2].ctx.type.should.be.equal('method'); comments[2].ctx.name.should.be.equal('method'); + comments[2].ctx.params.should.eql([]); comments[2].ctx.string.should.be.equal('Foo.prototype.method()'); comments[2].line.should.equal(18); comments[2].codeStart.should.equal(22); @@ -260,6 +265,7 @@ module.exports = { comments[1].description.full.should.equal('

construct a Foo

'); comments[1].ctx.type.should.be.equal('constructor'); comments[1].ctx.name.should.be.equal('constructor'); + comments[1].ctx.params.should.eql(['options']); comments[1].ctx.constructor.should.be.equal('FooBar'); comments[1].ctx.string.should.be.equal('FooBar.prototype.constructor()'); comments[1].line.should.equal(9); @@ -268,6 +274,7 @@ module.exports = { // class method comments[2].description.full.should.equal('

Method of the Foo class.

'); comments[2].ctx.type.should.be.equal('method'); + comments[2].ctx.params.should.eql([]); comments[2].ctx.name.should.be.equal('bar'); comments[2].ctx.constructor.should.be.equal('FooBar'); comments[2].ctx.string.should.be.equal('FooBar.prototype.bar()'); @@ -277,6 +284,7 @@ module.exports = { // class static method comments[3].description.full.should.equal('

Static method of the Foo class.

'); comments[3].ctx.type.should.be.equal('method'); + comments[3].ctx.params.should.eql([]); comments[3].ctx.name.should.be.equal('staticMethod'); comments[3].ctx.constructor.should.be.equal('FooBar'); comments[3].ctx.string.should.be.equal('FooBar.staticMethod()'); @@ -287,6 +295,7 @@ module.exports = { comments[4].description.full.should.equal('

Static generator method of the Foo class.

'); comments[4].ctx.type.should.be.equal('method'); comments[4].ctx.name.should.be.equal('*staticGeneratorMethod'); + comments[4].ctx.params.should.eql([]); comments[4].ctx.constructor.should.be.equal('FooBar'); comments[4].ctx.string.should.be.equal('FooBar.*staticGeneratorMethod()'); comments[4].line.should.equal(34); @@ -295,6 +304,7 @@ module.exports = { // class generator method with computed name comments[5].description.full.should.equal('

Generator method with computed name.

'); comments[5].ctx.type.should.be.equal('method'); + comments[5].ctx.params.should.eql([]); comments[5].ctx.name.should.be.equal('*[Symbol.iterator]'); comments[5].ctx.constructor.should.be.equal('FooBar'); comments[5].ctx.string.should.be.equal('FooBar.prototype.*[Symbol.iterator]()'); @@ -333,6 +343,7 @@ module.exports = { comments[9].description.full.should.equal(''); comments[9].ctx.type.should.be.equal('constructor'); comments[9].ctx.name.should.be.equal('constructor'); + comments[9].ctx.params.should.eql(['options']); comments[9].ctx.constructor.should.be.equal('Baz'); comments[9].ctx.string.should.be.equal('Baz.prototype.constructor()'); comments[9].line.should.equal(72); @@ -373,6 +384,7 @@ module.exports = { comments[0].description.full.should.equal('

Luke, I am your constructor.

'); comments[0].ctx.type.should.be.equal('constructor'); comments[0].ctx.name.should.be.equal('Foo'); + comments[0].ctx.params.should.eql(['bar']); comments[0].ctx.string.should.be.equal('Foo()'); // prototoype object @@ -385,6 +397,7 @@ module.exports = { comments[2].description.full.should.equal('

Returns the first item.

'); comments[2].ctx.type.should.be.equal('method'); comments[2].ctx.name.should.be.equal('getFirst'); + comments[2].ctx.params.should.eql([]); comments[2].ctx.string.should.be.equal('Foo.prototype.getFirst()'); // getter function @@ -403,6 +416,7 @@ module.exports = { comments[5].description.full.should.equal('

Anonymous function on property.

'); comments[5].ctx.type.should.be.equal('method'); comments[5].ctx.name.should.be.equal('random'); + comments[5].ctx.params.should.eql([]); comments[5].ctx.string.should.be.equal('Foo.prototype.random()'); // this should be a separated function @@ -413,6 +427,7 @@ module.exports = { // classical prototype function property comments[7].description.full.should.equal('

Returns the last item.

\n
var f = new Foo([1, 5, 10]);\n\nf.getLast() === 10;\n
'); comments[7].ctx.type.should.be.equal('method'); + comments[7].ctx.params.should.eql([]); comments[7].ctx.name.should.be.equal('getLast'); comments[7].ctx.string.should.be.equal('Foo.prototype.getLast()'); @@ -443,12 +458,14 @@ module.exports = { comments[2].description.full.should.equal('

This function surely does something

'); comments[2].ctx.type.should.be.equal('method'); comments[2].ctx.name.should.be.equal('doSomething'); + comments[2].ctx.params.should.eql([]); comments[2].ctx.string.should.be.equal('doSomething()'); // property as a named method function comments[3].description.full.should.equal('

And them something else

'); comments[3].ctx.type.should.be.equal('method'); comments[3].ctx.name.should.be.equal('doSomethingElse'); + comments[3].ctx.params.should.eql([]); comments[3].ctx.string.should.be.equal('doSomethingElse()'); // getter function @@ -473,6 +490,7 @@ module.exports = { first.ctx.type.should.equal('method'); first.ctx.receiver.should.equal('exports'); first.ctx.name.should.equal('parseTagTypes'); + first.ctx.params.should.eql(['str']); first.code.should.equal('exports.parseTagTypes = function(str) {\n return str\n .replace(/[{}]/g, \'\')\n .split(/ *[|,\\/] */);\n};'); first.line.should.equal(2); first.codeStart.should.equal(11); @@ -528,33 +546,38 @@ module.exports = { }, 'test .parseCodeContext() function statement': function(){ - var ctx = dox.parseCodeContext('function $foo(){\n\n}'); + var ctx = dox.parseCodeContext('function $foo(a,b){\n\n}'); ctx.type.should.equal('function'); + ctx.params.should.eql(['a','b']); ctx.name.should.equal('$foo'); }, 'test .parseCodeContext() returned unnamed function statement': function(){ - var ctx = dox.parseCodeContext('return function (){\n\n}'); + var ctx = dox.parseCodeContext('return function ( a , b ){\n\n}'); ctx.type.should.equal('function'); + ctx.params.should.eql(['a','b']); ctx.name.should.equal(''); }, 'test .parseCodeContext() returned named function statement': function(){ - var ctx = dox.parseCodeContext('return function $foo (){\n\n}'); + var ctx = dox.parseCodeContext('return function $foo (a$,b$){\n\n}'); ctx.type.should.equal('function'); + ctx.params.should.eql(['a$','b$']); ctx.name.should.equal('$foo'); }, 'test .parseCodeContext() function expression': function(){ - var ctx = dox.parseCodeContext('var $foo = function(){\n\n}'); + var ctx = dox.parseCodeContext('var $foo = function(_a\t, \t_b){\n\n}'); ctx.type.should.equal('function'); + ctx.params.should.eql(['_a','_b']); ctx.name.should.equal('$foo'); }, 'test .parseCodeContext() prototype method': function(){ - var ctx = dox.parseCodeContext('$User.prototype.$save = function(){}'); + var ctx = dox.parseCodeContext('$User.prototype.$save = function(a,b){}'); ctx.type.should.equal('method'); ctx.constructor.should.equal('$User'); + ctx.params.should.eql(['a','b']); ctx.name.should.equal('$save'); }, @@ -585,10 +608,11 @@ module.exports = { }, 'test .parseCodeContext() method': function(){ - var ctx = dox.parseCodeContext('$user.$save = function(){}'); + var ctx = dox.parseCodeContext('$user.$save = function(a,b){}'); ctx.type.should.equal('method'); ctx.receiver.should.equal('$user'); ctx.name.should.equal('$save'); + ctx.params.should.eql(['a','b']); }, 'test .parseCodeContext() property': function(){