mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
feat(jsdoc-parse): add the source file's module type, if known, to the doclet meta info
This commit is contained in:
parent
e8bbe5b694
commit
5a6998bce7
@ -28,7 +28,7 @@ let uid = 100000000;
|
||||
|
||||
export const MODULE_TYPES = {
|
||||
AMD: 'amd',
|
||||
COMMON_JS: 'commonJs',
|
||||
COMMON_JS: 'commonjs',
|
||||
ES6: 'es6',
|
||||
};
|
||||
|
||||
|
||||
@ -642,6 +642,7 @@ walkers[Syntax.YieldExpression] = (node, parent, state, cb) => {
|
||||
function handleNode(node, parent, cbState) {
|
||||
let currentScope;
|
||||
const isScope = astNode.isScope(node);
|
||||
let moduleType;
|
||||
const { walker } = cbState;
|
||||
|
||||
astNode.addNodeProperties(node);
|
||||
@ -657,7 +658,12 @@ function handleNode(node, parent, cbState) {
|
||||
}
|
||||
cbState.nodes.push(node);
|
||||
|
||||
cbState.moduleType ??= astNode.detectModuleType(node);
|
||||
if (!cbState.moduleType) {
|
||||
moduleType = cbState.moduleType = astNode.detectModuleType(node);
|
||||
if (moduleType) {
|
||||
cbState.moduleTypes.set(cbState.filename, moduleType);
|
||||
}
|
||||
}
|
||||
|
||||
if (!walker._walkers[node.type]) {
|
||||
walker._logUnknownNodeType(node);
|
||||
@ -675,8 +681,9 @@ function handleNode(node, parent, cbState) {
|
||||
*/
|
||||
export class Walker {
|
||||
// TODO: docs
|
||||
constructor(env, walkerFuncs = walkers) {
|
||||
constructor(env, { moduleTypes }, walkerFuncs = walkers) {
|
||||
this._log = env.log;
|
||||
this._moduleTypes = moduleTypes;
|
||||
this._walkers = walkerFuncs;
|
||||
}
|
||||
|
||||
@ -692,6 +699,7 @@ export class Walker {
|
||||
const state = {
|
||||
filename: filename,
|
||||
moduleType: null,
|
||||
moduleTypes: this._moduleTypes,
|
||||
nodes: [],
|
||||
scopes: [],
|
||||
walker: this,
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import EventEmitter from 'node:events';
|
||||
import fs from 'node:fs';
|
||||
|
||||
@ -75,8 +76,9 @@ export class Parser extends EventEmitter {
|
||||
this._docletStore = new DocletStore(dependencies);
|
||||
this._emitter = dependencies.get('emitter');
|
||||
this._log = dependencies.get('log');
|
||||
this.moduleTypes = new Map();
|
||||
this._visitor = new Visitor();
|
||||
this._walker = new Walker(dependencies);
|
||||
this._walker = new Walker(dependencies, this);
|
||||
|
||||
this._visitor.setParser(this);
|
||||
|
||||
@ -242,7 +244,9 @@ export class Parser extends EventEmitter {
|
||||
|
||||
/** @private */
|
||||
_walkAst(ast, visitor, sourceName) {
|
||||
this._walker.recurse(ast, visitor, sourceName);
|
||||
const { filename, moduleType } = this._walker.recurse(ast, visitor, sourceName);
|
||||
|
||||
this.moduleTypes.set(filename, moduleType);
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import path from 'node:path';
|
||||
|
||||
import { astNode, Syntax } from '@jsdoc/ast';
|
||||
import { name } from '@jsdoc/core';
|
||||
import { combineDoclets } from '@jsdoc/doclet';
|
||||
@ -53,9 +56,8 @@ function isValidJsdoc(commentSrc) {
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
function getLeadingJsdocComment(node) {
|
||||
function getLeadingJsdocComment({ leadingComments }) {
|
||||
let comment = null;
|
||||
let leadingComments = node.leadingComments;
|
||||
|
||||
if (Array.isArray(leadingComments) && leadingComments.length) {
|
||||
// the attached comments may include line comments, which we don't want
|
||||
@ -106,6 +108,7 @@ function getParentDocletFromEvent(parser, { doclet }) {
|
||||
function makeInlineParamsFinisher(parser) {
|
||||
return (e) => {
|
||||
let documentedParams;
|
||||
const { doclet: eventDoclet } = e;
|
||||
let knownParams;
|
||||
let param;
|
||||
let parentDoclet;
|
||||
@ -119,7 +122,7 @@ function makeInlineParamsFinisher(parser) {
|
||||
|
||||
// we only want to use the doclet if it's param-specific (but not, for example, if it's
|
||||
// a param tagged with `@exports` in an AMD module)
|
||||
if (e.doclet.kind !== 'param') {
|
||||
if (eventDoclet.kind !== 'param') {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -131,22 +134,22 @@ function makeInlineParamsFinisher(parser) {
|
||||
param = documentedParams[i];
|
||||
|
||||
// is the param already documented? if so, we don't need to use the doclet
|
||||
if (param?.name === e.doclet.name) {
|
||||
e.doclet.undocumented = true;
|
||||
if (param?.name === eventDoclet.name) {
|
||||
eventDoclet.undocumented = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// if we ran out of documented params, or we're at the parameter's actual position,
|
||||
// splice in the param at the current index
|
||||
if (!param || i === knownParams.indexOf(e.doclet.name)) {
|
||||
if (!param || i === knownParams.indexOf(eventDoclet.name)) {
|
||||
documentedParams.splice(i, 0, {
|
||||
type: e.doclet.type || {},
|
||||
type: eventDoclet.type ?? {},
|
||||
description: '',
|
||||
name: e.doclet.name,
|
||||
name: eventDoclet.name,
|
||||
});
|
||||
|
||||
// the doclet is no longer needed
|
||||
e.doclet.undocumented = true;
|
||||
eventDoclet.undocumented = true;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -190,8 +193,7 @@ function findRestParam(params) {
|
||||
* the parameter is repeatable.
|
||||
*/
|
||||
function makeRestParamFinisher() {
|
||||
return (e) => {
|
||||
const doclet = e.doclet;
|
||||
return ({ code, doclet }) => {
|
||||
let documentedParams;
|
||||
let restNode;
|
||||
|
||||
@ -199,9 +201,9 @@ function makeRestParamFinisher() {
|
||||
return;
|
||||
}
|
||||
|
||||
documentedParams = doclet.params = doclet.params || [];
|
||||
documentedParams = doclet.params ??= [];
|
||||
restNode = findRestParam(
|
||||
e.code.node.params || e.code.node.value?.params || e.code.node.init?.params || []
|
||||
code.node.params || code.node.value?.params || code.node.init?.params || []
|
||||
);
|
||||
|
||||
if (restNode) {
|
||||
@ -252,9 +254,8 @@ function findDefaultParams(params) {
|
||||
* parameters.
|
||||
*/
|
||||
function makeDefaultParamFinisher() {
|
||||
return (e) => {
|
||||
return ({ code, doclet }) => {
|
||||
let defaultValues;
|
||||
const doclet = e.doclet;
|
||||
let documentedParams;
|
||||
let paramName;
|
||||
let params;
|
||||
@ -263,8 +264,8 @@ function makeDefaultParamFinisher() {
|
||||
return;
|
||||
}
|
||||
|
||||
documentedParams = doclet.params = doclet.params || [];
|
||||
params = e.code.node.params || e.code.node.value?.params || [];
|
||||
documentedParams = doclet.params ??= [];
|
||||
params = code.node.params ?? code.node.value?.params ?? [];
|
||||
defaultValues = findDefaultParams(params);
|
||||
|
||||
for (let i = 0, j = 0, l = params.length; i < l; i++) {
|
||||
@ -307,21 +308,20 @@ function makeDefaultParamFinisher() {
|
||||
* @return {function} A function that merges the constructor's doclet into the class's doclet.
|
||||
*/
|
||||
function makeConstructorFinisher(parser) {
|
||||
return (e) => {
|
||||
return ({ code, doclet: eventDoclet }) => {
|
||||
let combined;
|
||||
let doclets;
|
||||
const eventDoclet = e.doclet;
|
||||
let nodeId;
|
||||
let parentDoclet;
|
||||
|
||||
// for class declarations that are named module exports, the node that's documented is the
|
||||
// ExportNamedDeclaration, not the ClassDeclaration
|
||||
if (e.code.node.parent.parent.parent?.type === Syntax.ExportNamedDeclaration) {
|
||||
nodeId = e.code.node.parent.parent.parent.nodeId;
|
||||
if (code.node.parent.parent.parent?.type === Syntax.ExportNamedDeclaration) {
|
||||
nodeId = code.node.parent.parent.parent.nodeId;
|
||||
}
|
||||
// otherwise, we want the ClassDeclaration
|
||||
else {
|
||||
nodeId = e.code.node.parent.parent.nodeId;
|
||||
nodeId = code.node.parent.parent.nodeId;
|
||||
}
|
||||
doclets = parser._docletStore.docletsByNodeId.get(nodeId);
|
||||
// Use the first documented doclet for the parent node.
|
||||
@ -347,14 +347,12 @@ function makeConstructorFinisher(parser) {
|
||||
* @return {function} A function that adds an `async` property to the doclet of async functions.
|
||||
*/
|
||||
function makeAsyncFunctionFinisher() {
|
||||
return (e) => {
|
||||
const doclet = e.doclet;
|
||||
|
||||
return ({ code, doclet }) => {
|
||||
if (!doclet) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.code.node.async || e.code.node.value?.async || e.code.node.init?.async) {
|
||||
if (code.node.async || code.node.value?.async || code.node.init?.async) {
|
||||
doclet.async = true;
|
||||
}
|
||||
};
|
||||
@ -379,23 +377,45 @@ function makePrivatePropertyFinisher() {
|
||||
* @return {function} A function that marks a doclet as a generator function.
|
||||
*/
|
||||
function makeGeneratorFinisher() {
|
||||
return (e) => {
|
||||
const doclet = e.doclet;
|
||||
|
||||
return ({ code, doclet }) => {
|
||||
if (!doclet) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.code.node.generator || e.code.node.init?.generator || e.code.node.value?.generator) {
|
||||
if (code.node.generator || code.node.init?.generator || code.node.value?.generator) {
|
||||
doclet.generator = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that adds the source file's module type to a doclet.
|
||||
*
|
||||
* @private
|
||||
* @return {function} A function that adds the module type to the doclet's `meta` info.
|
||||
*/
|
||||
function makeModuleTypeFinisher(moduleTypes) {
|
||||
return ({ doclet }) => {
|
||||
let filepath;
|
||||
let type;
|
||||
|
||||
if (!doclet || !doclet.meta.filename || !doclet.meta.path) {
|
||||
return;
|
||||
}
|
||||
|
||||
filepath = path.join(doclet.meta.path, doclet.meta.filename);
|
||||
type = moduleTypes.get(filepath);
|
||||
|
||||
if (type) {
|
||||
doclet.meta.moduletype = type;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
class SymbolFound {
|
||||
// TODO: docs
|
||||
constructor(node, filename, extras = {}) {
|
||||
constructor(node, parser, filename, extras = {}) {
|
||||
this.id = extras.id || node.nodeId;
|
||||
this.comment = extras.comment || getLeadingJsdocComment(node) || '@undocumented';
|
||||
this.lineno = extras.lineno || node.loc.start.line;
|
||||
@ -406,6 +426,7 @@ class SymbolFound {
|
||||
this.code = extras.code;
|
||||
this.event = extras.event || 'symbolFound';
|
||||
this.finishers = extras.finishers || [];
|
||||
this.moduletype = null;
|
||||
|
||||
// make sure the event includes properties that don't have default values
|
||||
Object.keys(extras).forEach((key) => {
|
||||
@ -474,12 +495,13 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
|
||||
const extras = {
|
||||
code: astNode.getInfo(node),
|
||||
finishers: [makeModuleTypeFinisher(parser.moduleTypes)],
|
||||
};
|
||||
|
||||
switch (node.type) {
|
||||
// like: i = 0;
|
||||
case Syntax.AssignmentExpression:
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
trackVars(parser, node, e);
|
||||
|
||||
@ -495,8 +517,8 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
parent = node.parent;
|
||||
|
||||
if (node.leadingComments && parent && astNode.isFunction(parent)) {
|
||||
extras.finishers = [makeInlineParamsFinisher(parser)];
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
extras.finishers.push(makeInlineParamsFinisher(parser));
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
trackVars(parser, node, e);
|
||||
}
|
||||
@ -509,7 +531,7 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
|
||||
// like: let MyClass = class {}
|
||||
case Syntax.ClassExpression:
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
trackVars(parser, node, e);
|
||||
|
||||
@ -519,23 +541,23 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
|
||||
// like `#b = 1` in: class A { #b = 1; }
|
||||
case Syntax.ClassPrivateProperty:
|
||||
extras.finishers = [parser.resolveEnum, makePrivatePropertyFinisher()];
|
||||
extras.finishers.push(parser.resolveEnum, makePrivatePropertyFinisher());
|
||||
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
break;
|
||||
|
||||
// like `b = 1` in: class A { b = 1; }
|
||||
case Syntax.ClassProperty:
|
||||
extras.finishers = [parser.resolveEnum];
|
||||
extras.finishers.push(parser.resolveEnum);
|
||||
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
break;
|
||||
|
||||
// like: export * from 'foo'
|
||||
case Syntax.ExportAllDeclaration:
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
break;
|
||||
|
||||
@ -550,7 +572,7 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
|
||||
// like `foo as bar` in: export {foo as bar}
|
||||
case Syntax.ExportSpecifier:
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
trackVars(parser, node, e);
|
||||
|
||||
@ -566,7 +588,7 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
|
||||
// like: var foo = function() {};
|
||||
case Syntax.FunctionExpression:
|
||||
extras.finishers = [
|
||||
extras.finishers.push(
|
||||
// handle cases where at least one parameter has a default value
|
||||
makeDefaultParamFinisher(),
|
||||
// handle rest parameters
|
||||
@ -574,10 +596,10 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
// handle async functions
|
||||
makeAsyncFunctionFinisher(),
|
||||
// handle generator functions
|
||||
makeGeneratorFinisher(),
|
||||
];
|
||||
makeGeneratorFinisher()
|
||||
);
|
||||
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
trackVars(parser, node, e);
|
||||
|
||||
@ -595,8 +617,8 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
|
||||
// function parameters with inline comments
|
||||
if (node.leadingComments && parent && astNode.isFunction(parent)) {
|
||||
extras.finishers = [makeInlineParamsFinisher(parser)];
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
extras.finishers.push(makeInlineParamsFinisher(parser));
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
trackVars(parser, node, e);
|
||||
}
|
||||
@ -608,7 +630,7 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
// No need to fire an event unless the node is already commented.
|
||||
case Syntax.MemberExpression:
|
||||
if (node.leadingComments) {
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -616,7 +638,7 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
// like: foo() {}
|
||||
// or: constructor() {}
|
||||
case Syntax.MethodDefinition:
|
||||
extras.finishers = [
|
||||
extras.finishers.push(
|
||||
// handle cases where at least one parameter has a default value
|
||||
makeDefaultParamFinisher(),
|
||||
// handle rest parameters
|
||||
@ -624,20 +646,20 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
// handle async functions
|
||||
makeAsyncFunctionFinisher(),
|
||||
// handle generator functions
|
||||
makeGeneratorFinisher(),
|
||||
];
|
||||
makeGeneratorFinisher()
|
||||
);
|
||||
// for constructors, we attempt to merge the constructor's docs into the class's docs
|
||||
if (node.kind === 'constructor') {
|
||||
extras.finishers.push(makeConstructorFinisher(parser));
|
||||
}
|
||||
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
break;
|
||||
|
||||
// like `{}` in: function Foo = Class.create(/** @lends Foo */ {});
|
||||
case Syntax.ObjectExpression:
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
break;
|
||||
|
||||
@ -645,10 +667,10 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
// like `get bar() {}` in: var foo = { get bar() {} };
|
||||
case Syntax.Property:
|
||||
if (node.kind !== 'get' && node.kind !== 'set') {
|
||||
extras.finishers = [parser.resolveEnum];
|
||||
extras.finishers.push(parser.resolveEnum);
|
||||
}
|
||||
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
break;
|
||||
|
||||
@ -657,8 +679,8 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
parent = node.parent;
|
||||
|
||||
if (node.leadingComments && parent && astNode.isFunction(parent)) {
|
||||
extras.finishers = [makeInlineParamsFinisher(parser)];
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
extras.finishers.push(makeInlineParamsFinisher(parser));
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
trackVars(parser, node, e);
|
||||
}
|
||||
@ -667,7 +689,7 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
|
||||
// like: var i = 0;
|
||||
case Syntax.VariableDeclarator:
|
||||
extras.finishers = [
|
||||
extras.finishers.push(
|
||||
// handle cases where at least one parameter has a default value
|
||||
makeDefaultParamFinisher(),
|
||||
// handle rest parameters
|
||||
@ -675,10 +697,10 @@ function makeSymbolFoundEvent(node, parser, filename) {
|
||||
// handle async functions
|
||||
makeAsyncFunctionFinisher(),
|
||||
// handle generator functions
|
||||
makeGeneratorFinisher(),
|
||||
];
|
||||
makeGeneratorFinisher()
|
||||
);
|
||||
|
||||
e = new SymbolFound(node, filename, extras);
|
||||
e = new SymbolFound(node, parser, filename, extras);
|
||||
|
||||
trackVars(parser, node, e);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user