add merge method to doclets (#1215)

This commit is contained in:
Jeff Williams 2017-07-14 21:39:21 -07:00
parent 976cc62dc8
commit 0551bd49ed
2 changed files with 170 additions and 2 deletions

View File

@ -14,6 +14,9 @@ var jsdoc = {
tag: {
Tag: require('jsdoc/tag').Tag,
dictionary: require('jsdoc/tag/dictionary')
},
util: {
doop: require('jsdoc/util/doop')
}
};
var path = require('jsdoc/path');
@ -178,6 +181,8 @@ exports._replaceDictionary = function _replaceDictionary(dict) {
var Doclet = exports.Doclet = function(docletSrc, meta) {
var newTags = [];
meta = meta || {};
/** The original text of the comment from the source code. */
this.comment = docletSrc;
this.setMeta(meta);
@ -462,3 +467,77 @@ Doclet.prototype.setMeta = function(meta) {
}
}
};
/**
* Extend a destination object with properties from the source object, ignoring properties that
* should be excluded.
*
* @private
* @param {Object} source - The source object.
* @param {Object} destination - The destination object.
* @param {Array.<string>} exclude - The names of properties to exclude from copying.
*/
function extend(source, destination, exclude) {
var properties = _.difference(Object.getOwnPropertyNames(source), exclude);
properties.forEach(function(property) {
switch (typeof source[property]) {
case 'function':
// do nothing
break;
case 'object':
destination[property] = jsdoc.util.doop(source[property]);
break;
default:
destination[property] = source[property];
}
});
}
/**
* Extend a destination doclet with the specified properties from the source doclet, provided that
* the properties from the source doclet appear to be a better fit.
*
* @private
* @param {module:jsdoc/doclet.Doclet} source - The source doclet.
* @param {module:jsdoc/doclet.Doclet} destination - The destination doclet.
* @param {Array.<string>} include - The names of properties to copy.
*/
function maybeExtend(source, destination, include) {
include.forEach(function(property) {
var shouldExtend = false;
if ({}.hasOwnProperty.call(source, property)) {
// use the source property if the destination property is missing or empty
if (!destination[property] || !destination[property].length) {
shouldExtend = true;
}
// use the source property if it's at least as long as the destination property
else if (source[property].length >= destination[property].length) {
shouldExtend = true;
}
}
if (shouldExtend) {
destination[property] = jsdoc.util.doop(source[property]);
}
});
}
/**
* Merge another doclet into this doclet.
*
* @param {module:jsdoc/doclet.Doclet} doclet - The doclet to merge into this one.
*/
Doclet.prototype.merge = function(doclet) {
var specialCase = [
'params',
'properties'
];
extend(doclet, this, specialCase);
maybeExtend(doclet, this, specialCase);
};

View File

@ -26,7 +26,7 @@ describe('jsdoc/doclet', function() {
describe('setScope', function() {
it('should accept the correct scope names', function() {
function setScope(scopeName) {
var doclet = new Doclet('/** Huzzah, a doclet! */', {});
var doclet = new Doclet('/** Huzzah, a doclet! */');
doclet.setScope(scopeName);
}
@ -38,7 +38,7 @@ describe('jsdoc/doclet', function() {
it('should throw an error for invalid scope names', function() {
function setScope() {
var doclet = new Doclet('/** Woe betide this doclet. */', {});
var doclet = new Doclet('/** Woe betide this doclet. */');
doclet.setScope('fiddlesticks');
}
@ -46,4 +46,93 @@ describe('jsdoc/doclet', function() {
expect(setScope).toThrow();
});
});
describe('merge', function() {
it('should override most properties of the original doclet', function() {
var originalDoclet = new Doclet('/** Hello!\n@version 1.0.0 */');
var newDoclet = new Doclet('/** New and improved!\n@version 2.0.0 */');
originalDoclet.merge(newDoclet);
Object.getOwnPropertyNames(originalDoclet).forEach(function(property) {
expect(originalDoclet[property]).toEqual(newDoclet[property]);
});
});
it('should add properties that are missing from the original doclet', function() {
var originalDoclet = new Doclet('/** Hello! */');
var newDoclet = new Doclet('/** Hello!\n@version 2.0.0 */');
originalDoclet.merge(newDoclet);
expect(originalDoclet.version).toBe('2.0.0');
});
describe('params and properties', function() {
var properties = [
'params',
'properties'
];
it('should use the new doclet\'s params and properties if the original doclet had none',
function() {
var originalDoclet = new Doclet('/** Hello! */');
var newComment = [
'/**',
' * @param {string} foo - The foo.',
' * @property {number} bar - The bar.',
' */'
].join('\n');
var newDoclet = new Doclet(newComment);
originalDoclet.merge(newDoclet);
properties.forEach(function(property) {
expect(originalDoclet[property]).toEqual(newDoclet[property]);
});
});
it('should use the new doclet\'s params and properties if the new doclet has at ' +
'least as many of them as the old doclet', function() {
var originalComment = [
'/**',
' * @param {string} foo - The foo.',
' * @property {number} bar - The bar.',
' */'
].join('\n');
var originalDoclet = new Doclet(originalComment);
var newComment = [
'/**',
' * @param {number} baz - The baz.',
' * @property {string} qux - The qux.',
' */'
].join('\n');
var newDoclet = new Doclet(newComment);
originalDoclet.merge(newDoclet);
properties.forEach(function(property) {
expect(originalDoclet[property]).toEqual(newDoclet[property]);
});
});
it('should use the old doclet\'s params and properties if the new doclet has fewer ' +
'of them', function() {
var originalComment = [
'/**',
' * @param {string} foo - The foo.',
' * @property {number} bar - The bar.',
' */'
].join('\n');
var originalDoclet = new Doclet(originalComment);
var newDoclet = new Doclet('/** Hello! */');
originalDoclet.merge(newDoclet);
properties.forEach(function(property) {
expect(originalDoclet[property]).not.toEqual(newDoclet[property]);
});
});
});
});
});