export function parseContents()

in src/jsdoc.ts [212:289]


export function parseContents(commentText: string): ParsedJSDocComment|null {
  // Make sure we have proper line endings before parsing on Windows.
  commentText = normalizeLineEndings(commentText);
  // Strip all the " * " bits from the front of each line.
  commentText = commentText.replace(/^\s*\*? ?/gm, '');
  const lines = commentText.split('\n');
  const tags: Tag[] = [];
  const warnings: string[] = [];
  for (const line of lines) {
    let match = line.match(/^\s*@(\S+) *(.*)/);
    if (match) {
      let [_, tagName, text] = match;
      if (tagName === 'returns') {
        // A synonym for 'return'.
        tagName = 'return';
      }
      let type: string|undefined;
      if (BANNED_JSDOC_TAGS_INPUT.has(tagName)) {
        if (tagName !== 'template') {
          // Tell the user to not write banned tags, because there is TS
          // syntax available for them.
          warnings.push(`@${tagName} annotations are redundant with TypeScript equivalents`);
          continue;  // Drop the tag so Closure won't process it.
        } else {
          // But @template in particular is special: it's ok for the user to
          // write it for documentation purposes, but we don't want the
          // user-written one making it into the output because Closure interprets
          // it as well.
          // Drop it without any warning.  (We also don't ensure its correctness.)
          continue;
        }
      } else if (JSDOC_TAGS_WITH_TYPES.has(tagName)) {
        if (text[0] === '{') {
          warnings.push(
              `the type annotation on @${tagName} is redundant with its TypeScript type, ` +
              `remove the {...} part`);
          continue;
        }
      } else if (tagName === 'suppress') {
        const typeMatch = text.match(/^\{(.*)\}(.*)$/);
        if (typeMatch) {
          [, type, text] = typeMatch;
        } else {
          warnings.push(`malformed @${tagName} tag: "${text}"`);
        }
      } else if (tagName === 'dict') {
        warnings.push('use index signatures (`[k: string]: type`) instead of @dict');
        continue;
      }

      // Grab the parameter name from @param tags.
      let parameterName: string|undefined;
      if (tagName === 'param') {
        match = text.match(/^(\S+) ?(.*)/);
        if (match) [_, parameterName, text] = match;
      }

      const tag: Tag = {tagName};
      if (parameterName) tag.parameterName = parameterName;
      if (text) tag.text = text;
      if (type) tag.type = type;
      tags.push(tag);
    } else {
      // Text without a preceding @tag on it is either the plain text
      // documentation or a continuation of a previous tag.
      if (tags.length === 0) {
        tags.push({tagName: '', text: line});
      } else {
        const lastTag = tags[tags.length - 1];
        lastTag.text = (lastTag.text || '') + '\n' + line;
      }
    }
  }
  if (warnings.length > 0) {
    return {tags, warnings};
  }
  return {tags};
}