vue-docgen-api
Version:
Toolbox to extract information from Vue component files for documentation generation purposes.
237 lines (236 loc) • 10.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSlotDocBlock = exports.getSlotComment = void 0;
var bt = __importStar(require("@babel/types"));
var recast_1 = require("recast");
var getDoclets_1 = __importDefault(require("../utils/getDoclets"));
var getDocblock_1 = require("../utils/getDocblock");
var transformTagsIntoObject_1 = __importDefault(require("../utils/transformTagsIntoObject"));
var getProperties_1 = __importDefault(require("./utils/getProperties"));
/**
* Extract slots information form the render function of an object-style VueJs component
* @param documentation
* @param path
*/
function slotHandler(documentation, path) {
if (bt.isObjectExpression(path.node)) {
var renderPath = (0, getProperties_1.default)(path, 'render');
// if no prop return
if (!renderPath.length) {
return Promise.resolve();
}
var renderValuePath = bt.isObjectProperty(renderPath[0].node)
? renderPath[0].get('value')
: renderPath[0];
(0, recast_1.visit)(renderValuePath.node, {
// this.$slots.default()
visitCallExpression: function (pathCall) {
if (pathCall.node.callee.type === 'MemberExpression' &&
pathCall.node.callee.object.type === 'MemberExpression' &&
pathCall.node.callee.object.object.type === 'ThisExpression' &&
pathCall.node.callee.property.type === 'Identifier' &&
pathCall.node.callee.object.property.type === 'Identifier' &&
(pathCall.node.callee.object.property.name === '$slots' ||
pathCall.node.callee.object.property.name === '$scopedSlots')) {
var doc = documentation.getSlotDescriptor(pathCall.node.callee.property.name);
var comment = getSlotComment(pathCall, doc);
var bindings = pathCall.node.arguments[0];
if ((bindings === null || bindings === void 0 ? void 0 : bindings.type) === 'ObjectExpression' && bindings.properties.length) {
doc.bindings = getBindings(bindings, comment ? comment.bindings : undefined);
}
return false;
}
this.traverse(pathCall);
return undefined;
},
// this.$slots.mySlot
visitMemberExpression: function (pathMember) {
if (pathMember.node.object.type === 'MemberExpression' &&
pathMember.node.object.object.type === 'ThisExpression' &&
pathMember.node.object.property.type === 'Identifier' &&
(pathMember.node.object.property.name === '$slots' ||
pathMember.node.object.property.name === '$scopedSlots') &&
pathMember.node.property.type === 'Identifier') {
var doc = documentation.getSlotDescriptor(pathMember.node.property.name);
getSlotComment(pathMember, doc);
return false;
}
this.traverse(pathMember);
return undefined;
},
visitJSXElement: function (pathJSX) {
var tagName = pathJSX.node.openingElement.name;
var nodeJSX = pathJSX.node;
if (tagName.type === 'JSXIdentifier' && tagName.name === 'slot') {
var doc = documentation.getSlotDescriptor(getName(nodeJSX));
var parentNode = pathJSX.parentPath.node;
var comment_1;
if (bt.isJSXElement(parentNode)) {
comment_1 = getJSXDescription(nodeJSX, parentNode.children, doc);
}
var bindings = nodeJSX.openingElement.attributes;
if (bindings && bindings.length) {
doc.bindings = bindings.map(function (b) {
return getBindingsFromJSX(b, comment_1 ? comment_1.bindings : undefined);
});
}
return false;
}
this.traverse(pathJSX);
return undefined;
}
});
}
return Promise.resolve();
}
exports.default = slotHandler;
function isStatement(path) {
return (path &&
(bt.isDeclaration(path.node) || bt.isReturnStatement(path.node) || bt.isIfStatement(path.node)));
}
function getName(nodeJSX) {
var oe = nodeJSX.openingElement;
var names = oe.attributes.filter(function (a) { return bt.isJSXAttribute(a) && a.name.name === 'name'; });
var nameNode = names.length ? names[0].value : null;
return nameNode && bt.isStringLiteral(nameNode) ? nameNode.value : 'default';
}
function getJSXDescription(nodeJSX, siblings, descriptor) {
var indexInParent = siblings.indexOf(nodeJSX);
var commentExpression = null;
for (var i = indexInParent - 1; i > -1; i--) {
var currentNode = siblings[i];
if (bt.isJSXExpressionContainer(currentNode)) {
commentExpression = currentNode;
break;
}
}
if (!commentExpression || !commentExpression.expression.innerComments) {
return undefined;
}
var cmts = commentExpression.expression.innerComments;
var lastComment = cmts[cmts.length - 1];
return parseCommentNode(lastComment, descriptor);
}
function getSlotComment(path, descriptor) {
var desc = getExpressionDescription(path, descriptor);
if (desc) {
return desc;
}
// in case we don't find a description on the expression,
// look for it on the containing statement
// 1: find the statement
var i = 10;
while (i-- && path && !isStatement(path)) {
path = path.parentPath;
}
// 2: extract the description if it exists
return path ? getExpressionDescription(path, descriptor) : undefined;
}
exports.getSlotComment = getSlotComment;
function getExpressionDescription(path, descriptor) {
var node = path.node;
if (!node || !node.leadingComments || node.leadingComments.length === 0) {
return undefined;
}
return parseCommentNode(node.leadingComments[node.leadingComments.length - 1], descriptor);
}
function parseCommentNode(node, descriptor) {
if (node.type !== 'CommentBlock') {
return undefined;
}
return parseSlotDocBlock(node.value, descriptor);
}
function parseSlotDocBlock(str, descriptor) {
var _a;
var docBlock = (0, getDocblock_1.parseDocblock)(str).trim();
var jsDoc = (0, getDoclets_1.default)(docBlock);
if (!((_a = jsDoc.tags) === null || _a === void 0 ? void 0 : _a.length)) {
return undefined;
}
var slotTags = jsDoc.tags.filter(function (t) { return t.title === 'slot'; });
if (slotTags.length) {
var tagContent = slotTags[0].content;
var description = typeof tagContent === 'string' ? tagContent : undefined;
if (description && (!descriptor.description || !descriptor.description.length)) {
descriptor.description = description;
var fixedNameMatch = description.match(/^(\S+) - (.*)$/);
if (fixedNameMatch) {
descriptor.name = fixedNameMatch[1];
descriptor.description = fixedNameMatch[2];
}
}
var tags = jsDoc.tags.filter(function (t) { return t.title !== 'slot' && t.title !== 'binding'; });
if (tags.length) {
descriptor.tags = (0, transformTagsIntoObject_1.default)(tags);
}
return {
bindings: jsDoc.tags.filter(function (t) { return t.title === 'binding'; })
};
}
return undefined;
}
exports.parseSlotDocBlock = parseSlotDocBlock;
function getBindings(node, bindingsFromComments) {
return node.properties.reduce(function (bindings, prop) {
if (bt.isIdentifier(prop.key)) {
var name_1 = prop.key.name;
var description = prop.leadingComments && prop.leadingComments.length
? (0, getDocblock_1.parseDocblock)(prop.leadingComments[prop.leadingComments.length - 1].value)
: undefined;
if (!description) {
var descbinding = bindingsFromComments
? bindingsFromComments.filter(function (b) { return b.name === name_1; })[0]
: undefined;
if (descbinding) {
bindings.push(descbinding);
return bindings;
}
}
else {
bindings.push({
title: 'binding',
name: name_1,
description: description
});
}
}
return bindings;
}, []);
}
function getBindingsFromJSX(attr, bindings) {
var name = attr.name.name;
var descbinding = bindings ? bindings.filter(function (b) { return b.name === name; })[0] : undefined;
if (descbinding) {
return descbinding;
}
return {
title: 'binding',
name: name
};
}