Reference Source Test

src/Doc/ClassDoc.js

import fs from 'fs-extra';
import AbstractDoc from './AbstractDoc.js';
import ParamParser from '../Parser/ParamParser.js';
import NamingUtil from '../Util/NamingUtil.js';

/**
 * Doc Class from Class Declaration AST node.
 */
export default class ClassDoc extends AbstractDoc {
  /**
   * apply own tag.
   * @private
   */
  _apply() {
    super._apply();

    this._$interface();
    this._$extends();
    this._$implements();
  }

  /** specify ``class`` to kind. */
  _$kind() {
    super._$kind();
    this._value.kind = 'class';
  }

  /** take out self name from self node */
  _$name() {
    super._$name();

    if (this._node.id) {
      this._value.name = this._node.id.name;
    } else {
      this._value.name = NamingUtil.filePathToName(this._pathResolver.filePath);
    }
  }

  /** take out self memberof from file path. */
  _$memberof() {
    super._$memberof();
    this._value.memberof = this._pathResolver.filePath;
  }

  /** for @interface */
  _$interface() {
    const tag = this._find(['@interface']);
    if (tag) {
      this._value.interface = ['', 'true', true].includes(tag.tagValue);
    } else {
      this._value.interface = false;
    }
  }

  /** for @extends, does not need to use this tag. */
  _$extends() {
    const values = this._findAllTagValues(['@extends', '@extend']);
    if (values) {
      this._value.extends = [];
      for (const value of values) {
        const {typeText} = ParamParser.parseParamValue(value, true, false, false);
        this._value.extends.push(typeText);
      }
      return;
    }

    if (this._node.superClass) {
      const node = this._node;
      let longnames = [];
      const targets = [];

      if (node.superClass.type === 'CallExpression') {
        targets.push(node.superClass.callee, ...node.superClass.arguments);
      } else {
        targets.push(node.superClass);
      }

      for (const target of targets) {
        /* eslint-disable default-case */
        switch (target.type) {
          case 'Identifier':
            longnames.push(this._resolveLongname(target.name));
            break;
          case 'MemberExpression': {
            const fullIdentifier = this._flattenMemberExpression(target);
            const rootIdentifier = fullIdentifier.split('.')[0];
            const rootLongname = this._resolveLongname(rootIdentifier);
            const filePath = rootLongname.replace(/~.*/, '');
            longnames.push(`${filePath}~${fullIdentifier}`);
          }
            break;
        }
      }

      if (node.superClass.type === 'CallExpression') {
        // expression extends may have non-class, so filter only class by name rule.
        longnames = longnames.filter((v)=> v.match(/^[A-Z]|^[$_][A-Z]/));

        const filePath = this._pathResolver.fileFullPath;
        const line = node.superClass.loc.start.line;
        const start = node.superClass.loc.start.column;
        const end = node.superClass.loc.end.column;
        this._value.expressionExtends = this._readSelection(filePath, line, start, end);
      }

      if (longnames.length) this._value.extends = longnames;
    }
  }

  /** for @implements */
  _$implements() {
    const values = this._findAllTagValues(['@implements', '@implement']);
    if (!values) return;

    this._value.implements = [];
    for (const value of values) {
      const {typeText} = ParamParser.parseParamValue(value, true, false, false);
      this._value.implements.push(typeText);
    }
  }

  /**
   * read selection text in file.
   * @param {string} filePath - target file full path.
   * @param {number} line - line number (one origin).
   * @param {number} startColumn - start column number (one origin).
   * @param {number} endColumn - end column number (one origin).
   * @returns {string} selection text
   * @private
   */
  _readSelection(filePath, line, startColumn, endColumn) {
    const code = fs.readFileSync(filePath).toString();
    const lines = code.split('\n');
    const selectionLine = lines[line - 1];
    const tmp = [];
    for (let i = startColumn; i < endColumn; i++) {
      tmp.push(selectionLine.charAt(i));
    }
    return tmp.join('');
  }
}