docs(jsdoc-doclet): improve JSDoc comments

This commit is contained in:
Jeff Williams 2025-07-05 09:37:16 -07:00
parent 3ca5db9db5
commit 4c4a58260d
No known key found for this signature in database
5 changed files with 206 additions and 62 deletions

View File

@ -16,6 +16,9 @@
/**
* Provides methods for augmenting the parse results based on their content.
*
* @namespace augment
* @memberof module:@jsdoc/doclet
*/
import { fromParts, SCOPE, toParts } from '@jsdoc/name';
@ -133,7 +136,7 @@ function staticToInstance(doclet) {
}
/**
* Update the list of doclets to be added to another symbol.
* Updates the list of doclets to be added to another symbol.
*
* We add only one doclet per longname. For example: If `ClassA` inherits from two classes that both
* use the same method name, `ClassA` gets docs for one method rather than two.
@ -472,19 +475,20 @@ function augment(docletStore, propertyName, docletFinder, env) {
}
/**
* Add doclets to reflect class inheritance.
* Adds doclets to reflect class inheritance.
*
* For example, if `ClassA` has the instance method `myMethod`, and `ClassB` inherits from `ClassA`,
* calling this method creates a new doclet for `ClassB#myMethod`.
*
* @return {void}
* @alias module:@jsdoc/doclet.augment.addInherited
* @param {!module:@jsdoc/doclet.DocletStore} docletStore - The doclet store to update.
*/
export function addInherited(docletStore) {
augment(docletStore, 'augments', getInheritedAdditions);
}
/**
* Add doclets to reflect mixins. When a symbol is mixed into a class, the class' version of the
* Adds doclets to reflect mixins. When a symbol is mixed into a class, the class' version of the
* mixed-in symbol is treated as an instance member.
*
* For example:
@ -494,16 +498,15 @@ export function addInherited(docletStore) {
* + If `MixinA` has the static method `myMethod`, and `ClassA` mixes `MixinA`, calling this method
* creates a new doclet for the instance method `ClassA#myMethod`.
*
* @param {!Array.<module:@jsdoc/doclet.Doclet>} doclets - The doclets generated by JSDoc.
* @param {!Object} doclets.index - The doclet index.
* @return {void}
* @alias module:@jsdoc/doclet.augment.addMixedIn
* @param {!module:@jsdoc/doclet.DocletStore} docletStore - The doclet store to update.
*/
export function addMixedIn(doclets) {
augment(doclets, 'mixes', getMixedInAdditions);
export function addMixedIn(docletStore) {
augment(docletStore, 'mixes', getMixedInAdditions);
}
/**
* Add and update doclets to reflect implementations of interfaces.
* Adds and updates doclets to reflect implementations of interfaces.
*
* For example, if `InterfaceA` has the instance method `myMethod`, and `ClassA` implements
* `InterfaceA`, calling this method does the following:
@ -515,14 +518,15 @@ export function addMixedIn(doclets) {
* If `ClassA#myMethod` used the `@override` or `@inheritdoc` tag, calling this method would also
* generate a new doclet that reflects the interface's documentation for `InterfaceA#myMethod`.
*
* @return {void}
* @alias module:@jsdoc/doclet.augment.addImplemented
* @param {!module:@jsdoc/doclet.DocletStore} docletStore - The doclet store to update.
*/
export function addImplemented(doclets) {
augment(doclets, 'implements', getImplementedAdditions);
export function addImplemented(docletStore) {
augment(docletStore, 'implements', getImplementedAdditions);
}
/**
* Add and update doclets to reflect all of the following:
* Adds and updates doclets to reflect all of the following:
*
* + Inherited classes
* + Mixins
@ -530,7 +534,8 @@ export function addImplemented(doclets) {
*
* Calling this method is equivalent to calling all other methods exported by this module.
*
* @return {void}
* @alias module:@jsdoc/doclet.augment.augmentAll
* @param {!module:@jsdoc/doclet.DocletStore} docletStore - The doclet store to update.
*/
export function augmentAll(docletStore) {
addMixedIn(docletStore);

View File

@ -55,10 +55,15 @@ function cloneBorrowedDoclets({ borrowed, longname }, docletStore) {
}
/**
Take a copy of the docs for borrowed symbols and attach them to the
docs for the borrowing symbol. This process changes the symbols involved,
moving docs from the "borrowed" array and into the general docs, then
deleting the "borrowed" array.
* Creates doclets for borrowed symbols, and adds them to the doclet store.
*
* The `name`, `memberof`, and `longname` properties for the new doclets are rewritten to show that
* they belong to the borrowing symbol.
*
* This method also removes the `borrowed` property from each borrowed doclet.
*
* @alias module:@jsdoc/doclet.resolveBorrows
* @param {!module:@jsdoc/doclet.DocletStore} docletStore - The doclet store to update.
*/
export function resolveBorrows(docletStore) {
for (const doclet of docletStore.docletsWithBorrowed) {

View File

@ -56,17 +56,25 @@ function removeFromSet(targetMap, key, value) {
/**
* Stores and classifies the doclets that JSDoc creates as it parses your source files.
*
* The doclet store categorizes doclets based on their properties, so that the JSDoc template can
* A doclet store categorizes doclets based on their properties, so that the JSDoc template can
* efficiently retrieve the doclets that it needs. For example, when the template generates
* documentation for a class, it can retrieve all of the doclets that represent members of that
* class.
*
* To retrieve the doclets that you need, use the doclet store's instance properties. For example,
* {@link module:@jsdoc/doclet.DocletStore#docletsByLongname} maps longnames to the doclets with
* that longname.
*
* After you add a doclet to the store, the store automatically tracks changes to a doclet's
* properties and recategorizes the doclet as needed. For example, if a doclet's `kind` property
* changes from `class` to `interface`, then the doclet store automatically recategorizes the doclet
* as an interface.
*
* @alias @jsdoc/doclet.DocletStore
* Doclets can be _visible_, meaning that they should be used to generate output, or _hidden_,
* meaning that they're ignored when generating output. Except as noted, the doclet store exposes
* only visible doclets.
*
* @alias module:@jsdoc/doclet.DocletStore
*/
export class DocletStore {
#commonPathPrefix;
@ -91,9 +99,9 @@ export class DocletStore {
*
* When you create a doclet store, you provide a JSDoc environment object. The doclet store
* listens for new doclets that are created in that environment. When a new doclet is created, the
* doclet store tracks it automatically.
* doclet store adds it automatically and tracks updates to the doclet.
*
* @param {@jsdoc/core.Env} env - The JSDoc environment to use.
* @param {module:@jsdoc/core.Env} env - The JSDoc environment to use.
*/
constructor(env) {
this.#commonPathPrefix = null;
@ -101,28 +109,85 @@ export class DocletStore {
this.#isListening = false;
this.#sourcePaths = new Map();
// TODO: Add descriptions and types for public properties.
/** @type Map<string, Set<Doclet>> */
/**
* Map of all doclet longnames to a `Set` of all doclets with that longname. Includes both
* visible and hidden doclets.
*
* @type Map<string, Set<module:@jsdoc/doclet.Doclet>>
*/
this.allDocletsByLongname = new Map();
/** Doclets that are used to generate output. */
/**
* All visible doclets.
*
* @type Set<module:@jsdoc/doclet.Doclet>
*/
this.doclets = new Set();
/** @type Map<string, Set<Doclet>> */
/**
* Map from a doclet kind to a `Set` of doclets with that kind.
*
* @type Map<string, Set<module:@jsdoc/doclet.Doclet>>
*/
this.docletsByKind = new Map();
/** @type Map<string, Set<Doclet>> */
/**
* Map from a doclet longname to a `Set` of doclets with that longname.
*
* @type Map<string, Set<module:@jsdoc/doclet.Doclet>>
*/
this.docletsByLongname = new Map();
/** @type Map<string, Set<Doclet>> */
/**
* Map from a doclet `memberof` value to a `Set` of doclets with that `memberof`.
*
* @type Map<string, Set<module:@jsdoc/doclet.Doclet>>
*/
this.docletsByMemberof = new Map();
/** @type Map<string, Set<Doclet>> */
/**
* Map from an AST node ID, generated during parsing, to a `Set` of doclets for that node ID.
*
* @type Map<string, Set<module:@jsdoc/doclet.Doclet>>
*/
this.docletsByNodeId = new Map();
/**
* Doclets that have an `augments` property.
*
* @type Set<module:@jsdoc/doclet.Doclet>
*/
this.docletsWithAugments = new Set();
/**
* Doclets that have a `borrowed` property.
*
* @type Set<module:@jsdoc/doclet.Doclet>
*/
this.docletsWithBorrowed = new Set();
/**
* Doclets that have an `implements` property.
*
* @type Set<module:@jsdoc/doclet.Doclet>
*/
this.docletsWithImplements = new Set();
/**
* Doclets that have a `mixes` property.
*
* @type Set<module:@jsdoc/doclet.Doclet>
*/
this.docletsWithMixes = new Set();
/**
* Doclets that belong to the global scope.
*
* @type Set<module:@jsdoc/doclet.Doclet>
*/
this.globals = new Set();
/** @type Map<string, Set<Doclet>> */
/**
* Map from an event's longname to a `Set` of doclets that listen to that event.
*
* @type Map<string, Set<module:@jsdoc/doclet.Doclet>>
*/
this.listenersByListensTo = new Map();
/** Doclets that aren't used to generate output. */
/**
* Doclets that are hidden and shouldn't be used to generate output.
*
* @type Set<module:@jsdoc/doclet.Doclet>
*/
this.unusedDoclets = new Set();
this.#docletChangedHandler = (e) => this.#handleDocletChanged(e, {});
@ -318,7 +383,15 @@ export class DocletStore {
// `undocumented` only affects visibility, which is handled above, so we ignore it here.
}
// Adds a doclet to the store directly, rather than by listening to events.
/**
* Adds a doclet to the store directly, rather than by listening to events from the JSDoc
* environment.
*
* Use this method if you need to track a doclet that's generated outside of JSDoc's parsing
* process.
*
* @param {module:@jsdoc/doclet.Doclet} doclet - The doclet to add.
*/
add(doclet) {
let doclets;
let nodeId;
@ -337,10 +410,25 @@ export class DocletStore {
}
}
/**
* All known doclets, including both visible and hidden doclets.
*
* @type Set<module:@jsdoc/doclet.Doclet>
*/
get allDoclets() {
return new Set([...this.doclets, ...this.unusedDoclets]);
}
/**
* The longest filepath prefix that's shared by the source files that were parsed.
*
* + If there's only one source file, then the prefix is the source file's directory name.
* + If the source files don't have a common prefix, then the prefix is an empty string.
*
* If a doclet is hidden, then its source filepath is ignored when determining the prefix.
*
* @type {string}
*/
get commonPathPrefix() {
let commonPrefix;
let sourcePaths;
@ -365,14 +453,30 @@ export class DocletStore {
return commonPrefix ?? '';
}
/**
* The longnames of all visible doclets.
*
* @type Array<string>
*/
get longnames() {
return Array.from(this.docletsByLongname.keys());
}
/**
* The source paths associated with all visible doclets.
*
* @type Array<string>
*/
get sourcePaths() {
return Array.from(this.#sourcePaths.values());
}
/**
* Start listening to events from the JSDoc environment.
*
* In general, you don't need to call this method. A `DocletStore` always listens for events by
* default.
*/
startListening() {
if (!this.#isListening) {
this.#emitter.on('docletChanged', this.#docletChangedHandler);
@ -382,6 +486,12 @@ export class DocletStore {
}
}
/**
* Stop listening to events from the JSDoc environment.
*
* Call this method if you're done using a `DocletStore`, and you don't want it to listen to
* future events.
*/
stopListening() {
if (this.#isListening) {
this.#emitter.removeListener('docletChanged', this.#docletChangedHandler);

View File

@ -410,8 +410,9 @@ function copyPropsWithIncludelist(primary, secondary, target, include) {
}
/**
* Combine two doclets into a new doclet.
* Combines two doclets into a new doclet.
*
* @alias module:@jsdoc/doclet.combineDoclets
* @param {module:@jsdoc/doclet.Doclet} primary - The doclet whose properties will be used.
* @param {module:@jsdoc/doclet.Doclet} secondary - The doclet to use as a fallback for properties
* that the primary doclet does not have.
@ -433,7 +434,7 @@ export function combineDoclets(primary, secondary) {
}
/**
* Represents a single JSDoc comment.
* Information about a single JSDoc comment, or a single symbol in a source file.
*
* @alias module:@jsdoc/doclet.Doclet
*/
@ -441,7 +442,7 @@ Doclet = class {
#dictionary;
/**
* Create a doclet.
* Creates a doclet.
*
* @param {string} docletSrc - The raw source code of the jsdoc comment.
* @param {object} meta - Properties describing the code related to this comment.
@ -468,7 +469,11 @@ Doclet = class {
});
WATCHABLE_PROPS.forEach((prop) => this.#defineWatchableProp(prop));
/** The original text of the comment from the source code. */
/**
* The text of the comment from the source code.
*
* @type {string}
*/
this.comment = docletSrc;
meta ??= {};
this.setMeta(meta);
@ -492,10 +497,22 @@ Doclet = class {
}
}
/**
* Creates a copy of an existing doclet.
*
* @param {module:@jsdoc/doclet.Doclet} doclet - The doclet to copy.
* @returns {module:@jsdoc/doclet.Doclet} A copy of the doclet.
*/
static clone(doclet) {
return combineDoclets(doclet, Doclet.emptyDoclet(doclet.env));
}
/**
* Creates an empty doclet.
*
* @param {module:@jsdoc/core.Env} env - The JSDoc environment to use.
* @returns {module:@jsdoc/doclet.Doclet} An empty doclet.
*/
static emptyDoclet(env) {
return new Doclet('', {}, env);
}
@ -534,7 +551,7 @@ Doclet = class {
}
/**
* Add a tag to the doclet.
* Adds a tag to the doclet.
*
* @param {string} title - The title of the tag being added.
* @param {string} [text] - The text of the tag being added.
@ -554,7 +571,7 @@ Doclet = class {
}
/**
* Check whether the doclet represents a globally available symbol.
* Checks whether the doclet represents a globally available symbol.
*
* @returns {boolean} `true` if the doclet represents a global; `false` otherwise.
*/
@ -563,7 +580,7 @@ Doclet = class {
}
/**
* Check whether the doclet should be used to generate output.
* Checks whether the doclet should be used to generate output.
*
* @returns {boolean} `true` if the doclet should be used to generate output; `false` otherwise.
*/
@ -647,20 +664,21 @@ Doclet = class {
}
/**
* Set the doclet's `longname` property.
* Sets the doclet's `longname` property.
*
* @param {string} longname - The longname for the doclet.
*/
setLongname(longname) {
/**
* The fully resolved symbol name.
* @type {string}
*/
longname = removeGlobal(longname);
if (this.#dictionary.isNamespace(this.kind)) {
longname = applyNamespace(longname, this.kind);
}
/**
* The fully resolved symbol name.
*
* @type {string}
*/
this.longname = longname;
}
@ -672,6 +690,7 @@ Doclet = class {
setMemberof(sid) {
/**
* The longname of the symbol that contains this one, if any.
*
* @type {string}
*/
this.memberof = removeGlobal(sid)
@ -680,7 +699,7 @@ Doclet = class {
}
/**
* Set the doclet's `scope` property. Must correspond to a scope name that is defined in
* Sets the doclet's `scope` property. Must correspond to a scope name that is defined in
* {@link module:@jsdoc/name.SCOPE.NAMES}.
*
* @param {string} scope - The scope for the doclet relative to the symbol's parent.
@ -707,10 +726,10 @@ Doclet = class {
}
/**
* Add a symbol to this doclet's `borrowed` array.
* Adds a symbol to the doclet's `borrowed` array.
*
* @param {string} source - The longname of the symbol that is the source.
* @param {string} target - The name the symbol is being assigned to.
* @param {string} source - The longname of the symbol that is borrowed.
* @param {string} target - The name that the borrowed symbol is assigned to.
*/
borrow(source, target) {
const about = { from: source };
@ -728,6 +747,11 @@ Doclet = class {
this.borrowed.push(about);
}
/**
* Adds a symbol to the doclet's `mixes` array.
*
* @param {string} source - The longname of the symbol that is mixed in.
*/
mix(source) {
/**
* A list of symbols that are mixed into this one, if any.
@ -739,7 +763,7 @@ Doclet = class {
}
/**
* Add a symbol to the doclet's `augments` array.
* Adds a symbol to the doclet's `augments` array.
*
* @param {string} base - The longname of the base symbol.
*/
@ -753,10 +777,12 @@ Doclet = class {
this.augments.push(base);
}
// TODO: Add typedef for `meta`.
/**
* Set the `meta` property of this doclet.
* Sets the `meta` property of the doclet, which contains metadata about the source code that the
* doclet corresponds to.
*
* @param {object} meta
* @param {object} meta - The data to add to the doclet.
*/
setMeta(meta) {
let pathname;

View File

@ -16,12 +16,6 @@
import stripBom from 'strip-bom';
/**
* Provides access to information about a JavaScript package.
*
* @see https://www.npmjs.org/doc/files/package.json.html
*/
// Collect all of the license information from a `package.json` file.
function getLicenses(packageInfo) {
const licenses = packageInfo.licenses ? packageInfo.licenses.slice() : [];
@ -75,9 +69,13 @@ function getLicenses(packageInfo) {
* **Note**: JSDoc does not validate or normalize the contents of `package.json` files. If your
* `package.json` file does not follow the npm specification, some properties of the `Package`
* object may not use the format documented here.
*
* @alias module:@jsdoc/doclet.Package
*/
export class Package {
/**
* Creates an object that represents a package.
*
* @param {string} json - The contents of the `package.json` file.
* @param {Object} env - The JSDoc environment.
*/
@ -142,7 +140,7 @@ export class Package {
/**
* The contributors to this package.
*
* @type {Array.<(module:@jsdoc/doclet.Package~PersonInfo|string)>}
* @type {Array<(module:@jsdoc/doclet.Package~PersonInfo|string)>}
*/
this.contributors = packageInfo.contributors;
}
@ -195,7 +193,7 @@ export class Package {
* After JSDoc parses your input files, it sets this property to a list of paths to your input
* files.
*
* @type {Array.<string>}
* @type {Array<string>}
*/
this.files = [];
@ -212,7 +210,7 @@ export class Package {
/**
* Keywords to help users find the package.
*
* @type {Array.<string>}
* @type {Array<string>}
*/
this.keywords = packageInfo.keywords;
}
@ -222,7 +220,7 @@ export class Package {
* The licenses used by this package. Combines information from the `package.json` file's
* `license` property and the deprecated `licenses` property.
*
* @type {Array.<module:@jsdoc/doclet.Package~LicenseInfo>}
* @type {Array<module:@jsdoc/doclet.Package~LicenseInfo>}
*/
this.licenses = getLicenses(packageInfo);
}