新增调试信息

This commit is contained in:
2025-10-27 22:15:25 +08:00
parent ae62457d8c
commit 04642cb2f0
5479 changed files with 683397 additions and 3450 deletions
+21
View File
@@ -0,0 +1,21 @@
/* eslint-disable spaced-comment */
/**
* Demonstrate how to modify the source code before the parser sees it.
*
* @module plugins/commentConvert
*/
exports.handlers = {
///
/// Convert ///-style comments into jsdoc comments.
/// @param e
/// @param e.filename
/// @param e.source
///
beforeParse(e) {
e.source = e.source.replace(/(\n[ \t]*\/\/\/[^\n]*)+/g, $ => {
const replacement = `\n/**${$.replace(/^[ \t]*\/\/\//mg, '').replace(/(\n$|$)/, '*/$1')}`;
return replacement;
});
}
};
+17
View File
@@ -0,0 +1,17 @@
/**
* Remove everything in a file except JSDoc-style comments. By enabling this plugin, you can
* document source files that are not valid JavaScript (including source files for other languages).
* @module plugins/commentsOnly
*/
exports.handlers = {
beforeParse(e) {
// a JSDoc comment looks like: /**[one or more chars]*/
const comments = e.source.match(/\/\*\*[\s\S]+?\*\//g);
if (comments) {
e.source = comments.join('\n\n');
} else {
e.source = ''; // If file has no comments, parser should still receive no code
}
}
};
+18
View File
@@ -0,0 +1,18 @@
/**
* Escape HTML tags in descriptions.
*
* @module plugins/escapeHtml
*/
exports.handlers = {
/**
* Translate HTML tags in descriptions into safe entities. Replaces <, & and newlines
*/
newDoclet({doclet}) {
if (doclet.description) {
doclet.description = doclet.description
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/\r\n|\n|\r/g, '<br>');
}
}
};
+98
View File
@@ -0,0 +1,98 @@
/**
* Dump information about parser events to the console.
*
* @module plugins/eventDumper
*/
const _ = require('underscore');
const doop = require('jsdoc/util/doop');
const dump = require('jsdoc/util/dumper').dump;
const env = require('jsdoc/env');
const util = require('util');
const conf = env.conf.eventDumper || {};
// Dump the included parser events (defaults to all events)
let events = conf.include || [
'parseBegin',
'fileBegin',
'beforeParse',
'jsdocCommentFound',
'symbolFound',
'newDoclet',
'fileComplete',
'parseComplete',
'processingComplete'
];
// Don't dump the excluded parser events
if (conf.exclude) {
events = _.difference(events, conf.exclude);
}
/**
* Replace AST node objects in events with a placeholder.
*
* @param {Object} o - An object whose properties may contain AST node objects.
* @return {Object} The modified object.
*/
function replaceNodeObjects(o) {
const OBJECT_PLACEHOLDER = '<Object>';
if (o.code && o.code.node) {
// don't break the original object!
o.code = doop(o.code);
o.code.node = OBJECT_PLACEHOLDER;
}
if (o.doclet && o.doclet.meta && o.doclet.meta.code && o.doclet.meta.code.node) {
// don't break the original object!
o.doclet.meta.code = doop(o.doclet.meta.code);
o.doclet.meta.code.node = OBJECT_PLACEHOLDER;
}
if (o.astnode) {
o.astnode = OBJECT_PLACEHOLDER;
}
return o;
}
/**
* Get rid of unwanted crud in an event object.
*
* @param {object} e The event object.
* @return {object} The fixed-up object.
*/
function cleanse(e) {
let result = {};
Object.keys(e).forEach(prop => {
// by default, don't stringify properties that contain an array of functions
if (!conf.includeFunctions && util.isArray(e[prop]) && e[prop][0] &&
String(typeof e[prop][0]) === 'function') {
result[prop] = `function[${e[prop].length}]`;
}
// never include functions that belong to the object
else if (typeof e[prop] !== 'function') {
result[prop] = e[prop];
}
});
// allow users to omit node objects, which can be enormous
if (conf.omitNodes) {
result = replaceNodeObjects(result);
}
return result;
}
exports.handlers = {};
events.forEach(eventType => {
exports.handlers[eventType] = e => {
console.log( dump({
type: eventType,
content: cleanse(e)
}) );
};
});
+88
View File
@@ -0,0 +1,88 @@
/**
* Translate doclet descriptions from Markdown into HTML.
*
* @module plugins/markdown
*/
const env = require('jsdoc/env');
const config = env.conf.markdown || {};
const defaultTags = [
'author',
'classdesc',
'description',
'exceptions',
'params',
'properties',
'returns',
'see',
'summary'
];
const hasOwnProp = Object.prototype.hasOwnProperty;
const parse = require('jsdoc/util/markdown').getParser();
let tags = [];
let excludeTags = [];
function shouldProcessString(tagName, text) {
let shouldProcess = true;
// we only want to process `@author` and `@see` tags that contain Markdown links
if ( (tagName === 'author' || tagName === 'see') && !text.includes('[') ) {
shouldProcess = false;
}
return shouldProcess;
}
/**
* Process the markdown source in a doclet. The properties that should be processed are
* configurable, but always include "author", "classdesc", "description", "exceptions", "params",
* "properties", "returns", and "see". Handled properties can be bare strings, objects, or arrays
* of objects.
*/
function process(doclet) {
tags.forEach(tag => {
if ( !hasOwnProp.call(doclet, tag) ) {
return;
}
if (typeof doclet[tag] === 'string' && shouldProcessString(tag, doclet[tag]) ) {
doclet[tag] = parse(doclet[tag]);
}
else if ( Array.isArray(doclet[tag]) ) {
doclet[tag].forEach((value, index, original) => {
const inner = {};
inner[tag] = value;
process(inner);
original[index] = inner[tag];
});
}
else if (doclet[tag]) {
process(doclet[tag]);
}
});
}
// set up the list of "tags" (properties) to process
if (config.tags) {
tags = config.tags.slice();
}
// set up the list of default tags to exclude from processing
if (config.excludeTags) {
excludeTags = config.excludeTags.slice();
}
defaultTags.forEach(tag => {
if (!excludeTags.includes(tag) && !tags.includes(tag)) {
tags.push(tag);
}
});
exports.handlers = {
/**
* Translate Markdown syntax in a new doclet's description into HTML. Is run
* by JSDoc 3 whenever a "newDoclet" event fires.
*/
newDoclet({doclet}) {
process(doclet);
}
};
+181
View File
@@ -0,0 +1,181 @@
/**
* The Overload Helper plugin automatically adds a signature-like string to the longnames of
* overloaded functions and methods. In JSDoc, this string is known as a _variation_. (The longnames
* of overloaded constructor functions are _not_ updated, so that JSDoc can identify the class'
* members correctly.)
*
* Using this plugin allows you to link to overloaded functions without manually adding `@variation`
* tags to your documentation.
*
* For example, suppose your code includes a function named `foo` that you can call in the
* following ways:
*
* + `foo()`
* + `foo(bar)`
* + `foo(bar, baz)` (where `baz` is repeatable)
*
* This plugin assigns the following variations and longnames to each version of `foo`:
*
* + `foo()` gets the variation `()` and the longname `foo()`.
* + `foo(bar)` gets the variation `(bar)` and the longname `foo(bar)`.
* + `foo(bar, baz)` (where `baz` is repeatable) gets the variation `(bar, ...baz)` and the longname
* `foo(bar, ...baz)`.
*
* You can then link to these functions with `{@link foo()}`, `{@link foo(bar)}`, and
* `{@link foo(bar, ...baz)`. Note that the variation is based on the names of the function
* parameters, _not_ their types.
*
* If you prefer to manually assign variations to certain functions, you can still do so with the
* `@variation` tag. This plugin will not change these variations or add more variations for that
* function, as long as the variations you've defined result in unique longnames.
*
* If an overloaded function includes multiple signatures with the same parameter names, the plugin
* will assign numeric variations instead, starting at `(1)` and counting upwards.
*
* @module plugins/overloadHelper
*/
// lookup table of function doclets by longname
let functionDoclets;
function hasUniqueValues(obj) {
let isUnique = true;
const seen = [];
Object.keys(obj).forEach(key => {
if (seen.includes(obj[key])) {
isUnique = false;
}
seen.push(obj[key]);
});
return isUnique;
}
function getParamNames(params) {
const names = [];
params.forEach(param => {
let name = param.name || '';
if (param.variable) {
name = `...${name}`;
}
if (name !== '') {
names.push(name);
}
});
return names.length ? names.join(', ') : '';
}
function getParamVariation({params}) {
return getParamNames(params || []);
}
function getUniqueVariations(doclets) {
let counter = 0;
const variations = {};
const docletKeys = Object.keys(doclets);
function getUniqueNumbers() {
docletKeys.forEach(doclet => {
let newLongname;
while (true) {
counter++;
variations[doclet] = String(counter);
// is this longname + variation unique?
newLongname = `${doclets[doclet].longname}(${variations[doclet]})`;
if ( !functionDoclets[newLongname] ) {
break;
}
}
});
}
function getUniqueNames() {
// start by trying to preserve existing variations
docletKeys.forEach(doclet => {
variations[doclet] = doclets[doclet].variation || getParamVariation(doclets[doclet]);
});
// if they're identical, try again, without preserving existing variations
if ( !hasUniqueValues(variations) ) {
docletKeys.forEach(doclet => {
variations[doclet] = getParamVariation(doclets[doclet]);
});
// if they're STILL identical, switch to numeric variations
if ( !hasUniqueValues(variations) ) {
getUniqueNumbers();
}
}
}
// are we already using numeric variations? if so, keep doing that
if (functionDoclets[`${doclets.newDoclet.longname}(1)`]) {
getUniqueNumbers();
}
else {
getUniqueNames();
}
return variations;
}
function ensureUniqueLongname(newDoclet) {
const doclets = {
oldDoclet: functionDoclets[newDoclet.longname],
newDoclet: newDoclet
};
const docletKeys = Object.keys(doclets);
let oldDocletLongname;
let variations = {};
if (doclets.oldDoclet) {
oldDocletLongname = doclets.oldDoclet.longname;
// if the shared longname has a variation, like MyClass#myLongname(variation),
// remove the variation
if (doclets.oldDoclet.variation || doclets.oldDoclet.variation === '') {
docletKeys.forEach(doclet => {
doclets[doclet].longname = doclets[doclet].longname.replace(/\([\s\S]*\)$/, '');
doclets[doclet].variation = null;
});
}
variations = getUniqueVariations(doclets);
// update the longnames/variations
docletKeys.forEach(doclet => {
doclets[doclet].longname += `(${variations[doclet]})`;
doclets[doclet].variation = variations[doclet];
});
// update the old doclet in the lookup table
functionDoclets[oldDocletLongname] = null;
functionDoclets[doclets.oldDoclet.longname] = doclets.oldDoclet;
}
// always store the new doclet in the lookup table
functionDoclets[doclets.newDoclet.longname] = doclets.newDoclet;
return doclets.newDoclet;
}
exports.handlers = {
parseBegin() {
functionDoclets = {};
},
newDoclet(e) {
if (e.doclet.kind === 'function') {
e.doclet = ensureUniqueLongname(e.doclet);
}
},
parseComplete() {
functionDoclets = null;
}
};
+30
View File
@@ -0,0 +1,30 @@
/**
* Adds support for reusable partial jsdoc files.
*
* @module plugins/partial
*/
const env = require('jsdoc/env');
const fs = require('jsdoc/fs');
const path = require('path');
exports.handlers = {
/**
* Include a partial jsdoc
*
* @param e
* @param e.filename
* @param e.source
* @example
* @partial "partial_doc.jsdoc"
*/
beforeParse(e) {
e.source = e.source.replace(/(@partial ".*")+/g, $ => {
const pathArg = $.match(/".*"/)[0].replace(/"/g, '');
const fullPath = path.join(e.filename, '..', pathArg);
const partialData = fs.readFileSync(fullPath, env.opts.encoding);
return partialData;
});
}
};
+19
View File
@@ -0,0 +1,19 @@
/**
* Strips the rails template tags from a js.erb file
*
* @module plugins/railsTemplate
*/
exports.handlers = {
/**
* Remove rails tags from the source input (e.g. <% foo bar %>)
*
* @param e
* @param e.filename
* @param e.source
*/
beforeParse(e) {
if (e.filename.match(/\.erb$/)) {
e.source = e.source.replace(/<%.*%>/g, '');
}
}
};
+15
View File
@@ -0,0 +1,15 @@
/**
* This is just an example.
*
* @module plugins/shout
*/
exports.handlers = {
/**
* Make your descriptions more shoutier.
*/
newDoclet({doclet}) {
if (typeof doclet.description === 'string') {
doclet.description = doclet.description.toUpperCase();
}
}
};
+49
View File
@@ -0,0 +1,49 @@
/**
* @module plugins/sourcetag
*/
const logger = require('jsdoc/util/logger');
exports.handlers = {
/**
* Support @source tag. Expected value like:
*
* { "filename": "myfile.js", "lineno": 123 }
*
* Modifies the corresponding meta values on the given doclet.
*
* WARNING: If you are using a JSDoc template that generates pretty-printed source files,
* such as JSDoc's default template, this plugin can cause JSDoc to crash. To fix this issue,
* update your template settings to disable pretty-printed source files.
*
* @source { "filename": "sourcetag.js", "lineno": 9 }
*/
newDoclet({doclet}) {
let tags = doclet.tags;
let tag;
let value;
// any user-defined tags in this doclet?
if (typeof tags !== 'undefined') {
// only interested in the @source tags
tags = tags.filter(({title}) => title === 'source');
if (tags.length) {
// take the first one
tag = tags[0];
try {
value = JSON.parse(tag.value);
}
catch (ex) {
logger.error('@source tag expects a valid JSON value, like { "filename": "myfile.js", "lineno": 123 }.');
return;
}
doclet.meta = doclet.meta || {};
doclet.meta.filename = value.filename || '';
doclet.meta.lineno = value.lineno || '';
}
}
}
};
+58
View File
@@ -0,0 +1,58 @@
/**
* This plugin creates a summary tag, if missing, from the first sentence in the description.
*
* @module plugins/summarize
*/
exports.handlers = {
/**
* Autogenerate summaries, if missing, from the description, if present.
*/
newDoclet({doclet}) {
let endTag;
let tags;
let stack;
// If the summary is missing, grab the first sentence from the description
// and use that.
if (doclet && !doclet.summary && doclet.description) {
// The summary may end with `.$`, `. `, or `.<` (a period followed by an HTML tag).
doclet.summary = doclet.description.split(/\.$|\.\s|\.</)[0];
// Append `.` as it was removed in both cases, or is possibly missing.
doclet.summary += '.';
// This is an excerpt of something that is possibly HTML.
// Balance it using a stack. Assume it was initially balanced.
tags = doclet.summary.match(/<[^>]+>/g) || [];
stack = [];
tags.forEach(tag => {
const idx = tag.indexOf('/');
if (idx === -1) {
// start tag -- push onto the stack
stack.push(tag);
} else if (idx === 1) {
// end tag -- pop off of the stack
stack.pop();
}
// otherwise, it's a self-closing tag; don't modify the stack
});
// stack should now contain only the start tags that lack end tags,
// with the most deeply nested start tag at the top
while (stack.length > 0) {
// pop the unmatched tag off the stack
endTag = stack.pop();
// get just the tag name
endTag = endTag.substring(1, endTag.search(/[ >]/));
// append the end tag
doclet.summary += `</${endTag}>`;
}
// and, finally, if the summary starts and ends with a <p> tag, remove it; let the
// template decide whether to wrap the summary in a <p> tag
doclet.summary = doclet.summary.replace(/^<p>(.*)<\/p>$/i, '$1');
}
}
};
+31
View File
@@ -0,0 +1,31 @@
'use strict';
/**
* @see [Nowhere](http://nowhere.com)
*/
function foo() {}
/**
* @see AnObject#myProperty
*/
function bar() {}
/**
* @author [Mr. Macintosh](http://www.folklore.org/StoryView.py?story=Mister_Macintosh.txt)
* @classdesc My class.
* @description My class.
* @exception {Error} Some error.
* @param {string} myParam - My parameter.
* @property {string} value - Value of myParam.
* @return {MyClass} Class instance.
* @see [Example Inc.](http://example.com)
* @summary My class.
*/
function MyClass(myParam) {
this.value = myParam;
}
/**
* "See" {@link chat."#channel"."say-\"hello\""}.
*/
function MyOtherClass() {}
+50
View File
@@ -0,0 +1,50 @@
/**
* A bowl of non-spicy soup.
* @class
*//**
* A bowl of spicy soup.
* @class
* @param {number} spiciness - The spiciness of the soup, in Scoville heat units (SHU).
*/
function Soup(spiciness) {}
/**
* Slurp the soup.
*//**
* Slurp the soup loudly.
* @param {number} dBA - The slurping volume, in A-weighted decibels.
*/
Soup.prototype.slurp = function(dBA) {};
/**
* Salt the soup as needed, using a highly optimized soup-salting heuristic.
*//**
* Salt the soup, specifying the amount of salt to add.
* @variation mg
* @param {number} amount - The amount of salt to add, in milligrams.
*/
Soup.prototype.salt = function(amount) {};
/**
* Heat the soup by the specified number of degrees.
* @param {number} degrees - The number of degrees, in Fahrenheit, by which to heat the soup.
*//**
* Heat the soup by the specified number of degrees.
* @variation 1
* @param {string} degrees - The number of degrees, in Fahrenheit, by which to heat the soup, but
* as a string for some reason.
*//**
* Heat the soup by the specified number of degrees.
* @param {boolean} degrees - The number of degrees, as a boolean. Wait, what?
*/
Soup.prototype.heat = function(degrees) {};
/**
* Discard the soup.
* @variation discardSoup
*//**
* Discard the soup by pouring it into the specified container.
* @variation discardSoup
* @param {Object} container - The container in which to discard the soup.
*/
Soup.prototype.discard = function(container) {};
+19
View File
@@ -0,0 +1,19 @@
/**
* Strips the rails template tags from a js.erb file
*
* @module plugins/railsTemplate
*/
exports.handlers = {
/**
* Remove rails tags from the source input (e.g. <% foo bar %>)
* @param e
* @param e.filename
* @param e.source
*/
beforeParse: function(e) {
if (e.filename.match(/\.erb$/)) {
e.source = e.source.replace(/<%.*%> /g, "");
}
}
};
+22
View File
@@ -0,0 +1,22 @@
'use strict';
/** This doclet will be shown by default, just like normal. */
function normal() {}
/** This doclet will be hidden by default because it begins with an underscore. */
function _hidden() {}
/**
* Klass class
* @class
*/
function Klass() {
/** This is a private property of the class, and should not. */
this._privateProp = null;
/**
* This is a property explicitly marked as private.
* @private
*/
this.privateProp = null;
}
+20
View File
@@ -0,0 +1,20 @@
'use strict';
describe('commentConvert plugin', function() {
var env = require('jsdoc/env');
var path = require('jsdoc/path');
var docSet;
var parser = jasmine.createParser();
var pluginPath = 'plugins/commentConvert';
var pluginPathResolved = path.join(env.dirname, pluginPath);
var plugin = require(pluginPathResolved);
require('jsdoc/plugins').installPlugins([pluginPathResolved], parser);
docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser);
it('should convert ///-style comments into jsdoc comments', function() {
var doclet = docSet.getByLongname('module:plugins/commentConvert.handlers.beforeParse');
expect(doclet.length).toEqual(1);
});
});
+20
View File
@@ -0,0 +1,20 @@
'use strict';
describe('escapeHtml plugin', function() {
var env = require('jsdoc/env');
var path = require('jsdoc/path');
var docSet;
var parser = jasmine.createParser();
var pluginPath = 'plugins/escapeHtml';
var pluginPathResolved = path.join(env.dirname, pluginPath);
require('jsdoc/plugins').installPlugins([pluginPathResolved], parser);
docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser);
it("should escape '&', '<' and newlines in doclet descriptions", function() {
var doclet = docSet.getByLongname('module:plugins/escapeHtml.handlers.newDoclet');
expect(doclet[0].description).toEqual('Translate HTML tags in descriptions into safe entities. Replaces &lt;, &amp; and newlines');
});
});
+60
View File
@@ -0,0 +1,60 @@
'use strict';
var env = require('jsdoc/env');
var path = require('jsdoc/path');
describe('markdown plugin', function() {
var pluginPath = 'plugins/markdown';
var pluginPathResolved = path.join(env.dirname, pluginPath);
var plugin = require(pluginPathResolved);
var docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/markdown.js');
// TODO: more tests; refactor the plugin so multiple settings can be tested
it('should process the correct tags by default', function() {
var myClass = docSet.getByLongname('MyClass')[0];
plugin.handlers.newDoclet({ doclet: myClass });
[
myClass.author[0],
myClass.classdesc,
myClass.description,
myClass.exceptions[0].description,
myClass.params[0].description,
myClass.properties[0].description,
myClass.returns[0].description,
myClass.see,
myClass.summary
].forEach(function(value) {
// if we processed the value, it should be wrapped in a <p> tag
expect( /^<p>(?:.+)<\/p>$/.test(value) ).toBe(true);
});
});
it('should unescape &quot; entities in inline tags, but not elsewhere', function() {
var myOtherClass = docSet.getByLongname('MyOtherClass')[0];
plugin.handlers.newDoclet({ doclet: myOtherClass });
expect(myOtherClass.description).toContain('chat."#channel"."say-\\"hello\\""');
expect(myOtherClass.description).toContain('&quot;See&quot;');
});
describe('@see tag support', function() {
var foo = docSet.getByLongname('foo')[0];
var bar = docSet.getByLongname('bar')[0];
it('should parse @see tags containing links', function() {
plugin.handlers.newDoclet({ doclet: foo });
expect(typeof foo).toEqual('object');
expect(foo.see[0]).toEqual('<p><a href="http://nowhere.com">Nowhere</a></p>');
});
it('should not parse @see tags that do not contain links', function() {
plugin.handlers.newDoclet({ doclet: bar });
expect(typeof bar).toEqual('object');
expect(bar.see[0]).toEqual('AnObject#myProperty');
});
});
});
+102
View File
@@ -0,0 +1,102 @@
'use strict';
describe('plugins/overloadHelper', function() {
var env = require('jsdoc/env');
var path = require('jsdoc/path');
var docSet;
var parser = jasmine.createParser();
var pluginPath = 'plugins/overloadHelper';
var pluginPathResolved = path.resolve(env.dirname, pluginPath);
var plugin = require(pluginPathResolved);
require('jsdoc/plugins').installPlugins([pluginPathResolved], parser);
docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/overloadHelper.js', parser);
it('should exist', function() {
expect(plugin).toBeDefined();
expect(typeof plugin).toBe('object');
});
it('should export handlers', function() {
expect(plugin.handlers).toBeDefined();
expect(typeof plugin.handlers).toBe('object');
});
it('should export a "newDoclet" handler', function() {
expect(plugin.handlers.newDoclet).toBeDefined();
expect(typeof plugin.handlers.newDoclet).toBe('function');
});
it('should export a "parseComplete" handler', function() {
expect(plugin.handlers.parseComplete).toBeDefined();
expect(typeof plugin.handlers.parseComplete).toBe('function');
});
describe('newDoclet handler', function() {
it('should not add unique longnames to constructors', function() {
var soup = docSet.getByLongname('Soup');
var soup1 = docSet.getByLongname('Soup()');
var soup2 = docSet.getByLongname('Soup(spiciness)');
expect(soup.length).toBe(2);
expect(soup1.length).toBe(0);
expect(soup2.length).toBe(0);
});
it('should add unique longnames to methods', function() {
var slurp = docSet.getByLongname('Soup#slurp');
var slurp1 = docSet.getByLongname('Soup#slurp()');
var slurp2 = docSet.getByLongname('Soup#slurp(dBA)');
expect(slurp.length).toBe(0);
expect(slurp1.length).toBe(1);
expect(slurp2.length).toBe(1);
});
it('should update the "variation" property of the method', function() {
var slurp1 = docSet.getByLongname('Soup#slurp()')[0];
var slurp2 = docSet.getByLongname('Soup#slurp(dBA)')[0];
expect(slurp1.variation).toBe('');
expect(slurp2.variation).toBe('dBA');
});
it('should not add to or change existing variations that are unique', function() {
var salt1 = docSet.getByLongname('Soup#salt');
var salt2 = docSet.getByLongname('Soup#salt(mg)');
expect(salt1.length).toBe(1);
expect(salt2.length).toBe(1);
});
it('should not duplicate the names of existing numeric variations', function() {
var heat1 = docSet.getByLongname('Soup#heat(1)');
var heat2 = docSet.getByLongname('Soup#heat(2)');
var heat3 = docSet.getByLongname('Soup#heat(3)');
expect(heat1.length).toBe(1);
expect(heat2.length).toBe(1);
expect(heat3.length).toBe(1);
});
it('should replace identical variations with new, unique variations', function() {
var discard1 = docSet.getByLongname('Soup#discard()');
var discard2 = docSet.getByLongname('Soup#discard(container)');
expect(discard1.length).toBe(1);
expect(discard2.length).toBe(1);
});
});
describe('parseComplete handler', function() {
// disabled because on the second run, each comment is being parsed twice; who knows why...
xit('should not retain parse results between parser runs', function() {
parser.clear();
docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/overloadHelper.js', parser);
var heat = docSet.getByLongname('Soup#heat(4)');
expect(heat.length).toBe(0);
});
});
});
+19
View File
@@ -0,0 +1,19 @@
'use strict';
describe('railsTemplate plugin', function() {
var env = require('jsdoc/env');
var path = require('jsdoc/path');
var parser = jasmine.createParser();
var pluginPath = path.join(env.dirname, 'plugins/railsTemplate');
var plugin = require(pluginPath);
require('jsdoc/plugins').installPlugins([pluginPath], parser);
require('jsdoc/src/handlers').attachTo(parser);
it('should remove <% %> rails template tags from the source of *.erb files', function() {
var docSet = parser.parse([path.join(env.dirname, 'plugins/test/fixtures/railsTemplate.js.erb')]);
expect(docSet[2].description).toEqual('Remove rails tags from the source input (e.g. )');
});
});
+20
View File
@@ -0,0 +1,20 @@
'use strict';
describe('shout plugin', function() {
var env = require('jsdoc/env');
var path = require('jsdoc/path');
var docSet;
var parser = jasmine.createParser();
var pluginPath = 'plugins/shout';
var pluginPathResolved = path.join(env.dirname, pluginPath);
var plugin = require(pluginPathResolved);
require('jsdoc/plugins').installPlugins([pluginPathResolved], parser);
docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser);
it('should make the description uppercase', function() {
var doclet = docSet.getByLongname('module:plugins/shout.handlers.newDoclet');
expect(doclet[0].description).toEqual('MAKE YOUR DESCRIPTIONS MORE SHOUTIER.');
});
});
+22
View File
@@ -0,0 +1,22 @@
'use strict';
describe('sourcetag plugin', function() {
var env = require('jsdoc/env');
var path = require('jsdoc/path');
var docSet;
var parser = jasmine.createParser();
var pluginPath = 'plugins/sourcetag';
var pluginPathResolved = path.join(env.dirname, pluginPath);
require('jsdoc/plugins').installPlugins([pluginPathResolved], parser);
docSet = jasmine.getDocSetFromFile(pluginPath + '.js', parser);
it("should set the lineno and filename of the doclet's meta property", function() {
var doclet = docSet.getByLongname('module:plugins/sourcetag.handlers.newDoclet');
expect(doclet[0].meta).toBeDefined();
expect(doclet[0].meta.filename).toEqual('sourcetag.js');
expect(doclet[0].meta.lineno).toEqual(9);
});
});
+112
View File
@@ -0,0 +1,112 @@
/*global describe, expect, it */
'use strict';
var summarize = require('../../summarize');
describe('summarize', function() {
it('should export handlers', function() {
expect(summarize.handlers).toBeDefined();
expect(typeof summarize.handlers).toBe('object');
});
it('should export a newDoclet handler', function() {
expect(summarize.handlers.newDoclet).toBeDefined();
expect(typeof summarize.handlers.newDoclet).toBe('function');
});
describe('newDoclet handler', function() {
var handler = summarize.handlers.newDoclet;
it('should not blow up if the doclet is missing', function() {
function noDoclet() {
return handler({});
}
expect(noDoclet).not.toThrow();
});
it('should not change the summary if it is already defined', function() {
var doclet = {
summary: 'This is a summary.',
description: 'Descriptions are good.'
};
handler({ doclet: doclet });
expect(doclet.summary).not.toBe(doclet.description);
});
it('should not do anything if the description is missing', function() {
var doclet = {};
handler({ doclet: doclet });
expect(doclet.summary).not.toBeDefined();
});
it('should use the first sentence as the summary', function() {
var doclet = {
description: 'This sentence is the summary. This sentence is not.'
};
handler({ doclet: doclet });
expect(doclet.summary).toBe('This sentence is the summary.');
});
it('should not add an extra period if there is only one sentence in the description',
function() {
var doclet = {
description: 'This description has only one sentence.'
};
handler({ doclet: doclet });
expect(doclet.summary).toBe('This description has only one sentence.');
});
it('should use the entire description, plus a period, as the summary if the description ' +
'does not contain a period', function() {
var doclet = {
description: 'This is a description'
};
handler({ doclet: doclet });
expect(doclet.summary).toBe('This is a description.');
});
it('should use the entire description as the summary if the description contains only ' +
'one sentence', function() {
var doclet = {
description: 'This is a description.'
};
handler({ doclet: doclet });
expect(doclet.description).toBe('This is a description.');
});
it('should work when an HTML tag immediately follows the first sentence', function() {
var doclet = {
description: 'This sentence is the summary.<small>This sentence is small.</small>'
};
handler({ doclet: doclet });
expect(doclet.summary).toBe('This sentence is the summary.');
});
it('should generate valid HTML if a tag is opened, but not closed, in the summary',
function() {
var doclet = {
description: 'This description has <em>a tag. The tag straddles</em> sentences.'
};
handler({ doclet: doclet });
expect(doclet.summary).toBe('This description has <em>a tag.</em>');
});
it('should not include a <p> tag in the summary', function() {
var doclet = {
description: '<p>This description contains HTML.</p><p>And plenty of it!</p>'
};
handler({ doclet: doclet });
expect(doclet.summary).toBe('This description contains HTML.');
});
});
});
+35
View File
@@ -0,0 +1,35 @@
'use strict';
describe('underscore plugin', function () {
var env = require('jsdoc/env');
var path = require('jsdoc/path');
var docSet;
var parser = jasmine.createParser();
var pluginPath = 'plugins/underscore';
var fixturePath = 'plugins/test/fixtures/underscore';
var pluginPathResolved = path.join(env.dirname, pluginPath);
var plugin = require(pluginPathResolved);
require('jsdoc/plugins').installPlugins([pluginPathResolved], parser);
docSet = jasmine.getDocSetFromFile(fixturePath + '.js', parser);
it('should not mark normal, public properties as private', function() {
// Base line tests
var normal = docSet.getByLongname('normal');
expect(normal[0].access).toBeUndefined();
var realPrivate = docSet.getByLongname('Klass#privateProp');
expect(realPrivate[0].access).toEqual('private');
});
it('should hide doclet for symbols beginning with an underscore under normal circumstances', function () {
var hidden = docSet.getByLongname('_hidden');
expect(hidden[0].access).toEqual('private');
});
it('picks up "this"', function() {
var privateUnderscore = docSet.getByLongname('Klass#_privateProp');
expect(privateUnderscore[0].access).toEqual('private');
});
});
+16
View File
@@ -0,0 +1,16 @@
/**
* Removes all symbols that begin with an underscore from the doc output. If
* you're using underscores to denote private variables in modules, this
* automatically hides them.
*
* @module plugins/underscore
*/
exports.handlers = {
newDoclet({doclet}) {
// Ignore comment blocks for all symbols that begin with underscore
if (doclet.name.charAt(0) === '_' || doclet.name.substr(0, 6) === 'this._') {
doclet.access = 'private';
}
}
};