Skip to content

Commit 42a5033

Browse files
committed
chore(docs): improve docs parser type
previously we barfed on function type definition with optional arguments like {function(number=)} this fixes it I also added a bunch of code that helps to debug incorrectly parsed docs.
1 parent 6b19e7d commit 42a5033

File tree

2 files changed

+79
-46
lines changed

2 files changed

+79
-46
lines changed

docs/spec/ngdocSpec.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,15 @@ describe('ngdoc', function() {
5555
'@name a\n' +
5656
'@param {*} a short\n' +
5757
'@param {Type} b med\n' +
58-
'@param {Class=} [c=2] long\nline');
58+
'@param {Class=} [c=2] long\nline\n' +
59+
'@param {function(number, string=)} d fn with optional arguments');
5960
doc.parse();
6061
expect(doc.param).toEqual([
6162
{name:'a', description:'<p>short</p>', type:'*', optional:false, 'default':undefined},
6263
{name:'b', description:'<p>med</p>', type:'Type', optional:false, 'default':undefined},
63-
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'}
64+
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'},
65+
{name:'d', description:'<p>fn with optional arguments</p>',
66+
type: 'function(number, string=)', optional: false, 'default':undefined}
6467
]);
6568
});
6669

@@ -318,9 +321,9 @@ describe('ngdoc', function() {
318321
});
319322

320323
it('should not parse @property without a type', function() {
321-
var doc = new Doc("@property fake");
324+
var doc = new Doc("@property fake", 'test.js', '44');
322325
expect(function() { doc.parse(); }).
323-
toThrow(new Error("Not a valid 'property' format: fake"));
326+
toThrow(new Error("Not a valid 'property' format: fake (found in: test.js:44)"));
324327
});
325328

326329
it('should parse @property with type', function() {
@@ -350,15 +353,30 @@ describe('ngdoc', function() {
350353
describe('@returns', function() {
351354
it('should not parse @returns without type', function() {
352355
var doc = new Doc("@returns lala");
353-
expect(doc.parse).toThrow();
356+
expect(function() { doc.parse(); }).
357+
toThrow();
358+
});
359+
360+
361+
it('should not parse @returns with invalid type', function() {
362+
var doc = new Doc("@returns {xx}x} lala", 'test.js', 34);
363+
expect(function() { doc.parse(); }).
364+
toThrow(new Error("Not a valid 'returns' format: {xx}x} lala (found in: test.js:34)"));
354365
});
355366

367+
356368
it('should parse @returns with type and description', function() {
357369
var doc = new Doc("@name a\n@returns {string} descrip tion");
358370
doc.parse();
359371
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip tion</p>'});
360372
});
361373

374+
it('should parse @returns with complex type and description', function() {
375+
var doc = new Doc("@name a\n@returns {function(string, number=)} description");
376+
doc.parse();
377+
expect(doc.returns).toEqual({type: 'function(string, number=)', description: '<p>description</p>'});
378+
});
379+
362380
it('should transform description of @returns with markdown', function() {
363381
var doc = new Doc("@name a\n@returns {string} descrip *tion*");
364382
doc.parse();

docs/src/ngdoc.js

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -214,23 +214,25 @@ Doc.prototype = {
214214
if (atName) {
215215
var text = trim(atText.join('\n')), match;
216216
if (atName == 'param') {
217-
match = text.match(/^\{([^}=]+)(=)?\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
218-
// 1 12 2 34 4 5 5 6 6 3 7 7
217+
match = text.match(/^\{([^}]+)\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
218+
// 1 1 23 3 4 4 5 5 2 6 6
219219
if (!match) {
220-
throw new Error("Not a valid 'param' format: " + text);
220+
throw new Error("Not a valid 'param' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
221221
}
222+
223+
var optional = (match[1].slice(-1) === '=');
222224
var param = {
223-
name: match[5] || match[4],
224-
description:self.markdown(text.replace(match[0], match[7])),
225-
type: match[1],
226-
optional: !!match[2],
227-
'default':match[6]
225+
name: match[4] || match[3],
226+
description:self.markdown(text.replace(match[0], match[6])),
227+
type: optional ? match[1].substring(0, match[1].length-1) : match[1],
228+
optional: optional,
229+
'default':match[5]
228230
};
229231
self.param.push(param);
230232
} else if (atName == 'returns' || atName == 'return') {
231-
match = text.match(/^\{([^}=]+)\}\s+(.*)/);
233+
match = text.match(/^\{([^}]+)\}\s+(.*)/);
232234
if (!match) {
233-
throw new Error("Not a valid 'returns' format: " + text + ' in ' + self.file + ':' + self.line);
235+
throw new Error("Not a valid 'returns' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
234236
}
235237
self.returns = {
236238
type: match[1],
@@ -245,7 +247,7 @@ Doc.prototype = {
245247
} else if(atName == 'property') {
246248
match = text.match(/^\{(\S+)\}\s+(\S+)(\s+(.*))?/);
247249
if (!match) {
248-
throw new Error("Not a valid 'property' format: " + text);
250+
throw new Error("Not a valid 'property' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
249251
}
250252
var property = new Doc({
251253
type: match[1],
@@ -383,40 +385,53 @@ Doc.prototype = {
383385
var self = this;
384386
dom.h('Usage', function() {
385387
var restrict = self.restrict || 'AC';
388+
386389
if (restrict.match(/E/)) {
387-
dom.text('as element (see ');
390+
dom.text('This directive can be used as custom element, but we aware of ');
388391
dom.tag('a', {href:'guide/ie'}, 'IE restrictions');
389-
dom.text(')');
390-
dom.code(function() {
391-
dom.text('<');
392-
dom.text(dashCase(self.shortName));
393-
renderParams('\n ', '="', '"');
394-
dom.text('>\n</');
395-
dom.text(dashCase(self.shortName));
396-
dom.text('>');
397-
});
392+
dom.text('.');
398393
}
399-
if (restrict.match(/A/)) {
400-
var element = self.element || 'ANY';
401-
dom.text('as attribute');
402-
dom.code(function() {
403-
dom.text('<' + element + ' ');
404-
dom.text(dashCase(self.shortName));
405-
renderParams('\n ', '="', '"', true);
406-
dom.text('>\n ...\n');
407-
dom.text('</' + element + '>');
408-
});
409-
}
410-
if (restrict.match(/C/)) {
411-
dom.text('as class');
412-
var element = self.element || 'ANY';
413-
dom.code(function() {
414-
dom.text('<' + element + ' class="');
415-
dom.text(dashCase(self.shortName));
416-
renderParams(' ', ': ', ';', true);
417-
dom.text('">\n ...\n');
418-
dom.text('</' + element + '>');
394+
395+
if (self.usage) {
396+
dom.tag('pre', function() {
397+
dom.tag('code', function() {
398+
dom.text(self.usage);
399+
});
419400
});
401+
} else {
402+
if (restrict.match(/E/)) {
403+
dom.text('as element:');
404+
dom.code(function() {
405+
dom.text('<');
406+
dom.text(dashCase(self.shortName));
407+
renderParams('\n ', '="', '"');
408+
dom.text('>\n</');
409+
dom.text(dashCase(self.shortName));
410+
dom.text('>');
411+
});
412+
}
413+
if (restrict.match(/A/)) {
414+
var element = self.element || 'ANY';
415+
dom.text('as attribute');
416+
dom.code(function() {
417+
dom.text('<' + element + ' ');
418+
dom.text(dashCase(self.shortName));
419+
renderParams('\n ', '="', '"', true);
420+
dom.text('>\n ...\n');
421+
dom.text('</' + element + '>');
422+
});
423+
}
424+
if (restrict.match(/C/)) {
425+
dom.text('as class');
426+
var element = self.element || 'ANY';
427+
dom.code(function() {
428+
dom.text('<' + element + ' class="');
429+
dom.text(dashCase(self.shortName));
430+
renderParams(' ', ': ', ';', true);
431+
dom.text('">\n ...\n');
432+
dom.text('</' + element + '>');
433+
});
434+
}
420435
}
421436
self.html_usage_directiveInfo(dom);
422437
self.html_usage_parameters(dom);

0 commit comments

Comments
 (0)