turn Doclet#merge into doclet.combine

This commit is contained in:
Jeff Williams 2017-07-15 14:01:12 -07:00
parent 77bd47f58a
commit 4aa3ee8f96
2 changed files with 101 additions and 102 deletions

View File

@ -468,18 +468,7 @@ 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);
function dooper(source, target, properties) {
properties.forEach(function(property) {
switch (typeof source[property]) {
case 'function':
@ -487,57 +476,86 @@ function extend(source, destination, exclude) {
break;
case 'object':
destination[property] = jsdoc.util.doop(source[property]);
target[property] = jsdoc.util.doop(source[property]);
break;
default:
destination[property] = source[property];
target[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.
* Combine two doclets into a target doclet, using properties from the secondary doclet only when
* those properties do not exist on the primary doclet, and ignoring properties that should be
* excluded.
*
* @private
* @param {module:jsdoc/doclet.Doclet} source - The source doclet.
* @param {module:jsdoc/doclet.Doclet} destination - The destination doclet.
* @param {module:jsdoc/doclet.Doclet} primary - The primary doclet.
* @param {module:jsdoc/doclet.Doclet} secondary - The secondary doclet.
* @param {module:jsdoc/doclet.Doclet} target - The doclet to which properties will be copied.
* @param {Array.<string>} exclude - The names of properties to exclude from copying.
*/
function combine(primary, secondary, target, exclude) {
var primaryProperties = _.difference(Object.getOwnPropertyNames(primary), exclude);
var secondaryProperties = _.difference(Object.getOwnPropertyNames(secondary),
exclude.concat(primaryProperties));
dooper(primary, target, primaryProperties);
dooper(secondary, target, secondaryProperties);
}
/**
* Combine specified properties from two doclets into a target doclet, using the properties of the
* primary doclet unless the properties of the secondary doclet appear to be a better fit.
*
* @private
* @param {module:jsdoc/doclet.Doclet} primary - The primary doclet.
* @param {module:jsdoc/doclet.Doclet} secondary - The secondary doclet.
* @param {module:jsdoc/doclet.Doclet} target - The doclet to which properties will be copied.
* @param {Array.<string>} include - The names of properties to copy.
*/
function maybeExtend(source, destination, include) {
function combineWithLogic(primary, secondary, target, include) {
include.forEach(function(property) {
var shouldExtend = false;
var shouldUsePrimary = 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;
if ({}.hasOwnProperty.call(primary, property)) {
// use the primary property if the secondary property is missing or empty
if (!secondary[property] || !secondary[property].length) {
shouldUsePrimary = true;
}
// use the source property if it's not empty
else if (source[property].length) {
shouldExtend = true;
else if (primary[property].length) {
shouldUsePrimary = true;
}
}
if (shouldExtend) {
destination[property] = jsdoc.util.doop(source[property]);
if (shouldUsePrimary) {
target[property] = jsdoc.util.doop(primary[property]);
}
else {
target[property] = jsdoc.util.doop(secondary[property]);
}
});
}
/**
* Merge another doclet into this doclet.
* Combine two doclets into a new doclet.
*
* @param {module:jsdoc/doclet.Doclet} doclet - The doclet to merge into this one.
* @param {module:jsdoc/doclet.Doclet} primary - The doclet whose properties will be used.
* @param {module:jsdoc/doclet.Doclet} secondary - The doclet to use as a fallback for properties
* that the primary doclet does not have.
*/
Doclet.prototype.merge = function(doclet) {
exports.combine = function(primary, secondary) {
var specialCase = [
'params',
'properties'
];
var target = new Doclet('');
extend(doclet, this, specialCase);
maybeExtend(doclet, this, specialCase);
combine(primary, secondary, target, specialCase);
combineWithLogic(primary, secondary, target, specialCase);
return target;
};

View File

@ -3,7 +3,10 @@
describe('jsdoc/doclet', function() {
// TODO: more tests
var _ = require('underscore');
var Doclet = require('jsdoc/doclet').Doclet;
var jsdoc = {
doclet: require('jsdoc/doclet')
};
var Doclet = jsdoc.doclet.Doclet;
var docSet = jasmine.getDocSetFromFile('test/fixtures/doclet.js');
var test1 = docSet.getByLongname('test1')[0];
@ -47,25 +50,23 @@ describe('jsdoc/doclet', function() {
});
});
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 */');
describe('combine', function() {
it('should override most properties of the secondary doclet', function() {
var primaryDoclet = new Doclet('/** New and improved!\n@version 2.0.0 */');
var secondaryDoclet = new Doclet('/** Hello!\n@version 1.0.0 */');
var newDoclet = jsdoc.doclet.combine(primaryDoclet, secondaryDoclet);
originalDoclet.merge(newDoclet);
Object.getOwnPropertyNames(originalDoclet).forEach(function(property) {
expect(originalDoclet[property]).toEqual(newDoclet[property]);
Object.getOwnPropertyNames(newDoclet).forEach(function(property) {
expect(newDoclet[property]).toEqual(primaryDoclet[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 */');
it('should add properties that are missing from the secondary doclet', function() {
var primaryDoclet = new Doclet('/** Hello!\n@version 2.0.0 */');
var secondaryDoclet = new Doclet('/** Hello! */');
var newDoclet = jsdoc.doclet.combine(primaryDoclet, secondaryDoclet);
originalDoclet.merge(newDoclet);
expect(originalDoclet.version).toBe('2.0.0');
expect(newDoclet.version).toBe('2.0.0');
});
describe('params and properties', function() {
@ -74,65 +75,45 @@ describe('jsdoc/doclet', function() {
'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);
it('should use the secondary doclet\'s params and properties if the primary doclet ' +
'had none', function() {
var primaryDoclet = new Doclet('/** Hello! */');
var secondaryComment = [
'/**',
' * @param {string} foo - The foo.',
' * @property {number} bar - The bar.',
' */'
].join('\n');
var secondaryDoclet = new Doclet(secondaryComment);
var newDoclet = jsdoc.doclet.combine(primaryDoclet, secondaryDoclet);
originalDoclet.merge(newDoclet);
properties.forEach(function(property) {
expect(originalDoclet[property]).toEqual(newDoclet[property]);
});
properties.forEach(function(property) {
expect(newDoclet[property]).toEqual(secondaryDoclet[property]);
});
});
it('should use the new doclet\'s params and properties if the new doclet has some',
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);
it('should use the primary doclet\'s params and properties if the primary doclet has ' +
'some', function() {
var primaryComment = [
'/**',
' * @param {number} baz - The baz.',
' * @property {string} qux - The qux.',
' */'
].join('\n');
var primaryDoclet = new Doclet(primaryComment);
var secondaryComment = [
'/**',
' * @param {string} foo - The foo.',
' * @property {number} bar - The bar.',
' */'
].join('\n');
var secondaryDoclet = new Doclet(secondaryComment);
var newDoclet = jsdoc.doclet.combine(primaryDoclet, secondaryDoclet);
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 none',
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]);
});
properties.forEach(function(property) {
expect(newDoclet[property]).toEqual(primaryDoclet[property]);
});
});
});
});
});