コメント除去 + 関数リストアップ


const fs = require('fs');
const path = require('path');

function removeComments(code) {
  const lines = code.split(/(?<=\n)/g);
  const result = [];
  let inMultilineComment = false;
  let inLiteralDoubleQuote = false;
  let inLiteralSingleQuote = false;

  for (const line of lines) {
    let processedLine = '';
    let inComment = false;

    for (let i = 0; i < line.length; i++) {
      const char = line[i];

      if (inMultilineComment) {
        // マルチラインコメントモード
        if (char === '*' && line[i + 1] === '/') {
          // 終端検出
          inMultilineComment = false;
          i++; // /ぶんをスキップ
        } else if (char === '\n') {
          // コメント継続中は改行コードのみ行末に追加
          processedLine += char;
        }
      } else if (inComment) {
        // シングルラインコメントモード
        if (char === '\n') {
          processedLine += char;
          inComment = false;
        }
      } else if (inLiteralDoubleQuote) {
        processedLine += char;
        if (char === '"' && ((i == 0) || line[i - 1] !== '\\')) {
          inLiteralDoubleQuote = false;
        }
      } else if (inLiteralSingleQuote) {
        processedLine += char;
        if (char === "'" && ((i == 0) || line[i - 1] !== '\\')) {
          inLiteralSingleQuote = false;
        }
      } else {
        if (char === '/' && line[i + 1] === '/') {
          inComment = true;
          i++;
        } else if (char === '/' && line[i + 1] === '*') {
          inMultilineComment = true;
          i++;
        } else if (char === '"') {
          inLiteralDoubleQuote = true;
          processedLine += char;
        } else if (char === "'") {
          inLiteralSingleQuote = true;
          processedLine += char;
        } else {
          processedLine += char;
        }
      }
    }

    if (processedLine.length > 0 || inMultilineComment) {
      result.push(processedLine);
    }
  }
  return result
}

function extractFunctions(lines) {
  //  const lines = code.split(/\n/g);
  const result = [];
  let currentFunctionName = '';
  let startLine = -1;
  let endLine = -1;
  let inString = false;
  let stringStartChar = '';
  let depth = 0;

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];

    // 関数定義かどうかを判定(リテラル内は無視)
    const regex = /function\s+(\w+)\s*\(/;
    const match = line.match(regex);
    if (match && depth === 0 && !inString) {
      currentFunctionName = match[1];
      startLine = i + 1;
      depth = 1;
    }

    // 関数{}の開始と終了判定
    for (let j = 0; j < line.length; j++) {
      const char = line[j];
      if ((char === '"' || char === '`') && !inString) {
        inString = true;
        stringStartChar = char;
      } else if (char === stringStartChar && inString) {
        inString = false;
      } else if (!inString) {
        if (char === '{') {
          depth++;
        } else if (char === '}') {
          depth--;
          if (depth === 1) {
            endLine = i + 1;
            result.push(`${currentFunctionName},${startLine},${endLine}`);
            currentFunctionName = '';
            depth = 0;
          }
        }
      }
    }
  }

  return result.join('\n');
}

function main() {
  // 処理対象のファイルパスを指定
  const filePath = path.join(__dirname, 'example.js');

  // ファイルを読み込む
  const code = fs.readFileSync(filePath, 'utf8');
  const code_linux = code.replace(/\r\n/g, '\n');
  // コメントを削除
  const lines_without_comments = removeComments(code_linux);
  // 結果を出力
  const functions = extractFunctions(lines_without_comments);
  console.log(functions);
}

main();