UNPKG

@appium/typedoc-plugin-appium

Version:

TypeDoc plugin for Appium & its extensions

156 lines 6.73 kB
"use strict"; /** * Utilities to derive comments from various sources * @module */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractExamples = exports.cloneComment = exports.deriveComment = exports.NAME_EXAMPLE_TAG = void 0; const lodash_1 = __importDefault(require("lodash")); const typedoc_1 = require("typedoc"); const types_1 = require("../types"); const finder_1 = require("./finder"); const logger_1 = require("../../logger"); const log = logger_1.fallbackLogger.createChildLogger('comment'); exports.NAME_EXAMPLE_TAG = '@example'; /** * The beginning of fenced code block looks like this. * * @todo Ensure the _end_ of the fenced code block exists */ const FENCED_CODE_BLOCK_REGEX = /^\s*```(\w+)?/; /** * Tries to figure out a comment for a command * @param opts Options * @returns A {@linkcode CommentData} containing a {@linkcode Comment}, if one can be found * @internal */ function deriveComment(opts = {}) { const { refl, comment, knownMethods } = opts; const commentFinders = (0, finder_1.getFinders)(refl); /** * The result of running thru all of the comment finders. Each value will have a * {@linkcode CommentSource} corresponding to the finder, and the a `comment` property _if and * only if_ a {@linkcode Comment} was found. */ const rawCommentData = commentFinders.map(({ getter, commentSource }) => ({ comment: getter({ refl, comment, knownBuiltinMethods: knownMethods }), commentSource, })); /** * The result of filtering out any {@linkcode CommentData} objects which do not have a `comment` property * _or_ which do but the {@linkcode Comment} property does not have any visible components. * * A comment without visible components is defined by TypeDoc as one without a "summary" (or an * empty summary) and one which has no block tags. Block tags, by definition, display something. */ const commentData = rawCommentData.filter(({ comment }) => comment?.hasVisibleComponent()); // the first summary found; this is where precedence comes in to play const summaryCommentData = commentData.find(({ comment }) => comment.summary.length); // in this bit we want to pull all block tags from all comments, and see which have content and // which don't. for each tag name (e.g., `@returns`), prefer the tag with content. if there is // no such tag, just pick the first one const allBlockTags = commentData.flatMap(({ comment }) => comment.blockTags); const tagsByTag = lodash_1.default.groupBy(allBlockTags, 'tag'); const finalBlockTags = []; for (const tags of Object.values(tagsByTag)) { if (tags.length === 1) { finalBlockTags.push(tags[0]); } else if (tags.length > 1) { // prefer a tag with content const tag = tags.find((t) => t.content.length) ?? tags[0]; finalBlockTags.push(tag); } else { if (refl) { log.warn('No comment tags found in derived comment for %s "%s". This is a bug', refl.constructor.name, refl.name); } } } // if we have a summary comment and some block tags, and the block tags are not the same as the // summary comment's block tags, then create a new comment merged from the summary and all block // tags. if (summaryCommentData && finalBlockTags.length && lodash_1.default.xor(summaryCommentData.comment.blockTags, finalBlockTags).length) { const comment = cloneComment(summaryCommentData.comment, finalBlockTags); return { comment, commentSource: types_1.CommentSource.Multiple }; } // pick the first one found return lodash_1.default.first(commentData); } exports.deriveComment = deriveComment; /** * Clones a comment. Mostly. I think. * @param comment Comment to clone * @param blockTags Block tags to use; if not provided, the comment's block tags will be used (these * are also cloned) * @returns A new comment */ function cloneComment(comment, blockTags) { const newComment = new typedoc_1.Comment(typedoc_1.Comment.cloneDisplayParts(comment.summary), (blockTags ?? comment.blockTags).map((blockTag) => { const tag = new typedoc_1.CommentTag(blockTag.tag, typedoc_1.Comment.cloneDisplayParts(blockTag.content)); tag.name = blockTag.name; return tag; })); newComment.modifierTags = new Set(comment.modifierTags); return newComment; } exports.cloneComment = cloneComment; /** * Type guard to narrow a string to a key of {@linkcode ExampleLanguage} * @param value anything * @returns `true` if the value is a valid language */ function isValidLang(value) { return value in types_1.ExampleLanguage; } /** * Finds any `@example` tags within a comment and creates an {@linkcode ExtractedExamples} object * for them. Creates a new comment with the examples removed, so we can handle them in a * custom way via our theme. * * Note that the same `Comment` can be passed multiple times (if it is displayed in multiple * modules). Our choices are either to a) clone the comment each time, or b) memoize the function * in order to avoid a problem wherein the comment has its examples extracted on the first call, but * subsequent calls will not contain any examples (since they've already been extracted). It may be * more memory-friendly to memoize, but it may not be the safest thing to do (unclear). * * @param comment A comment to extract examples from * @returns New comment (with examples removed) */ const extractExamples = (comment) => { if (!comment) { return; } comment = cloneComment(comment); const exampleTags = comment.getTags(exports.NAME_EXAMPLE_TAG); if (!exampleTags) { return { comment }; } const examples = exampleTags.flatMap(({ content }) => content.reduce((examples, { text }) => { const match = text.match(FENCED_CODE_BLOCK_REGEX); if (match) { const matchedLang = match[1] ?? 'js'; const lang = isValidLang(matchedLang) ? matchedLang : 'js'; return [ ...examples, { lang: types_1.ExampleLanguage[lang], text, }, ]; } return examples; }, [])); if (examples.length) { comment.removeTags(exports.NAME_EXAMPLE_TAG); return { examples, comment }; } return { comment }; }; exports.extractExamples = extractExamples; //# sourceMappingURL=index.js.map