mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
refactor(jsdoc): shift functionality into tag dictionaries
With an eye towards removing the `jsdoc/env` dependency later. Notable changes include: + `Dictionary` now has methods to define tags. (Those methods used to be tied to the tag definitions, which seems backwards to me.) + `Dictionary` now has a `fromConfig()` static method that takes `jsdoc/env` as an argument and returns a new dictionary that's set up appropriately.
This commit is contained in:
parent
8a0a078f94
commit
3025520e15
@ -17,9 +17,11 @@ const {
|
||||
SCOPE_TO_PUNC,
|
||||
toParts
|
||||
} = require('@jsdoc/core').name;
|
||||
const helper = require('jsdoc/util/templateHelper');
|
||||
const path = require('path');
|
||||
const { Syntax } = require('@jsdoc/parse');
|
||||
const { Tag } = require('jsdoc/tag');
|
||||
const tag = require('jsdoc/tag');
|
||||
const Tag = tag.Tag;
|
||||
|
||||
const DEFAULT_SCOPE = SCOPE.NAMES.STATIC;
|
||||
|
||||
@ -282,8 +284,8 @@ function resolve(doclet) {
|
||||
*/
|
||||
exports._replaceDictionary = function _replaceDictionary(dict) {
|
||||
dictionary = dict;
|
||||
require('jsdoc/tag')._replaceDictionary(dict);
|
||||
require('jsdoc/util/templateHelper')._replaceDictionary(dict);
|
||||
tag._replaceDictionary(dict);
|
||||
helper._replaceDictionary(dict);
|
||||
};
|
||||
|
||||
function removeGlobal(longname) {
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
/** @module jsdoc/tag/dictionary */
|
||||
const definitions = require('jsdoc/tag/dictionary/definitions');
|
||||
const jsdocEnv = require('jsdoc/env');
|
||||
const { log } = require('@jsdoc/util');
|
||||
|
||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
const DEFINITIONS = {
|
||||
closure: 'closureTags',
|
||||
jsdoc: 'jsdocTags'
|
||||
};
|
||||
|
||||
let dictionary;
|
||||
|
||||
/** @private */
|
||||
@ -12,7 +19,7 @@ class TagDefinition {
|
||||
|
||||
etc = etc || {};
|
||||
|
||||
this.title = dict.normalise(title);
|
||||
this.title = dict.normalize(title);
|
||||
|
||||
Object.defineProperty(this, '_dictionary', {
|
||||
value: dict
|
||||
@ -36,15 +43,19 @@ class TagDefinition {
|
||||
*/
|
||||
class Dictionary {
|
||||
constructor() {
|
||||
// TODO: Consider adding internal tags in the constructor, ideally as fallbacks that aren't
|
||||
// used to confirm whether a tag is defined/valid, rather than requiring every set of tag
|
||||
// definitions to contain the internal tags.
|
||||
this._tags = {};
|
||||
this._tagSynonyms = {};
|
||||
// The longnames for `Package` objects include a `package` namespace. There's no `package` tag,
|
||||
// though, so we declare the namespace here.
|
||||
// The longnames for `Package` objects include a `package` namespace. There's no `package`
|
||||
// tag, though, so we declare the namespace here.
|
||||
// TODO: Consider making this a fallback as suggested above for internal tags.
|
||||
this._namespaces = ['package'];
|
||||
}
|
||||
|
||||
_defineNamespace(title) {
|
||||
title = this.normalise(title || '');
|
||||
title = this.normalize(title || '');
|
||||
|
||||
if (title && !this._namespaces.includes(title)) {
|
||||
this._namespaces.push(title);
|
||||
@ -58,34 +69,70 @@ class Dictionary {
|
||||
|
||||
this._tags[tagDef.title] = tagDef;
|
||||
|
||||
if (opts && opts.isNamespace) {
|
||||
if (tagDef.isNamespace) {
|
||||
this._defineNamespace(tagDef.title);
|
||||
}
|
||||
if (tagDef.synonyms) {
|
||||
tagDef.synonyms.forEach(synonym => {
|
||||
this.defineSynonym(title, synonym);
|
||||
});
|
||||
}
|
||||
|
||||
return this._tags[tagDef.title];
|
||||
}
|
||||
|
||||
defineTags(tagDefs) {
|
||||
const tags = {};
|
||||
|
||||
for (const title of Object.keys(tagDefs)) {
|
||||
tags[title] = this.defineTag(title, tagDefs[title]);
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
defineSynonym(title, synonym) {
|
||||
this._tagSynonyms[synonym.toLowerCase()] = this.normalise(title);
|
||||
this._tagSynonyms[synonym.toLowerCase()] = this.normalize(title);
|
||||
}
|
||||
|
||||
static fromConfig(env) {
|
||||
let dictionaries = env.conf.tags.dictionaries;
|
||||
const dict = new Dictionary();
|
||||
|
||||
if (!dictionaries) {
|
||||
log.error(
|
||||
'The configuration setting "tags.dictionaries" is undefined. ' +
|
||||
'Unable to load tag definitions.'
|
||||
);
|
||||
} else {
|
||||
dictionaries.slice().reverse().forEach(dictName => {
|
||||
const tagDefs = definitions[DEFINITIONS[dictName]];
|
||||
|
||||
if (!tagDefs) {
|
||||
log.error(
|
||||
'The configuration setting "tags.dictionaries" contains ' +
|
||||
`the unknown dictionary name ${dictName}. Ignoring the dictionary.`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dict.defineTags(tagDefs);
|
||||
});
|
||||
|
||||
dict.defineTags(definitions.internalTags);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
getNamespaces() {
|
||||
return this._namespaces.slice(0);
|
||||
}
|
||||
|
||||
lookUp(title) {
|
||||
title = this.normalise(title);
|
||||
|
||||
if ( hasOwnProp.call(this._tags, title) ) {
|
||||
return this._tags[title];
|
||||
}
|
||||
|
||||
return false;
|
||||
return this._namespaces.slice();
|
||||
}
|
||||
|
||||
isNamespace(kind) {
|
||||
if (kind) {
|
||||
kind = this.normalise(kind);
|
||||
kind = this.normalize(kind);
|
||||
if (this._namespaces.includes(kind)) {
|
||||
return true;
|
||||
}
|
||||
@ -94,7 +141,25 @@ class Dictionary {
|
||||
return false;
|
||||
}
|
||||
|
||||
lookup(title) {
|
||||
title = this.normalize(title);
|
||||
|
||||
if ( hasOwnProp.call(this._tags, title) ) {
|
||||
return this._tags[title];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
lookUp(title) {
|
||||
return this.lookup(title);
|
||||
}
|
||||
|
||||
normalise(title) {
|
||||
return this.normalize(title);
|
||||
}
|
||||
|
||||
normalize(title) {
|
||||
const canonicalName = title.toLowerCase();
|
||||
|
||||
if ( hasOwnProp.call(this._tagSynonyms, canonicalName) ) {
|
||||
@ -103,15 +168,10 @@ class Dictionary {
|
||||
|
||||
return canonicalName;
|
||||
}
|
||||
|
||||
normalize(title) {
|
||||
return this.normalise(title);
|
||||
}
|
||||
}
|
||||
|
||||
// initialize the default dictionary
|
||||
dictionary = new Dictionary();
|
||||
definitions.defineTags(dictionary);
|
||||
dictionary = Dictionary.fromConfig(jsdocEnv);
|
||||
|
||||
// make the constructor available for unit-testing purposes
|
||||
dictionary.Dictionary = Dictionary;
|
||||
|
||||
@ -15,11 +15,12 @@ const parseTagType = require('@jsdoc/tag').type.parse;
|
||||
|
||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
const DEFINITIONS = {
|
||||
closure: 'closureTags',
|
||||
jsdoc: 'jsdocTags'
|
||||
};
|
||||
const MODULE_NAMESPACE = 'module:';
|
||||
const NOOP_TAG = {
|
||||
onTagged: () => {
|
||||
// Do nothing.
|
||||
}
|
||||
};
|
||||
|
||||
// Clone a tag definition, excluding synonyms.
|
||||
function cloneTagDef(tagDef, extras) {
|
||||
@ -214,7 +215,7 @@ function combineTypes({value}) {
|
||||
}
|
||||
|
||||
// Tags that JSDoc uses internally, and that must always be defined.
|
||||
const internalTags = {
|
||||
const internalTags = exports.internalTags = {
|
||||
// Special separator tag indicating that multiple doclets should be generated for the same
|
||||
// comment. Used internally (and by some JSDoc users, although it's not officially supported).
|
||||
// In the following example, the parser will replace `//**` with an `@also` tag:
|
||||
@ -855,10 +856,6 @@ baseTags = _.extend(baseTags, internalTags);
|
||||
// Tag dictionary for JSDoc.
|
||||
exports.jsdocTags = baseTags;
|
||||
|
||||
function ignore() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// Tag dictionary for Google Closure Compiler.
|
||||
exports.closureTags = {
|
||||
const: {
|
||||
@ -873,18 +870,12 @@ exports.closureTags = {
|
||||
constructor: cloneTagDef(baseTags.class),
|
||||
deprecated: cloneTagDef(baseTags.deprecated),
|
||||
// Closure Compiler only
|
||||
dict: {
|
||||
onTagged: ignore
|
||||
},
|
||||
dict: NOOP_TAG,
|
||||
enum: cloneTagDef(baseTags.enum),
|
||||
// Closure Compiler only
|
||||
export: {
|
||||
onTagged: ignore
|
||||
},
|
||||
export: NOOP_TAG,
|
||||
// Closure Compiler only
|
||||
externs: {
|
||||
onTagged: ignore
|
||||
},
|
||||
externs: NOOP_TAG,
|
||||
extends: cloneTagDef(baseTags.augments),
|
||||
fileoverview: {
|
||||
onTagged(doclet, tag) {
|
||||
@ -898,9 +889,7 @@ exports.closureTags = {
|
||||
final: cloneTagDef(baseTags.readonly),
|
||||
implements: cloneTagDef(baseTags.implements),
|
||||
// Closure Compiler only
|
||||
implicitcast: {
|
||||
onTagged: ignore
|
||||
},
|
||||
implicitcast: NOOP_TAG,
|
||||
inheritdoc: cloneTagDef(baseTags.inheritdoc),
|
||||
interface: cloneTagDef(baseTags.interface, {
|
||||
canHaveName: false,
|
||||
@ -912,17 +901,11 @@ exports.closureTags = {
|
||||
license: cloneTagDef(baseTags.license),
|
||||
modifies: cloneTagDef(baseTags.modifies),
|
||||
// Closure Compiler only
|
||||
noalias: {
|
||||
onTagged: ignore
|
||||
},
|
||||
noalias: NOOP_TAG,
|
||||
// Closure Compiler only
|
||||
nocollapse: {
|
||||
onTagged: ignore
|
||||
},
|
||||
nocollapse: NOOP_TAG,
|
||||
// Closure Compiler only
|
||||
nocompile: {
|
||||
onTagged: ignore
|
||||
},
|
||||
nocompile: NOOP_TAG,
|
||||
// Closure Compiler only
|
||||
nosideeffects: {
|
||||
onTagged(doclet) {
|
||||
@ -948,13 +931,9 @@ exports.closureTags = {
|
||||
},
|
||||
param: cloneTagDef(baseTags.param),
|
||||
// Closure Compiler only
|
||||
polymer: {
|
||||
onTagged: ignore
|
||||
},
|
||||
polymer: NOOP_TAG,
|
||||
// Closure Compiler only
|
||||
polymerBehavior: {
|
||||
onTagged: ignore
|
||||
},
|
||||
polymerBehavior: NOOP_TAG,
|
||||
// Closure Compiler only
|
||||
preserve: cloneTagDef(baseTags.license),
|
||||
private: {
|
||||
@ -989,17 +968,11 @@ exports.closureTags = {
|
||||
},
|
||||
return: cloneTagDef(baseTags.returns),
|
||||
// Closure Compiler only
|
||||
struct: {
|
||||
onTagged: ignore
|
||||
},
|
||||
struct: NOOP_TAG,
|
||||
// Closure Compiler only
|
||||
suppress: {
|
||||
onTagged: ignore
|
||||
},
|
||||
suppress: NOOP_TAG,
|
||||
// Closure Compiler only
|
||||
template: {
|
||||
onTagged: ignore
|
||||
},
|
||||
template: NOOP_TAG,
|
||||
'this': {
|
||||
canHaveType: true,
|
||||
onTagged(doclet, tag) {
|
||||
@ -1018,72 +991,5 @@ exports.closureTags = {
|
||||
}
|
||||
},
|
||||
// Closure Compiler only
|
||||
unrestricted: {
|
||||
onTagged: ignore
|
||||
}
|
||||
};
|
||||
|
||||
function addTagDefinitions(dictionary, tagDefs) {
|
||||
Object.keys(tagDefs).forEach(tagName => {
|
||||
let tagDef;
|
||||
|
||||
tagDef = tagDefs[tagName];
|
||||
dictionary.defineTag(tagName, tagDef);
|
||||
|
||||
if (tagDef.synonyms) {
|
||||
tagDef.synonyms.forEach(synonym => {
|
||||
dictionary.defineSynonym(tagName, synonym);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the given dictionary with the appropriate JSDoc tag definitions.
|
||||
*
|
||||
* If the `tagDefinitions` parameter is omitted, JSDoc uses its configuration settings to decide
|
||||
* which tags to add to the dictionary.
|
||||
*
|
||||
* If the `tagDefinitions` parameter is included, JSDoc adds only the tag definitions from the
|
||||
* `tagDefinitions` object. The configuration settings are ignored.
|
||||
*
|
||||
* @param {module:jsdoc/tag/dictionary} dictionary
|
||||
* @param {Object} [tagDefinitions] - A dictionary whose values define the rules for a JSDoc tag.
|
||||
*/
|
||||
exports.defineTags = (dictionary, tagDefinitions) => {
|
||||
let dictionaries;
|
||||
|
||||
if (!tagDefinitions) {
|
||||
dictionaries = env.conf.tags.dictionaries;
|
||||
|
||||
if (!dictionaries) {
|
||||
log.error(
|
||||
'The configuration setting "tags.dictionaries" is undefined. ' +
|
||||
'Unable to load tag definitions.'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
dictionaries = dictionaries.slice(0).reverse();
|
||||
}
|
||||
|
||||
dictionaries.forEach(dictName => {
|
||||
const tagDefs = exports[DEFINITIONS[dictName]];
|
||||
|
||||
if (!tagDefs) {
|
||||
log.error(
|
||||
'The configuration setting "tags.dictionaries" contains ' +
|
||||
`the unknown dictionary name ${dictName}. Ignoring the dictionary.`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
addTagDefinitions(dictionary, _.extend(tagDefs, internalTags));
|
||||
});
|
||||
}
|
||||
else {
|
||||
addTagDefinitions(dictionary, _.extend(tagDefinitions, internalTags));
|
||||
}
|
||||
unrestricted: NOOP_TAG
|
||||
};
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
const { _replaceDictionary } = require('jsdoc/doclet');
|
||||
const { augmentAll } = require('jsdoc/augment');
|
||||
const { createParser } = require('jsdoc/src/parser');
|
||||
const { defineTags } = require('jsdoc/tag/dictionary/definitions');
|
||||
const dictionary = require('jsdoc/tag/dictionary');
|
||||
const { Dictionary } = require('jsdoc/tag/dictionary');
|
||||
const env = require('jsdoc/env');
|
||||
const { EventBus } = require('@jsdoc/util');
|
||||
const fs = require('fs');
|
||||
@ -10,7 +9,7 @@ const handlers = require('jsdoc/src/handlers');
|
||||
const path = require('path');
|
||||
|
||||
const bus = new EventBus('jsdoc');
|
||||
const originalDictionary = dictionary;
|
||||
const originalDictionaries = env.conf.tags.dictionaries.slice();
|
||||
const parseResults = [];
|
||||
|
||||
const helpers = global.jsdoc = {
|
||||
@ -65,8 +64,7 @@ const helpers = global.jsdoc = {
|
||||
},
|
||||
getParseResults: () => parseResults,
|
||||
replaceTagDictionary: dictionaryNames => {
|
||||
const dict = new dictionary.Dictionary();
|
||||
const originalDictionaries = env.conf.tags.dictionaries.slice(0);
|
||||
let dict;
|
||||
|
||||
if (!Array.isArray(dictionaryNames)) {
|
||||
dictionaryNames = [dictionaryNames];
|
||||
@ -74,12 +72,13 @@ const helpers = global.jsdoc = {
|
||||
|
||||
env.conf.tags.dictionaries = dictionaryNames;
|
||||
|
||||
defineTags(dict);
|
||||
dict = Dictionary.fromConfig(env);
|
||||
dict.Dictionary = Dictionary;
|
||||
_replaceDictionary(dict);
|
||||
|
||||
env.conf.tags.dictionaries = originalDictionaries;
|
||||
},
|
||||
restoreTagDictionary: () => {
|
||||
_replaceDictionary(originalDictionary);
|
||||
_replaceDictionary(Dictionary.fromConfig(env));
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,48 +1,63 @@
|
||||
describe('jsdoc/tag/dictionary', () => {
|
||||
const dictionary = require('jsdoc/tag/dictionary');
|
||||
const testDictionary = new dictionary.Dictionary();
|
||||
const Dictionary = dictionary.Dictionary;
|
||||
const env = require('jsdoc/env');
|
||||
|
||||
let testDictionary;
|
||||
const tagOptions = {
|
||||
canHaveValue: true,
|
||||
isNamespace: true
|
||||
};
|
||||
const TAG_TITLE = '!!!testTag!!!';
|
||||
let TAG_DEF;
|
||||
const TAG_SYNONYM = '!!!testTagSynonym!!!';
|
||||
const TAG_DEF = testDictionary.defineTag(TAG_TITLE, tagOptions).synonym(TAG_SYNONYM);
|
||||
const TAG_TITLE = '!!!testTag!!!';
|
||||
|
||||
it('should exist', () => {
|
||||
expect(dictionary).toBeObject();
|
||||
beforeEach(() => {
|
||||
testDictionary = new Dictionary();
|
||||
TAG_DEF = testDictionary.defineTag(TAG_TITLE, tagOptions).synonym(TAG_SYNONYM);
|
||||
});
|
||||
|
||||
it('should be an instance of dictionary.Dictionary', () => {
|
||||
it('is an instance of dictionary.Dictionary', () => {
|
||||
expect(dictionary instanceof dictionary.Dictionary).toBe(true);
|
||||
});
|
||||
|
||||
it('should export a defineSynonym method', () => {
|
||||
it('has a defineSynonym method', () => {
|
||||
expect(dictionary.defineSynonym).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a defineTag method', () => {
|
||||
it('has a defineTag method', () => {
|
||||
expect(dictionary.defineTag).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a lookUp method', () => {
|
||||
it('has a defineTags method', () => {
|
||||
expect(dictionary.defineTags).toBeFunction();
|
||||
});
|
||||
|
||||
it('has a fromConfig static method', () => {
|
||||
expect(dictionary.Dictionary.fromConfig).toBeFunction();
|
||||
});
|
||||
|
||||
it('has a lookup method', () => {
|
||||
expect(dictionary.lookup).toBeFunction();
|
||||
});
|
||||
|
||||
it('has a lookUp method', () => {
|
||||
expect(dictionary.lookUp).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export an isNamespace method', () => {
|
||||
it('has an isNamespace method', () => {
|
||||
expect(dictionary.isNamespace).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a normalise method', () => {
|
||||
it('has a normalise method', () => {
|
||||
expect(dictionary.normalise).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a normalize method', () => {
|
||||
it('has a normalize method', () => {
|
||||
expect(dictionary.normalize).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a Dictionary constructor', () => {
|
||||
it('has a Dictionary constructor', () => {
|
||||
expect(dictionary.Dictionary).toBeFunction();
|
||||
});
|
||||
|
||||
@ -77,19 +92,147 @@ describe('jsdoc/tag/dictionary', () => {
|
||||
expect(makeTag).not.toThrow();
|
||||
expect(makeTag().title).toBe(testDictionary.normalize(NEW_TITLE));
|
||||
});
|
||||
|
||||
it('adds synonyms', () => {
|
||||
const tagDef = {
|
||||
mustHaveValue: true,
|
||||
synonyms: ['bar']
|
||||
};
|
||||
|
||||
testDictionary.defineTag('foo', tagDef);
|
||||
|
||||
expect(testDictionary.normalize('bar')).toBe('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookUp', () => {
|
||||
describe('defineTags', () => {
|
||||
it('behaves the same as adding tags individually', () => {
|
||||
const dict1 = new Dictionary();
|
||||
const dict2 = new Dictionary();
|
||||
const fakeTag = {
|
||||
mustHaveValue: true,
|
||||
synonym: 'phony'
|
||||
};
|
||||
|
||||
dict1.defineTag('fake', fakeTag);
|
||||
dict2.defineTags({ fake: fakeTag });
|
||||
|
||||
expect(dict2.lookup('fake')).toEqual(dict1.lookup('fake'));
|
||||
expect(dict2.lookup('phony')).toEqual(dict1.lookup('phony'));
|
||||
});
|
||||
|
||||
it('returns the tags it added', () => {
|
||||
const tags = {
|
||||
tag1: {
|
||||
tag1Attribute: 'foo'
|
||||
},
|
||||
tag2: {
|
||||
tag2Attribute: 'bar'
|
||||
}
|
||||
};
|
||||
const actualTags = testDictionary.defineTags(tags);
|
||||
|
||||
for (const tag of Object.keys(tags)) {
|
||||
expect(actualTags[tag]).toBeObject();
|
||||
for (const attrib of Object.keys(tags[tag])) {
|
||||
expect(tags[tag][attrib]).toBe(actualTags[tag][attrib]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromConfig', () => {
|
||||
const CLOSURE_TAGNAME = 'final';
|
||||
const dictionaryConfig = env.conf.tags.dictionaries.slice();
|
||||
const JSDOC_TAGNAME = 'abstract';
|
||||
|
||||
beforeEach(() => {
|
||||
env.conf.tags.dictionaries = [];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
env.conf.tags.dictionaries = dictionaryConfig.slice();
|
||||
});
|
||||
|
||||
it('logs an error if `env.conf.tags.dictionaries` is undefined', () => {
|
||||
function defineTags() {
|
||||
env.conf.tags.dictionaries = undefined;
|
||||
Dictionary.fromConfig(env);
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(defineTags, 'error')).toBeTrue();
|
||||
});
|
||||
|
||||
it('logs an error if an unknown dictionary is requested', () => {
|
||||
function defineTags() {
|
||||
env.conf.tags.dictionaries = ['jsmarmoset'];
|
||||
Dictionary.fromConfig(env);
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(defineTags, 'error')).toBeTrue();
|
||||
});
|
||||
|
||||
it('adds both JSDoc and Closure tags by default', () => {
|
||||
env.conf.tags.dictionaries = dictionaryConfig.slice();
|
||||
testDictionary = Dictionary.fromConfig(env);
|
||||
|
||||
expect(testDictionary.lookup(JSDOC_TAGNAME)).toBeObject();
|
||||
expect(testDictionary.lookup(CLOSURE_TAGNAME)).toBeObject();
|
||||
});
|
||||
|
||||
it('adds only the JSDoc tags if requested', () => {
|
||||
env.conf.tags.dictionaries = ['jsdoc'];
|
||||
testDictionary = Dictionary.fromConfig(env);
|
||||
|
||||
expect(testDictionary.lookup(JSDOC_TAGNAME)).toBeObject();
|
||||
expect(testDictionary.lookup(CLOSURE_TAGNAME)).toBeFalse();
|
||||
});
|
||||
|
||||
it('adds only the Closure tags if requested', () => {
|
||||
env.conf.tags.dictionaries = ['closure'];
|
||||
testDictionary = Dictionary.fromConfig(env);
|
||||
|
||||
expect(testDictionary.lookup(JSDOC_TAGNAME)).toBeFalse();
|
||||
expect(testDictionary.lookup(CLOSURE_TAGNAME)).toBeObject();
|
||||
});
|
||||
|
||||
it('prefers tagdefs from the first dictionary on the list', () => {
|
||||
env.conf.tags.dictionaries = ['closure', 'jsdoc'];
|
||||
testDictionary = Dictionary.fromConfig(env);
|
||||
|
||||
expect(testDictionary.lookup('deprecated').synonyms).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('adds tag synonyms', () => {
|
||||
env.conf.tags.dictionaries = ['jsdoc'];
|
||||
testDictionary = Dictionary.fromConfig(env);
|
||||
|
||||
expect(testDictionary.lookup('extends')).toBeObject();
|
||||
expect(testDictionary.normalize('extends')).toBe('augments');
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookup', () => {
|
||||
it("retrieves the definition using the tag's canonical name", () => {
|
||||
expect(testDictionary.lookUp(TAG_TITLE)).toBe(TAG_DEF);
|
||||
expect(testDictionary.lookup(TAG_TITLE)).toBe(TAG_DEF);
|
||||
});
|
||||
|
||||
it('retrieves the definition using a synonym for the tag', () => {
|
||||
expect(testDictionary.lookUp(TAG_SYNONYM)).toBe(TAG_DEF);
|
||||
expect(testDictionary.lookup(TAG_SYNONYM)).toBe(TAG_DEF);
|
||||
});
|
||||
|
||||
it('returns `false` when a tag is not found', () => {
|
||||
expect(testDictionary.lookUp('lkjas1l24jk')).toBeFalse();
|
||||
expect(testDictionary.lookup('lkjas1l24jk')).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookUp', () => {
|
||||
it('calls `lookup`', () => {
|
||||
const lookupSpy = spyOn(testDictionary, 'lookup');
|
||||
|
||||
testDictionary.lookUp('foo');
|
||||
|
||||
expect(lookupSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -112,22 +255,28 @@ describe('jsdoc/tag/dictionary', () => {
|
||||
});
|
||||
|
||||
describe('normalise', () => {
|
||||
it("should return the tag's title if it is not a synonym", () => {
|
||||
expect(testDictionary.normalise('FooBar')).toBe('foobar');
|
||||
expect(testDictionary.normalise(TAG_TITLE)).toBe(TAG_DEF.title);
|
||||
});
|
||||
it('calls `normalize`', () => {
|
||||
const normalizeSpy = spyOn(testDictionary, 'normalize');
|
||||
|
||||
it('should return the canonical name of a tag if the synonym is normalized', () => {
|
||||
expect(testDictionary.normalise(TAG_SYNONYM)).toBe(TAG_DEF.title);
|
||||
testDictionary.normalise('foo');
|
||||
|
||||
expect(normalizeSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalize', () => {
|
||||
// covered by tests for `normalise`
|
||||
it('returns the title if it is not a synonym', () => {
|
||||
expect(testDictionary.normalize('FooBar')).toBe('foobar');
|
||||
expect(testDictionary.normalize(TAG_TITLE)).toBe(TAG_DEF.title);
|
||||
});
|
||||
|
||||
it('returns the canonical name if the synonym is normalized', () => {
|
||||
expect(testDictionary.normalize(TAG_SYNONYM)).toBe(TAG_DEF.title);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dictionary', () => {
|
||||
it('should be a constructor', () => {
|
||||
it('is a constructor', () => {
|
||||
function newDictionary() {
|
||||
return new dictionary.Dictionary();
|
||||
}
|
||||
|
||||
@ -1,124 +1,35 @@
|
||||
describe('jsdoc/tag/dictionary/definitions', () => {
|
||||
const env = require('jsdoc/env');
|
||||
const definitions = require('jsdoc/tag/dictionary/definitions');
|
||||
const Dictionary = require('jsdoc/tag/dictionary').Dictionary;
|
||||
|
||||
it('should exist', () => {
|
||||
expect(definitions).toBeObject();
|
||||
});
|
||||
|
||||
it('should export a baseTags object', () => {
|
||||
it('has a baseTags object', () => {
|
||||
expect(definitions.baseTags).toBeObject();
|
||||
});
|
||||
|
||||
it('should export a closureTags object', () => {
|
||||
it('has a closureTags object', () => {
|
||||
expect(definitions.closureTags).toBeObject();
|
||||
});
|
||||
|
||||
it('should export a defineTags method', () => {
|
||||
expect(definitions.defineTags).toBeFunction();
|
||||
it('has an internalTags object', () => {
|
||||
expect(definitions.internalTags).toBeObject();
|
||||
});
|
||||
|
||||
it('should export a jsdocTags object', () => {
|
||||
it('has a jsdocTags object', () => {
|
||||
expect(definitions.jsdocTags).toBeObject();
|
||||
});
|
||||
|
||||
describe('baseTags', () => {
|
||||
// nothing to test except which tags are on the list, which would duplicate the code
|
||||
// Nothing to test except which tags are on the list, which would duplicate the code.
|
||||
});
|
||||
|
||||
describe('closureTags', () => {
|
||||
// nothing to test except which tags are on the list, which would duplicate the code
|
||||
// Nothing to test except which tags are on the list, which would duplicate the code.
|
||||
});
|
||||
|
||||
describe('defineTags', () => {
|
||||
const CLOSURE_TAGNAME = 'final';
|
||||
const dictionaryConfig = env.conf.tags.dictionaries.slice(0);
|
||||
const JSDOC_TAGNAME = 'abstract';
|
||||
let tagDict;
|
||||
|
||||
beforeEach(() => {
|
||||
env.conf.tags.dictionaries = [];
|
||||
tagDict = new Dictionary();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
env.conf.tags.dictionaries = dictionaryConfig.slice(0);
|
||||
});
|
||||
|
||||
it('should log an error if `env.conf.tags.dictionaries` is undefined', () => {
|
||||
function defineTags() {
|
||||
env.conf.tags.dictionaries = undefined;
|
||||
definitions.defineTags(tagDict);
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(defineTags, 'error')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should log an error if an unknown dictionary is requested', () => {
|
||||
function defineTags() {
|
||||
env.conf.tags.dictionaries = ['jsmarmoset'];
|
||||
definitions.defineTags(tagDict);
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(defineTags, 'error')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should add both JSDoc and Closure tags by default', () => {
|
||||
env.conf.tags.dictionaries = dictionaryConfig.slice(0);
|
||||
definitions.defineTags(tagDict);
|
||||
|
||||
expect(tagDict.lookUp(JSDOC_TAGNAME)).toBeObject();
|
||||
expect(tagDict.lookUp(CLOSURE_TAGNAME)).toBeObject();
|
||||
});
|
||||
|
||||
it('should add only the JSDoc tags if requested', () => {
|
||||
env.conf.tags.dictionaries = ['jsdoc'];
|
||||
definitions.defineTags(tagDict);
|
||||
|
||||
expect(tagDict.lookUp(JSDOC_TAGNAME)).toBeObject();
|
||||
expect(tagDict.lookUp(CLOSURE_TAGNAME)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should add only the Closure tags if requested', () => {
|
||||
env.conf.tags.dictionaries = ['closure'];
|
||||
definitions.defineTags(tagDict);
|
||||
|
||||
expect(tagDict.lookUp(JSDOC_TAGNAME)).toBeFalse();
|
||||
expect(tagDict.lookUp(CLOSURE_TAGNAME)).toBeObject();
|
||||
});
|
||||
|
||||
it('should prefer tagdefs from the first dictionary on the list', () => {
|
||||
env.conf.tags.dictionaries = ['closure', 'jsdoc'];
|
||||
definitions.defineTags(tagDict);
|
||||
|
||||
expect(tagDict.lookUp('deprecated').synonyms).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should add tag synonyms', () => {
|
||||
env.conf.tags.dictionaries = ['jsdoc'];
|
||||
definitions.defineTags(tagDict);
|
||||
|
||||
expect(tagDict.lookUp('extends')).toBeObject();
|
||||
expect(tagDict.normalize('extends')).toBe('augments');
|
||||
});
|
||||
|
||||
it('should ignore the config settings if tagdefs are passed in', () => {
|
||||
const tagDefs = {
|
||||
foo: {
|
||||
mustHaveValue: false
|
||||
}
|
||||
};
|
||||
|
||||
env.conf.tags.dictionaries = ['jsdoc'];
|
||||
definitions.defineTags(tagDict, tagDefs);
|
||||
|
||||
expect(tagDict.lookUp('foo')).toBeObject();
|
||||
expect(tagDict.lookUp('abstract')).toBeFalse();
|
||||
});
|
||||
describe('internalTags', () => {
|
||||
// Nothing to test except which tags are on the list, which would duplicate the code.
|
||||
});
|
||||
|
||||
describe('jsdocTags', () => {
|
||||
// nothing to test except which tags are on the list, which would duplicate the code
|
||||
// Nothing to test except which tags are on the list, which would duplicate the code.
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,7 +3,6 @@ const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
describe("jsdoc/util/templateHelper", () => {
|
||||
const _ = require('lodash');
|
||||
const definitions = require('jsdoc/tag/dictionary/definitions');
|
||||
const dictionary = require('jsdoc/tag/dictionary');
|
||||
const doclet = require('jsdoc/doclet');
|
||||
const env = require('jsdoc/env');
|
||||
@ -201,7 +200,6 @@ describe("jsdoc/util/templateHelper", () => {
|
||||
dict.defineTag('anaphylaxis', {
|
||||
isNamespace: true
|
||||
});
|
||||
definitions.defineTags(dict);
|
||||
doclet._replaceDictionary(dict);
|
||||
|
||||
filename = helper.getUniqueFilename('anaphylaxis:peanut');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user