UNPKG

polymer-analyzer

Version:
175 lines (173 loc) 5.95 kB
"use strict"; /** * @license * Copyright (c) 2015 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at * http://polymer.github.io/LICENSE.txt * The complete set of authors may be found at * http://polymer.github.io/AUTHORS.txt * The complete set of contributors may be found at * http://polymer.github.io/CONTRIBUTORS.txt * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt */ Object.defineProperty(exports, "__esModule", { value: true }); const doctrine = require("doctrine"); const model_1 = require("../model/model"); /** * Given a JSDoc string (minus opening/closing comment delimiters), extract its * description and tags. */ function parseJsdoc(docs) { docs = removeLeadingAsterisks(docs); const d = doctrine.parse(docs, { unwrap: false, // lineNumbers: true, preserveWhitespace: true, }); // Strip any leading and trailing newline characters in the // description of multiline comments for readibility. // TODO(rictic): figure out if we can trim() here or not. Something something // markdown? const description = d.description && d.description.replace(/^\n+|\n+$/g, ''); return { description, tags: parseCustomTags(d.tags) }; } exports.parseJsdoc = parseJsdoc; // Tags with a name: @title name description const tagsWithNames = new Set([ 'appliesMixin', 'demo', 'hero', 'mixinFunction', 'polymerBehavior', 'pseudoElement' ]); const firstWordAndRest = /^\s*(\S*)\s*(.*)$/; function parseCustomTags(tags) { return tags.map((tag) => { if (tag.description != null && tagsWithNames.has(tag.title)) { const match = firstWordAndRest.exec(tag.description); if (match != null) { const name = match[1]; const description = match[2]; return Object.assign({}, tag, { name, description }); } } return tag; }); } /** * removes leading *, and any space before it */ function removeLeadingAsterisks(description) { return description.split('\n') .map(function (line) { // remove leading '\s*' from each line const match = line.match(/^[\s]*\*\s?(.*)$/); return match ? match[1] : line; }) .join('\n'); } exports.removeLeadingAsterisks = removeLeadingAsterisks; function hasTag(jsdoc, title) { return getTag(jsdoc, title) !== undefined; } exports.hasTag = hasTag; /** * Finds the first JSDoc tag matching `title`. */ function getTag(jsdoc, title) { return jsdoc && jsdoc.tags && jsdoc.tags.find((t) => t.title === title); } exports.getTag = getTag; function unindent(text) { if (!text) return text; const lines = text.replace(/\t/g, ' ').split('\n'); const indent = lines.reduce(function (prev, line) { if (/^\s*$/.test(line)) return prev; // Completely ignore blank lines. const lineIndent = line.match(/^(\s*)/)[0].length; if (prev === null) return lineIndent; return lineIndent < prev ? lineIndent : prev; }, 0); return lines .map(function (l) { return l.substr(indent); }) .join('\n'); } exports.unindent = unindent; function isAnnotationEmpty(docs) { return docs === undefined || docs.tags.length === 0 && docs.description.trim() === ''; } exports.isAnnotationEmpty = isAnnotationEmpty; const privacyTags = new Set(['public', 'private', 'protected']); function getPrivacy(jsdoc) { return jsdoc && jsdoc.tags && jsdoc.tags.filter((t) => privacyTags.has(t.title)) .map((t) => t.title)[0]; } exports.getPrivacy = getPrivacy; /** * Returns the mixin applications, in the form of ScannedReferences, for the * jsdocs of class. * * The references are returned in presumed order of application - from furthest * up the prototype chain to closest to the subclass. */ function getMixinApplications(document, node, docs, warnings) { // TODO(justinfagnani): remove @mixes support const appliesMixinAnnotations = docs.tags.filter((tag) => tag.title === 'appliesMixin' || tag.title === 'mixes'); return appliesMixinAnnotations .map((annotation) => { const mixinId = annotation.name; // TODO(justinfagnani): we need source ranges for jsdoc annotations const sourceRange = document.sourceRangeForNode(node); if (mixinId === undefined) { warnings.push(new model_1.Warning({ code: 'class-mixes-annotation-no-id', message: '@appliesMixin annotation with no identifier. Usage `@appliesMixin MixinName`', severity: model_1.Severity.WARNING, sourceRange, parsedDocument: document })); return; } return new model_1.ScannedReference(mixinId, sourceRange); }) .filter((m) => m !== undefined); } exports.getMixinApplications = getMixinApplications; function extractDemos(jsdoc) { if (!jsdoc || !jsdoc.tags) { return []; } const demos = []; const demoUrls = new Set(); for (const tag of jsdoc.tags.filter((tag) => tag.title === 'demo' && tag.name)) { const demoUrl = tag.name; if (demoUrls.has(demoUrl)) { continue; } demoUrls.add(demoUrl); demos.push({ desc: tag.description || undefined, path: demoUrl, }); } return demos; } exports.extractDemos = extractDemos; function join(...jsdocs) { return { description: jsdocs.map((jsdoc) => jsdoc.description || '').join('\n\n').trim(), tags: jsdocs.map((jsdoc) => jsdoc.tags) .reduce((acc, tags) => acc.concat(tags)), }; } exports.join = join; //# sourceMappingURL=jsdoc.js.map