開始コード化:コードを解析するコード(タイプスクリプト)


コードの使用方法についていくつかの統計情報を取得したり、いくつかのドキュメントを生成する場合は、適切な場所です!
この記事であなたのコードを分析する方法を調査しましょう.
文字の比較の一連のregexを使用するよりも、我々はこれを効率的に構文解析を使用してASTツリーのおかげで達成する方法を説明します.
我々は、独自のユースケースの周りにこれを開発する:分析サービスへのすべての呼び出しを見つけて、Markdownレポート(我々の製品チームによって使用される)を作成します.
この作品は、Nfroidurejsarch

ステップ0 :パッケージのインストール


ここでは、この記事で使用するすべてのパッケージです.
npm install -D ts-node @babel/parser ast-types glob-promise json2md lodash
我々のスクリプトはtypescriptで書かれて、NPMコマンドによって起動されるでしょう.JSON :
"scripts": {
    "analytics": "ts-node -O '{\"module\":\"commonjs\"}' bin/parseAnalytics.ts"
}
ts-node 私たちのtypescriptスクリプトを変換するために必要です、そして、我々が使うので、少しのオプションimport 文.

ステップ1 :グラブファイル


最初のステップはかなり簡単です.プロジェクト内のすべてのファイルを最適化し、読み取りませんでしたので、訪問するファイルの一覧を生成します.
それをするために、我々は使用するnode-glob .
我々の場合は訪問したいjavascript or typescript 我々のファイル/src フォルダ.
const filesPaths = await glob("src/**/*.{ts,js}", {
    cwd: process.cwd(),
    dot: true,
    nodir: true,
    absolute: true,
});
process.cwd() 現在のディレクトリを返します.

ステップ2:ファイルの内容をASTに


コードを解析するには、コードからASTツリーを生成します.
すぐに、ASTツリーの定義です.

An abstract syntax tree (AST) is a tree representation of the abstract syntactic structure of source code written in a programming language.



それで、我々のファイルリストで、我々は彼らの内容を読みますfs 図書館.
const content = fs.readFileSync(filePath, "utf-8");
次に、Babel Parserの助けを借りてASTを作成します(必要に応じてプラグインを追加できます).
const ast = parse(content, {
    sourceType: "module",
    plugins: ["typescript", "jsx", "classProperties"],
});

ステップ3:訪問AST


この手順では、ASTツリーを参照して簡単な構造に変換します(この例では、すべての解析コールを使用した配列になります)、現在のイベントの名前が含まれています.
利用可能な多くの訪問機能(ASTタイプあたりのより少ない1つ)があるので、我々は最初に訪問関数が最も適切であるのを見つける必要があります.
そのためには、たとえばASTツリーエクスプローラーで訪れたいファイルの例をコピーしてくださいast-explorer .
この例では、解析したいコールはexpression文です:
Analytics.track({
    event: 'This is a event',
    properties: {
        device: 1
    },
});
したがって、私たちはvisitExpressionStatement :
const analyticsCalls = [];
 visit(ast, {
      visitExpressionStatement: function (path) {
          // Implement your logic here to grab correct parameter
          // In our case, we retrieve the event sended
          if (path.value.expression.callee?.property?.name === 'track') {
            const analyticsParams = path.value.expression.arguments[0];
            const analyticEventName = analyticsParams.properties.find(
              (prop) => prop.key.name === 'event',
            );
            analyticsCalls.push({
              event: analyticEventName?.value?.value || 'N/D',
            });
          }
          this.traverse(path);
      },
 });
訪問関数では、ASTノードからプロパティを探索し、次のステップの配列にプッシュするオブジェクトを作成します.
これは最も難しいステップですが、ASTエクスプローラーでは、各ASTノードの正しい訪問機能とプロパティを見つけるのに役立ちます.
The traverse 関数は呼び出されなければなりません.

ステップ4:あなたのMarkdownを生成する


最後に、以前に生成した配列を使用して、Markdownを作成しますjson2md package :
json2md([
    { h1: 'Analytics' },
    {
      table: {
        headers: ['Event'],
        rows: analyticsCalls.map((analyticsCall) => ({
          Event: analyticsCall.event,
        })),
      },
    },
]);

ファイナルスクリプト


スクリプトの要約です.
import { parse } from '@babel/parser';
import { visit } from 'ast-types';
import fs from 'fs';
import glob from 'glob-promise';
import json2md from 'json2md';
import { flatten } from 'lodash';

type AnalyticsElement = {
  event: string;
};

async function parseAnalytics() {
  const cwd = process.cwd();

  const filesPaths: string[] = await glob('src/**/*.{ts,js}', {
    cwd,
    dot: true,
    nodir: true,
    absolute: true,
  });

  const analyticsCalls = await Promise.all(
    filesPaths.map((file) => extractAnalytics(file)),
  );

  const markdownContent = generateMarkdown(flatten(analyticsCalls));

  fs.truncateSync('ANALYTICS.md');
  fs.writeFileSync('ANALYTICS.md', markdownContent);
}

async function extractAnalytics(filePath: string): Promise<AnalyticsElement[]> {
  const content = fs.readFileSync(filePath, 'utf-8');

  const ast = parse(content, {
    sourceType: 'module',
    plugins: ['typescript', 'jsx', 'classProperties'],
  });

  const analyticsCalls: AnalyticsElement[] = [];

  visit(ast, {
    visitExpressionStatement: function (path) {
      if (path.value.expression.callee?.property?.name === 'track') {
        const analyticsParams = path.value.expression.arguments[0];
        const analyticEventName = analyticsParams.properties.find(
          (prop) => prop.key.name === 'event',
        );
        analyticsCalls.push({
          event: analyticEventName?.value?.value || 'N/D',
        });
      }
      this.traverse(path);
    },
  });

  return analyticsCalls;
}

function generateMarkdown(analyticsCalls: AnalyticsElement[]): string {
  return json2md([
    { h1: 'Analytics' },
    {
      table: {
        headers: ['Event'],
        rows: analyticsCalls.map((analyticsCall) => ({
          Event: analyticsCall.event,
        })),
      },
    },
  ]);
}

parseAnalytics();

結論


今すぐあなたのニーズに合わせて、このusecaseを適応することができる必要があります、私たちはあなたがこの記事で物事を学んでほしい!
読書ありがとう!

著者