refactor(jsdoc): plumb dependency container through parser, doclets, and tags

And remove `jsdoc/env` dependency from `tag`, `tag/dictionary/definitions`, and `tag/validator`.
This commit is contained in:
Jeff Williams 2021-10-03 09:10:14 -07:00
parent 6583e7680e
commit f7055161eb
19 changed files with 138 additions and 102 deletions

View File

@ -303,7 +303,7 @@ module.exports = (() => {
const conf = dependencies.get('config'); const conf = dependencies.get('config');
props.parser = parser.createParser(conf); props.parser = parser.createParser(dependencies);
if (conf.plugins) { if (conf.plugins) {
plugins.installPlugins(conf.plugins, props.parser); plugins.installPlugins(conf.plugins, props.parser);

View File

@ -511,12 +511,12 @@ function getImplementedAdditions(implDoclets, allDoclets, { documented, memberof
return additions; return additions;
} }
function augment(doclets, propertyName, docletFinder) { function augment(doclets, propertyName, docletFinder, jsdocDeps) {
const index = doclets.index.longname; const index = doclets.index.longname;
const dependencies = sort(mapDependencies(index, propertyName)); const dependencies = sort(mapDependencies(index, propertyName));
dependencies.forEach((depName) => { dependencies.forEach((depName) => {
const additions = docletFinder(index[depName], doclets, doclets.index); const additions = docletFinder(index[depName], doclets, doclets.index, jsdocDeps);
additions.forEach((addition) => { additions.forEach((addition) => {
const longname = addition.longname; const longname = addition.longname;

View File

@ -380,13 +380,19 @@ class Doclet {
* Create a doclet. * Create a doclet.
* *
* @param {string} docletSrc - The raw source code of the jsdoc comment. * @param {string} docletSrc - The raw source code of the jsdoc comment.
* @param {object=} meta - Properties describing the code related to this comment. * @param {object} meta - Properties describing the code related to this comment.
* @param {object} dependencies - JSDoc dependencies.
*/ */
constructor(docletSrc, meta = {}) { constructor(docletSrc, meta, dependencies) {
let newTags = []; let newTags = [];
meta = meta || {};
/** The original text of the comment from the source code. */ /** The original text of the comment from the source code. */
this.comment = docletSrc; this.comment = docletSrc;
Object.defineProperty(this, 'dependencies', {
enumerable: false,
value: dependencies,
});
this.setMeta(meta); this.setMeta(meta);
docletSrc = unwrap(docletSrc); docletSrc = unwrap(docletSrc);
@ -442,7 +448,7 @@ class Doclet {
*/ */
addTag(title, text) { addTag(title, text) {
const tagDef = dictionary.lookUp(title); const tagDef = dictionary.lookUp(title);
const newTag = new Tag(title, text, this.meta); const newTag = new Tag(title, text, this.meta, this.dependencies);
if (tagDef && tagDef.onTagged) { if (tagDef && tagDef.onTagged) {
tagDef.onTagged(this, newTag); tagDef.onTagged(this, newTag);
@ -663,7 +669,7 @@ exports.Doclet = Doclet;
exports.combine = (primary, secondary) => { exports.combine = (primary, secondary) => {
const copyMostPropertiesExclude = ['params', 'properties', 'undocumented']; const copyMostPropertiesExclude = ['params', 'properties', 'undocumented'];
const copySpecificPropertiesInclude = ['params', 'properties']; const copySpecificPropertiesInclude = ['params', 'properties'];
const target = new Doclet(''); const target = new Doclet('', null, secondary.dependencies);
// First, copy most properties to the target doclet. // First, copy most properties to the target doclet.
copyMostProperties(primary, secondary, target, copyMostPropertiesExclude); copyMostProperties(primary, secondary, target, copyMostPropertiesExclude);

View File

@ -25,18 +25,18 @@ function filterByLongname({ longname }) {
return false; return false;
} }
function createDoclet(comment, e) { function createDoclet(comment, e, deps) {
let doclet; let doclet;
let flatComment; let flatComment;
let msg; let msg;
try { try {
doclet = new Doclet(comment, e); doclet = new Doclet(comment, e, deps);
} catch (error) { } catch (error) {
flatComment = comment.replace(/[\r\n]/g, ''); flatComment = comment.replace(/[\r\n]/g, '');
msg = `cannot create a doclet for the comment "${flatComment}": ${error.message}`; msg = `cannot create a doclet for the comment "${flatComment}": ${error.message}`;
log.error(msg); log.error(msg);
doclet = new Doclet('', e); doclet = new Doclet('', e, deps);
} }
return doclet; return doclet;
@ -61,13 +61,13 @@ function createDoclet(comment, e) {
* *
* @private * @private
*/ */
function createSymbolDoclet(comment, e) { function createSymbolDoclet(comment, e, deps) {
let doclet = createDoclet(comment, e); let doclet = createDoclet(comment, e, deps);
if (doclet.name) { if (doclet.name) {
// try again, without the comment // try again, without the comment
e.comment = '@undocumented'; e.comment = '@undocumented';
doclet = createDoclet(e.comment, e); doclet = createDoclet(e.comment, e, deps);
} }
return doclet; return doclet;
@ -273,7 +273,7 @@ function addSymbolMemberof(parser, doclet, astNode) {
} }
function newSymbolDoclet(parser, docletSrc, e) { function newSymbolDoclet(parser, docletSrc, e) {
const newDoclet = createSymbolDoclet(docletSrc, e); const newDoclet = createSymbolDoclet(docletSrc, e, parser.dependencies);
// if there's an alias, use that as the symbol name // if there's an alias, use that as the symbol name
if (newDoclet.alias) { if (newDoclet.alias) {
@ -327,7 +327,7 @@ exports.attachTo = (parser) => {
let newDoclet; let newDoclet;
for (let i = 0, l = comments.length; i < l; i++) { for (let i = 0, l = comments.length; i < l; i++) {
newDoclet = createDoclet(comments[i], e); newDoclet = createDoclet(comments[i], e, parser.dependencies);
// we're only interested in virtual comments here // we're only interested in virtual comments here
if (!newDoclet.name) { if (!newDoclet.name) {

View File

@ -72,18 +72,24 @@ function definedInScope(doclet, basename) {
*/ */
class Parser extends EventEmitter { class Parser extends EventEmitter {
// TODO: docs // TODO: docs
constructor(conf) { constructor(dependencies) {
super(); super();
this.clear(); this.clear();
this._conf = conf || {}; this._conf = dependencies.get('config');
this._dependencies = dependencies;
this._visitor = new Visitor(); this._visitor = new Visitor();
this._walker = new Walker(); this._walker = new Walker();
this._visitor.setParser(this); this._visitor.setParser(this);
Object.defineProperties(this, { Object.defineProperties(this, {
dependencies: {
get() {
return this._dependencies;
},
},
visitor: { visitor: {
get() { get() {
return this._visitor; return this._visitor;
@ -626,8 +632,8 @@ class Parser extends EventEmitter {
exports.Parser = Parser; exports.Parser = Parser;
// TODO: docs // TODO: docs
exports.createParser = (config) => { exports.createParser = (deps) => {
return new Parser(config); return new Parser(deps);
}; };
// TODO: document other events // TODO: document other events

View File

@ -333,7 +333,7 @@ function makeConstructorFinisher(parser) {
// We prefer the parent doclet because it has the correct kind, longname, and memberof. // We prefer the parent doclet because it has the correct kind, longname, and memberof.
// The child doclet might or might not have the correct kind, longname, and memberof. // The child doclet might or might not have the correct kind, longname, and memberof.
combined = combineDoclets(parentDoclet, eventDoclet); combined = combineDoclets(parentDoclet, eventDoclet, parser.dependencies);
parser.addResult(combined); parser.addResult(combined);

View File

@ -2,7 +2,7 @@
* Functionality related to JSDoc tags. * Functionality related to JSDoc tags.
* @module jsdoc/tag * @module jsdoc/tag
*/ */
const env = require('jsdoc/env'); const _ = require('lodash');
const { log } = require('@jsdoc/util'); const { log } = require('@jsdoc/util');
const path = require('path'); const path = require('path');
const tag = { const tag = {
@ -22,7 +22,7 @@ function trim(text, opts, meta) {
let match; let match;
opts = opts || {}; opts = opts || {};
text = String(typeof text === 'undefined' ? '' : text); text = String(_.isNull(text) || _.isUndefined(text) ? '' : text);
if (mustPreserveWhitespace(text, meta)) { if (mustPreserveWhitespace(text, meta)) {
text = `"${text}"`; text = `"${text}"`;
@ -42,11 +42,13 @@ function trim(text, opts, meta) {
return text; return text;
} }
function addHiddenProperty(obj, propName, propValue) { function addHiddenProperty(obj, propName, propValue, dependencies) {
const options = dependencies.get('options');
Object.defineProperty(obj, propName, { Object.defineProperty(obj, propName, {
value: propValue, value: propValue,
writable: true, writable: true,
enumerable: Boolean(env.opts.debug), enumerable: Boolean(options.debug),
configurable: true, configurable: true,
}); });
} }
@ -71,7 +73,7 @@ function parseType({ text, originalTitle }, { canHaveName, canHaveType }, meta)
} }
} }
function processTagText(tagInstance, tagDef, meta) { function processTagText(tagInstance, tagDef, meta, dependencies) {
let tagType; let tagType;
if (tagDef.onTagText) { if (tagDef.onTagText) {
@ -92,7 +94,7 @@ function processTagText(tagInstance, tagDef, meta) {
tagInstance.value.type = { tagInstance.value.type = {
names: tagType.type, names: tagType.type,
}; };
addHiddenProperty(tagInstance.value.type, 'parsedType', tagType.parsedType); addHiddenProperty(tagInstance.value.type, 'parsedType', tagType.parsedType, dependencies);
} }
['optional', 'nullable', 'variable', 'defaultvalue'].forEach((prop) => { ['optional', 'nullable', 'variable', 'defaultvalue'].forEach((prop) => {
@ -138,15 +140,17 @@ class Tag {
* Constructs a new tag object. Calls the tag validator. * Constructs a new tag object. Calls the tag validator.
* *
* @param {string} tagTitle * @param {string} tagTitle
* @param {string=} tagBody * @param {string} tagBody
* @param {object=} meta * @param {object} meta
* @param {object} dependencies
*/ */
constructor(tagTitle, tagBody, meta) { constructor(tagTitle, tagBody, meta, dependencies) {
let tagDef; let tagDef;
let trimOpts; let trimOpts;
meta = meta || {}; meta = meta || {};
this.dependencies = dependencies;
this.originalTitle = trim(tagTitle); this.originalTitle = trim(tagTitle);
/** The title of the tag (for example, `title` in `@title text`). */ /** The title of the tag (for example, `title` in `@title text`). */
@ -179,7 +183,7 @@ class Tag {
this.text = trim(tagBody, trimOpts, meta); this.text = trim(tagBody, trimOpts, meta);
if (this.text) { if (this.text) {
processTagText(this, tagDef, meta); processTagText(this, tagDef, meta, dependencies);
} }
tag.validator.validate(this, tagDef, meta); tag.validator.validate(this, tagDef, meta);

View File

@ -5,13 +5,12 @@
const _ = require('lodash'); const _ = require('lodash');
const { applyNamespace, SCOPE, LONGNAMES } = require('@jsdoc/core').name; const { applyNamespace, SCOPE, LONGNAMES } = require('@jsdoc/core').name;
const commonPathPrefix = require('common-path-prefix'); const commonPathPrefix = require('common-path-prefix');
const env = require('jsdoc/env');
const { isInlineTag } = require('@jsdoc/tag').inline; const { isInlineTag } = require('@jsdoc/tag').inline;
const { log } = require('@jsdoc/util'); const { log } = require('@jsdoc/util');
const { nodeToValue } = require('@jsdoc/parse').astNode; const { nodeToValue } = require('@jsdoc/parse').astNode;
const parseTagType = require('@jsdoc/tag').type.parse;
const path = require('path'); const path = require('path');
const { Syntax } = require('@jsdoc/parse'); const { Syntax } = require('@jsdoc/parse');
const parseTagType = require('@jsdoc/tag').type.parse;
const hasOwnProp = Object.prototype.hasOwnProperty; const hasOwnProp = Object.prototype.hasOwnProperty;
@ -31,7 +30,7 @@ function cloneTagDef(tagDef, extras) {
return extras ? _.extend(newTagDef, extras) : newTagDef; return extras ? _.extend(newTagDef, extras) : newTagDef;
} }
function getSourcePaths() { function getSourcePaths(env) {
const sourcePaths = env.sourceFiles.slice(0) || []; const sourcePaths = env.sourceFiles.slice(0) || [];
if (env.opts._) { if (env.opts._) {
@ -47,9 +46,9 @@ function getSourcePaths() {
return sourcePaths; return sourcePaths;
} }
function filepathMinusPrefix(filepath) { function filepathMinusPrefix(filepath, env) {
let commonPrefix; let commonPrefix;
const sourcePaths = getSourcePaths(); const sourcePaths = getSourcePaths(env);
let result = ''; let result = '';
commonPrefix = commonPrefix =
@ -121,7 +120,8 @@ function setNameToFile(doclet) {
let docletName; let docletName;
if (doclet.meta.filename) { if (doclet.meta.filename) {
docletName = filepathMinusPrefix(doclet.meta.path) + doclet.meta.filename; docletName =
filepathMinusPrefix(doclet.meta.path, doclet.dependencies.get('env')) + doclet.meta.filename;
doclet.addTag('name', docletName); doclet.addTag('name', docletName);
} }
} }
@ -150,8 +150,9 @@ function setDocletNameToFilename(doclet) {
let docletName = ''; let docletName = '';
if (doclet.meta.path) { if (doclet.meta.path) {
docletName = filepathMinusPrefix(doclet.meta.path); docletName = filepathMinusPrefix(doclet.meta.path, doclet.dependencies.get('env'));
} }
// TODO: Drop the file extension regardless of what it is.
docletName += doclet.meta.filename.replace(/\.js$/i, ''); docletName += doclet.meta.filename.replace(/\.js$/i, '');
doclet.name = docletName; doclet.name = docletName;

View File

@ -2,7 +2,6 @@
* @module jsdoc/tag/validator * @module jsdoc/tag/validator
* @requires jsdoc/tag/dictionary * @requires jsdoc/tag/dictionary
*/ */
const env = require('jsdoc/env');
const { log } = require('@jsdoc/util'); const { log } = require('@jsdoc/util');
function buildMessage(tagName, { filename, lineno, comment }, desc) { function buildMessage(tagName, { filename, lineno, comment }, desc) {
@ -18,8 +17,9 @@ function buildMessage(tagName, { filename, lineno, comment }, desc) {
/** /**
* Validate the given tag. * Validate the given tag.
*/ */
exports.validate = ({ title, text, value }, tagDef, meta) => { exports.validate = ({ dependencies, title, text, value }, tagDef, meta) => {
const allowUnknownTags = env.conf.tags.allowUnknownTags; const config = dependencies.get('config');
const allowUnknownTags = config.tags.allowUnknownTags;
// handle cases where the tag definition does not exist // handle cases where the tag definition does not exist
if (!tagDef) { if (!tagDef) {

View File

@ -18,7 +18,7 @@ const helpers = {
doclets: doclets, doclets: doclets,
}); });
}, },
createParser, createParser: () => createParser(jsdoc.deps),
didLog: (fn, level) => { didLog: (fn, level) => {
const events = []; const events = [];
@ -55,7 +55,7 @@ const helpers = {
} }
return { return {
doclets: doclets, doclets,
getByLongname(longname) { getByLongname(longname) {
return doclets.filter((doclet) => (doclet.longname || doclet.name) === longname); return doclets.filter((doclet) => (doclet.longname || doclet.name) === longname);
}, },

View File

@ -34,10 +34,14 @@ describe('module names', () => {
]; ];
env.opts._ = []; env.opts._ = [];
doclet = new Doclet('/** @module */', { doclet = new Doclet(
lineno: 1, '/** @module */',
filename: 'C:\\Users\\Jane Smith\\myproject\\lib\\mymodule.js', {
}); lineno: 1,
filename: 'C:\\Users\\Jane Smith\\myproject\\lib\\mymodule.js',
},
jsdoc.deps
);
expect(doclet.name).toBe('lib/mymodule'); expect(doclet.name).toBe('lib/mymodule');
}); });

View File

@ -38,7 +38,7 @@ describe('jsdoc/doclet', () => {
function makeDoclet(tagStrings) { function makeDoclet(tagStrings) {
const comment = `/**\n${tagStrings.join('\n')}\n*/`; const comment = `/**\n${tagStrings.join('\n')}\n*/`;
return new Doclet(comment, {}); return new Doclet(comment, {}, jsdoc.deps);
} }
describe('aliases', () => { describe('aliases', () => {
@ -205,7 +205,7 @@ describe('jsdoc/doclet', () => {
describe('setScope', () => { describe('setScope', () => {
it('should accept the correct scope names', () => { it('should accept the correct scope names', () => {
function setScope(scopeName) { function setScope(scopeName) {
const newDoclet = new Doclet('/** Huzzah, a doclet! */'); const newDoclet = new Doclet('/** Huzzah, a doclet! */', null, jsdoc.deps);
newDoclet.setScope(scopeName); newDoclet.setScope(scopeName);
} }
@ -217,7 +217,7 @@ describe('jsdoc/doclet', () => {
it('should throw an error for invalid scope names', () => { it('should throw an error for invalid scope names', () => {
function setScope() { function setScope() {
const newDoclet = new Doclet('/** Woe betide this doclet. */'); const newDoclet = new Doclet('/** Woe betide this doclet. */', null, jsdoc.deps);
newDoclet.setScope('fiddlesticks'); newDoclet.setScope('fiddlesticks');
} }
@ -228,8 +228,12 @@ describe('jsdoc/doclet', () => {
describe('combine', () => { describe('combine', () => {
it('should override most properties of the secondary doclet', () => { it('should override most properties of the secondary doclet', () => {
const primaryDoclet = new Doclet('/** New and improved!\n@version 2.0.0 */'); const primaryDoclet = new Doclet(
const secondaryDoclet = new Doclet('/** Hello!\n@version 1.0.0 */'); '/** New and improved!\n@version 2.0.0 */',
null,
jsdoc.deps
);
const secondaryDoclet = new Doclet('/** Hello!\n@version 1.0.0 */', null, jsdoc.deps);
const newDoclet = doclet.combine(primaryDoclet, secondaryDoclet); const newDoclet = doclet.combine(primaryDoclet, secondaryDoclet);
Object.getOwnPropertyNames(newDoclet).forEach((property) => { Object.getOwnPropertyNames(newDoclet).forEach((property) => {
@ -238,8 +242,8 @@ describe('jsdoc/doclet', () => {
}); });
it('should add properties that are missing from the secondary doclet', () => { it('should add properties that are missing from the secondary doclet', () => {
const primaryDoclet = new Doclet('/** Hello!\n@version 2.0.0 */'); const primaryDoclet = new Doclet('/** Hello!\n@version 2.0.0 */', null, jsdoc.deps);
const secondaryDoclet = new Doclet('/** Hello! */'); const secondaryDoclet = new Doclet('/** Hello! */', null, jsdoc.deps);
const newDoclet = doclet.combine(primaryDoclet, secondaryDoclet); const newDoclet = doclet.combine(primaryDoclet, secondaryDoclet);
expect(newDoclet.version).toBe('2.0.0'); expect(newDoclet.version).toBe('2.0.0');
@ -252,14 +256,14 @@ describe('jsdoc/doclet', () => {
"should use the secondary doclet's params and properties if the primary doclet " + "should use the secondary doclet's params and properties if the primary doclet " +
'had none', 'had none',
() => { () => {
const primaryDoclet = new Doclet('/** Hello! */'); const primaryDoclet = new Doclet('/** Hello! */', null, jsdoc.deps);
const secondaryComment = [ const secondaryComment = [
'/**', '/**',
' * @param {string} foo - The foo.', ' * @param {string} foo - The foo.',
' * @property {number} bar - The bar.', ' * @property {number} bar - The bar.',
' */', ' */',
].join('\n'); ].join('\n');
const secondaryDoclet = new Doclet(secondaryComment); const secondaryDoclet = new Doclet(secondaryComment, null, jsdoc.deps);
const newDoclet = doclet.combine(primaryDoclet, secondaryDoclet); const newDoclet = doclet.combine(primaryDoclet, secondaryDoclet);
properties.forEach((property) => { properties.forEach((property) => {
@ -275,14 +279,14 @@ describe('jsdoc/doclet', () => {
' * @property {string} qux - The qux.', ' * @property {string} qux - The qux.',
' */', ' */',
].join('\n'); ].join('\n');
const primaryDoclet = new Doclet(primaryComment); const primaryDoclet = new Doclet(primaryComment, null, jsdoc.deps);
const secondaryComment = [ const secondaryComment = [
'/**', '/**',
' * @param {string} foo - The foo.', ' * @param {string} foo - The foo.',
' * @property {number} bar - The bar.', ' * @property {number} bar - The bar.',
' */', ' */',
].join('\n'); ].join('\n');
const secondaryDoclet = new Doclet(secondaryComment); const secondaryDoclet = new Doclet(secondaryComment, null, jsdoc.deps);
const newDoclet = doclet.combine(primaryDoclet, secondaryDoclet); const newDoclet = doclet.combine(primaryDoclet, secondaryDoclet);
properties.forEach((property) => { properties.forEach((property) => {

View File

@ -6,7 +6,6 @@ describe('jsdoc/src/parser', () => {
const jsdocParser = require('jsdoc/src/parser'); const jsdocParser = require('jsdoc/src/parser');
const path = require('path'); const path = require('path');
const config = jsdoc.deps.get('config');
const dirname = path.resolve(path.join(__dirname, '..', '..', '..', '..')); const dirname = path.resolve(path.join(__dirname, '..', '..', '..', '..'));
it('should exist', () => { it('should exist', () => {
@ -22,8 +21,8 @@ describe('jsdoc/src/parser', () => {
}); });
describe('createParser', () => { describe('createParser', () => {
it('should return a Parser when called with a config', () => { it('should return a Parser when called with dependencies', () => {
expect(jsdocParser.createParser(config)).toBeObject(); expect(jsdocParser.createParser(jsdoc.deps)).toBeObject();
}); });
}); });
@ -31,7 +30,7 @@ describe('jsdoc/src/parser', () => {
let parser; let parser;
beforeEach(() => { beforeEach(() => {
parser = new jsdocParser.Parser(); parser = new jsdocParser.Parser(jsdoc.deps);
}); });
it('should have a "visitor" property', () => { it('should have a "visitor" property', () => {

View File

@ -3,7 +3,7 @@ describe('jsdoc/src/visitor', () => {
const { Parser } = require('jsdoc/src/parser'); const { Parser } = require('jsdoc/src/parser');
const { Visitor } = require('jsdoc/src/visitor'); const { Visitor } = require('jsdoc/src/visitor');
const parser = new Parser(); const parser = new Parser(jsdoc.deps);
const visitor = new Visitor(); const visitor = new Visitor();
describe('visitNodeComments', () => { describe('visitNodeComments', () => {

View File

@ -45,17 +45,17 @@ describe('jsdoc/tag', () => {
// allow each test to recreate the tags (for example, after enabling debug mode) // allow each test to recreate the tags (for example, after enabling debug mode)
function createTags() { function createTags() {
// synonym for @param; space in the title // synonym for @param; space in the title
tagArg = new jsdocTag.Tag('arg ', text, meta); tagArg = new jsdocTag.Tag('arg ', text, meta, jsdoc.deps);
// @param with no type, but with optional and defaultvalue // @param with no type, but with optional and defaultvalue
tagParam = new jsdocTag.Tag('param', '[foo=1]', meta); tagParam = new jsdocTag.Tag('param', '[foo=1]', meta, jsdoc.deps);
// @param with type and no type modifiers (such as optional) // @param with type and no type modifiers (such as optional)
tagParamWithType = new jsdocTag.Tag('param', '{string} foo', meta); tagParamWithType = new jsdocTag.Tag('param', '{string} foo', meta, jsdoc.deps);
// @example that does not need indentation to be removed // @example that does not need indentation to be removed
tagExample = new jsdocTag.Tag('example', textExample, meta); tagExample = new jsdocTag.Tag('example', textExample, meta, jsdoc.deps);
// @example that needs indentation to be removed // @example that needs indentation to be removed
tagExampleIndented = new jsdocTag.Tag('example', textExampleIndented, meta); tagExampleIndented = new jsdocTag.Tag('example', textExampleIndented, meta, jsdoc.deps);
// for testing that onTagText is run when necessary // for testing that onTagText is run when necessary
tagType = new jsdocTag.Tag('type', 'MyType ', meta); tagType = new jsdocTag.Tag('type', 'MyType ', meta, jsdoc.deps);
} }
beforeEach(() => { beforeEach(() => {
@ -116,10 +116,10 @@ describe('jsdoc/tag', () => {
let wsTrailing; let wsTrailing;
function newTags() { function newTags() {
wsOnly = new jsdocTag.Tag('name', ' ', { code: { name: ' ' } }); wsOnly = new jsdocTag.Tag('name', ' ', { code: { name: ' ' } }, jsdoc.deps);
wsLeading = new jsdocTag.Tag('name', ' foo', { code: { name: ' foo' } }); wsLeading = new jsdocTag.Tag('name', ' foo', { code: { name: ' foo' } }, jsdoc.deps);
wsTrailing = new jsdocTag.Tag('name', 'foo ', { code: { name: 'foo ' } }); wsTrailing = new jsdocTag.Tag('name', 'foo ', { code: { name: 'foo ' } }, jsdoc.deps);
wsBoth = new jsdocTag.Tag('name', ' foo ', { code: { name: ' foo ' } }); wsBoth = new jsdocTag.Tag('name', ' foo ', { code: { name: ' foo ' } }, jsdoc.deps);
} }
expect(jsdoc.didLog(newTags, 'error')).toBeFalse(); expect(jsdoc.didLog(newTags, 'error')).toBeFalse();
@ -208,7 +208,7 @@ describe('jsdoc/tag', () => {
describe('tag validating', () => { describe('tag validating', () => {
it('logs an error for tags with bad type expressions', () => { it('logs an error for tags with bad type expressions', () => {
function newTag() { function newTag() {
return new jsdocTag.Tag('param', '{!*!*!*!} foo'); return new jsdocTag.Tag('param', '{!*!*!*!} foo', null, jsdoc.deps);
} }
expect(jsdoc.didLog(newTag, 'error')).toBeTrue(); expect(jsdoc.didLog(newTag, 'error')).toBeTrue();
@ -216,7 +216,7 @@ describe('jsdoc/tag', () => {
it('validates tags with no text', () => { it('validates tags with no text', () => {
function newTag() { function newTag() {
return new jsdocTag.Tag('copyright'); return new jsdocTag.Tag('copyright', null, null, jsdoc.deps);
} }
expect(jsdoc.didLog(newTag, 'error')).toBeTrue(); expect(jsdoc.didLog(newTag, 'error')).toBeTrue();

View File

@ -18,15 +18,15 @@ describe('jsdoc/tag/validator', () => {
const dictionary = require('jsdoc/tag/dictionary'); const dictionary = require('jsdoc/tag/dictionary');
const allowUnknown = Boolean(config.tags.allowUnknownTags); const allowUnknown = Boolean(config.tags.allowUnknownTags);
const badTag = { title: 'lkjasdlkjfb' }; const badTag = { dependencies: jsdoc.deps, title: 'lkjasdlkjfb' };
const badTag2 = new tag.Tag('type', '{string} I am a string!'); const badTag2 = new tag.Tag('type', '{string} I am a string!', null, jsdoc.deps);
const meta = { const meta = {
filename: 'asdf.js', filename: 'asdf.js',
lineno: 1, lineno: 1,
comment: 'Better luck next time.', comment: 'Better luck next time.',
}; };
const goodTag = new tag.Tag('name', 'MyDocletName', meta); // mustHaveValue const goodTag = new tag.Tag('name', 'MyDocletName', meta, jsdoc.deps); // mustHaveValue
const goodTag2 = new tag.Tag('ignore', '', meta); // mustNotHaveValue const goodTag2 = new tag.Tag('ignore', '', meta, jsdoc.deps); // mustNotHaveValue
function validateTag(theTag) { function validateTag(theTag) {
validator.validate(theTag, dictionary.lookUp(theTag.title), meta); validator.validate(theTag, dictionary.lookUp(theTag.title), meta);

View File

@ -669,7 +669,7 @@ describe('jsdoc/util/templateHelper', () => {
let attribs; let attribs;
it('should return an array', () => { it('should return an array', () => {
doc = new doclet.Doclet('/** ljklajsdf */', {}); doc = new doclet.Doclet('/** ljklajsdf */', {}, jsdoc.deps);
attribs = helper.getAttribs(doc); attribs = helper.getAttribs(doc);
expect(attribs).toBeEmptyArray(); expect(attribs).toBeEmptyArray();
@ -681,7 +681,7 @@ describe('jsdoc/util/templateHelper', () => {
function doTests(tests, whatNotToContain) { function doTests(tests, whatNotToContain) {
for (const src in tests) { for (const src in tests) {
if (hasOwnProp.call(tests, src)) { if (hasOwnProp.call(tests, src)) {
doc = new doclet.Doclet(`/** ${src} */`, {}); doc = new doclet.Doclet(`/** ${src} */`, {}, jsdoc.deps);
attribs = helper.getAttribs(doc); attribs = helper.getAttribs(doc);
if (tests[src]) { if (tests[src]) {
@ -782,7 +782,11 @@ describe('jsdoc/util/templateHelper', () => {
}); });
it('should detect multiple attributes', () => { it('should detect multiple attributes', () => {
const fdsaFoo = new doclet.Doclet('/** @const module:fdsa~FOO\n@readonly\n@private */', {}); const fdsaFoo = new doclet.Doclet(
'/** @const module:fdsa~FOO\n@readonly\n@private */',
{},
jsdoc.deps
);
attribs = helper.getAttribs(fdsaFoo); attribs = helper.getAttribs(fdsaFoo);
@ -814,14 +818,14 @@ describe('jsdoc/util/templateHelper', () => {
// returns links to allowed types for a doclet. // returns links to allowed types for a doclet.
it('returns an empty array if the doclet has no specified type', () => { it('returns an empty array if the doclet has no specified type', () => {
const doc = new doclet.Doclet('/** @const ASDF */', {}); const doc = new doclet.Doclet('/** @const ASDF */', {}, jsdoc.deps);
const types = helper.getSignatureTypes(doc); const types = helper.getSignatureTypes(doc);
expect(types).toBeEmptyArray(); expect(types).toBeEmptyArray();
}); });
it("returns a string array of the doclet's types", () => { it("returns a string array of the doclet's types", () => {
const doc = new doclet.Doclet('/** @const {number|Array.<boolean>} ASDF */', {}); const doc = new doclet.Doclet('/** @const {number|Array.<boolean>} ASDF */', {}, jsdoc.deps);
const types = helper.getSignatureTypes(doc); const types = helper.getSignatureTypes(doc);
expect(types).toBeArrayOfSize(2); expect(types).toBeArrayOfSize(2);
@ -836,7 +840,7 @@ describe('jsdoc/util/templateHelper', () => {
// make some links. // make some links.
helper.longnameToUrl.MyClass = 'MyClass.html'; helper.longnameToUrl.MyClass = 'MyClass.html';
doc = new doclet.Doclet('/** @const {MyClass} ASDF */', {}); doc = new doclet.Doclet('/** @const {MyClass} ASDF */', {}, jsdoc.deps);
types = helper.getSignatureTypes(doc); types = helper.getSignatureTypes(doc);
expect(types).toBeArrayOfSize(1); expect(types).toBeArrayOfSize(1);
@ -850,7 +854,7 @@ describe('jsdoc/util/templateHelper', () => {
// make some links. // make some links.
helper.longnameToUrl.MyClass = 'MyClass.html'; helper.longnameToUrl.MyClass = 'MyClass.html';
doc = new doclet.Doclet('/** @const {MyClass} ASDF */', {}); doc = new doclet.Doclet('/** @const {MyClass} ASDF */', {}, jsdoc.deps);
types = helper.getSignatureTypes(doc, 'myCSSClass'); types = helper.getSignatureTypes(doc, 'myCSSClass');
expect(types).toBeArrayOfSize(1); expect(types).toBeArrayOfSize(1);
@ -862,7 +866,7 @@ describe('jsdoc/util/templateHelper', () => {
// retrieves parameter names. // retrieves parameter names.
// if css class is provided, optional parameters are wrapped in a <span> with that class. // if css class is provided, optional parameters are wrapped in a <span> with that class.
it('returns an empty array if the doclet has no specified type', () => { it('returns an empty array if the doclet has no specified type', () => {
const doc = new doclet.Doclet('/** @function myFunction */', {}); const doc = new doclet.Doclet('/** @function myFunction */', {}, jsdoc.deps);
const params = helper.getSignatureParams(doc); const params = helper.getSignatureParams(doc);
expect(params).toBeEmptyArray(); expect(params).toBeEmptyArray();
@ -871,7 +875,8 @@ describe('jsdoc/util/templateHelper', () => {
it("returns a string array of the doclet's parameter names", () => { it("returns a string array of the doclet's parameter names", () => {
const doc = new doclet.Doclet( const doc = new doclet.Doclet(
'/** @function myFunction\n @param {string} foo - asdf. */', '/** @function myFunction\n @param {string} foo - asdf. */',
{} {},
jsdoc.deps
); );
const params = helper.getSignatureParams(doc); const params = helper.getSignatureParams(doc);
@ -886,7 +891,8 @@ describe('jsdoc/util/templateHelper', () => {
' * @param {number} [bar=1] - another explanation.\n' + ' * @param {number} [bar=1] - another explanation.\n' +
' * @param {string} [baz] - another explanation.\n' + ' * @param {string} [baz] - another explanation.\n' +
' */', ' */',
{} {},
jsdoc.deps
); );
const params = helper.getSignatureParams(doc, 'cssClass'); const params = helper.getSignatureParams(doc, 'cssClass');
@ -903,7 +909,8 @@ describe('jsdoc/util/templateHelper', () => {
' * @param {number} [bar=1] - another explanation.\n' + ' * @param {number} [bar=1] - another explanation.\n' +
' * @param {string} [baz] - another explanation.\n' + ' * @param {string} [baz] - another explanation.\n' +
' */', ' */',
{} {},
jsdoc.deps
); );
const params = helper.getSignatureParams(doc); const params = helper.getSignatureParams(doc);
@ -936,7 +943,7 @@ describe('jsdoc/util/templateHelper', () => {
}); });
it('returns an empty array if the doclet has no returns', () => { it('returns an empty array if the doclet has no returns', () => {
const doc = new doclet.Doclet('/** @function myFunction */', {}); const doc = new doclet.Doclet('/** @function myFunction */', {}, jsdoc.deps);
const returns = helper.getSignatureReturns(doc); const returns = helper.getSignatureReturns(doc);
expect(returns).toBeEmptyArray(); expect(returns).toBeEmptyArray();
@ -945,7 +952,8 @@ describe('jsdoc/util/templateHelper', () => {
it('returns an empty array if the doclet has @returns but with no type', () => { it('returns an empty array if the doclet has @returns but with no type', () => {
const doc = new doclet.Doclet( const doc = new doclet.Doclet(
'/** @function myFunction\n@returns an interesting result.*/', '/** @function myFunction\n@returns an interesting result.*/',
{} {},
jsdoc.deps
); );
const returns = helper.getSignatureReturns(doc); const returns = helper.getSignatureReturns(doc);
@ -953,14 +961,14 @@ describe('jsdoc/util/templateHelper', () => {
}); });
it('uses the value of the `yields` property', () => { it('uses the value of the `yields` property', () => {
const doc = new doclet.Doclet('/** @yields {string} A string. */', {}); const doc = new doclet.Doclet('/** @yields {string} A string. */', {}, jsdoc.deps);
const html = helper.getSignatureReturns(doc); const html = helper.getSignatureReturns(doc);
expect(html).toContain('string'); expect(html).toContain('string');
}); });
it('prefers `yields` over `returns`', () => { it('prefers `yields` over `returns`', () => {
const doc = new doclet.Doclet('/** @yields {string}\n@returns {number} */', {}); const doc = new doclet.Doclet('/** @yields {string}\n@returns {number} */', {}, jsdoc.deps);
const html = helper.getSignatureReturns(doc); const html = helper.getSignatureReturns(doc);
expect(html).toContain('string'); expect(html).toContain('string');
@ -976,7 +984,8 @@ describe('jsdoc/util/templateHelper', () => {
doc = new doclet.Doclet( doc = new doclet.Doclet(
'/** @function myFunction\n@returns {number|MyClass} an interesting result.*/', '/** @function myFunction\n@returns {number|MyClass} an interesting result.*/',
{} {},
jsdoc.deps
); );
returns = helper.getSignatureReturns(doc); returns = helper.getSignatureReturns(doc);
@ -994,7 +1003,8 @@ describe('jsdoc/util/templateHelper', () => {
doc = new doclet.Doclet( doc = new doclet.Doclet(
'/** @function myFunction\n@returns {number|MyClass} an interesting result.*/', '/** @function myFunction\n@returns {number|MyClass} an interesting result.*/',
{} {},
jsdoc.deps
); );
returns = helper.getSignatureReturns(doc, 'myCssClass'); returns = helper.getSignatureReturns(doc, 'myCssClass');
@ -1012,14 +1022,16 @@ describe('jsdoc/util/templateHelper', () => {
// make a hierarchy. // make a hierarchy.
const lackeys = new doclet.Doclet( const lackeys = new doclet.Doclet(
'/** @member lackeys\n@memberof module:mafia/gangs.Sharks~Henchman\n@instance*/', '/** @member lackeys\n@memberof module:mafia/gangs.Sharks~Henchman\n@instance*/',
{} {},
jsdoc.deps
); );
const henchman = new doclet.Doclet( const henchman = new doclet.Doclet(
'/** @class Henchman\n@memberof module:mafia/gangs.Sharks\n@inner */', '/** @class Henchman\n@memberof module:mafia/gangs.Sharks\n@inner */',
{} {},
jsdoc.deps
); );
const gang = new doclet.Doclet('/** @namespace module:mafia/gangs.Sharks */', {}); const gang = new doclet.Doclet('/** @namespace module:mafia/gangs.Sharks */', {}, jsdoc.deps);
const mafia = new doclet.Doclet('/** @module mafia/gangs */', {}); const mafia = new doclet.Doclet('/** @module mafia/gangs */', {}, jsdoc.deps);
const data = taffy([lackeys, henchman, gang, mafia]); const data = taffy([lackeys, henchman, gang, mafia]);
afterEach(() => { afterEach(() => {

View File

@ -2,8 +2,8 @@ describe('@lends tag', () => {
// see also specs/documentation/lends.js for tests on @lends behaviour. // see also specs/documentation/lends.js for tests on @lends behaviour.
const { Doclet } = require('jsdoc/doclet'); const { Doclet } = require('jsdoc/doclet');
const doc = new Doclet('/** @lends */', {}); const doc = new Doclet('/** @lends */', {}, jsdoc.deps);
const doc2 = new Doclet('/** @lends MyClass# */', {}); const doc2 = new Doclet('/** @lends MyClass# */', {}, jsdoc.deps);
it("sets the doclet's 'alias' property to the tag value or <global>", () => { it("sets the doclet's 'alias' property to the tag value or <global>", () => {
expect(doc.alias).toBe('<global>'); expect(doc.alias).toBe('<global>');

View File

@ -58,7 +58,7 @@ describe('@overview tag', () => {
lineno: 1, lineno: 1,
filename: fakePath, filename: fakePath,
}; };
doclet = new Doclet(docletSrc, docletMeta); doclet = new Doclet(docletSrc, docletMeta, jsdoc.deps);
doclet.addTag('file', 'A random file.'); doclet.addTag('file', 'A random file.');
expect(doclet.name).toBe('somefile.js'); expect(doclet.name).toBe('somefile.js');