デフォルトのエクスポートを書き換えるための Codemod ツールを構築する

DDD
リリース: 2024-11-03 02:36:02
オリジナル
945 人が閲覧しました

Building a Codemod Tool for Rewriting Default Exports

最近、私たちは名前付きエクスポート/インポートに移行し、eslint ルール no-default-export を追加することにしました。

動機は次のようなものでした。

デフォルトのエクスポートでは、特に大規模なコードベースでは、コードの保守が困難になる可能性があります。インポートされた名前は同じエンティティでも異なる場合があり、コードの読み取りプロセスや静的アナライザーの作成に影響を及ぼし、作業がより困難になります。逆に、名前付きエクスポートに切り替えると、デフォルトのエクスポートの欠点がすべて解消されます。

もちろん、私たちは巨大なコードベースを持っており、~1500 のデフォルトのエクスポートと~12000 のデフォルトのインポートを手動で置き換えるのは興味深い仕事ではありません?

主な問題は、名前付きエクスポート用に作成された、リンクされたすべてのファイルを同じ新しい識別子で更新することでした。

例を挙げます:

// Button/Button.tsx
const Button = () => {};
export default Button;

// Button/index.ts
export { default } from './Button.tsx';

// SomePage1.tsx
import OldButton from './component/Button';

// SomePage2.tsx
import TestButton from './component/Button';
ログイン後にコピー
ログイン後にコピー

そして私が想定した目標結果は次のようになります:

// Button/Button.tsx
export const Button = () => {};

// Button/index.ts
export { Button } from './Button.tsx';

// SomePage1.tsx
import { Button as OldButton } from './component/Button';

// SomePage2.tsx
import { Button as TestButton } from './component/Button';
ログイン後にコピー
ログイン後にコピー

私がインターネットで見つけた各解決策は、そのファイルの外部には何も知ることなく、各ファイルを個別に変換する単なる codemod でした。

私は次のようなパーサーを夢見始めました。

  1. プロジェクト内のすべてのインポートを解決し、ファイル間の関係を保存します
  2. デフォルトのインポート/エクスポートに関する情報を収集します
  3. 名前付きエクスポートの新しい識別名を作成します
  4. リポジトリ全体のすべてのエントリを置き換えますか?

そこで私は、デフォルトのエクスポート/インポートを名前付きエクスポート/インポートに自動的に書き換える codemod ツールを開発するという新たな挑戦をしました。

ネタバレ

もう開発しました! ? ?

開発プロセス

最初の考え
これは、前回の実験「反応コンポーネント ツリーの視覚化」の直後に起こりました。最初のアイデアは、babel プラグインと webpack プラグインを再利用して、すべてのモジュールを反復処理して AST を解析することでした。しかし、jscodeshift にすでにパーサーがあり、jscodeshift の代わりとなるパーサーが見つかったとしたら、なぜでしょうか。 webpack プラグインを使用すると、バンドラーに依存しないツールを作成できるようになります。

ツール
OK、パーサーとして jscodeshift を使用しました。しかし、エントリポイントから始まるすべてのファイル間の関係を見つけるために、ネイティブのnodejs require.resolveなどのパスを解決するのに役立つresolveパッケージを見つけましたが、バンドラーのようなパスを解決することに似ており、拡張子、同期をより詳細に制御できます。 /async 動作など

2 段階プロセスのエンジニアリング
私のツールの初期バージョンは、すべてが 1 つのスクリプトにまとめられているようなものでした。ただし、柔軟性とパフォーマンスを向上させ、デバッグを伴う開発プロセスを簡素化するために、ツールを 2 つの段階にリファクタリングしました。

  1. データ収集: 最初のフェーズでは、コードベース全体のデフォルトのインポートとエクスポートのすべてのインスタンスを収集します

    • このフェーズを制御するために、環境変数 IS_GATHER_INFO を導入しました。スクリプトは、resolve を使用して、デフォルトのエクスポート / インポート
    • のすべての使用法を検索します。
    • 別の環境変数 ENTRY には、コードベースのエントリ ポイントへの相対パスが含まれており、そのファイルから開始して、すべてのインポートが解決および分析されます
  2. 変換: データが収集されると、第 2 フェーズでデフォルトのエクスポートが名前付きエクスポートに書き換えられます。 jscodeshift を使用して、ソース コードを並列かつ簡単に変換しました。

    • このフェーズを制御するために、環境変数 IS_TRANSFORM を導入しました

次の 2 つのステップに分割します:

  • データ収集を変換から切り離すことができ、実行されるコードの量と、開発とデバッグに費やす時間を削減できました。
    • gatherInfo 関数の結果を確認し、分析し、コードを再実行するのに非常に便利な方法です
    • 収集したデータを使用してパイプライン全体を繰り返し実行せずに変換をテストします
  • データ ダンプの収集は、さまざまなエントリ ポイントに対してこのツールを実行する必要があるが、収集されたデータを再利用する必要がある場合に役立ちます

ケースが蓄積し始めたので (動的インポート、再エクスポートされたデフォルト、エクスポートされたさまざまなエンティティ: 変数、関数、クラス、変数の問題の既に使用されている名前など)、テスト ケースのセットアップに追加の時間を費やしました。約 30 分でしっかりとしたテストのセットアップが完了し、テスト駆動開発 (TDD) に移行できるようになりました。信じてください。膨大な数のケースがあるこのようなツールの TDD に時間を費やす価値はあります。さらに進めば進むほど、テスト ケースからより多くの価値を感じることができます。テストがない場合にケースの半分をカバーした後、変更を追加する必要があるたびに他の多くのケースが壊れる可能性があるため、巨大なプロジェクトを実行してデバッグするのは悪夢になります。

AST:
次のタイプの AST ノードを使用しました:

  • デフォルトのインポートステートメントのみを検索する ImportDefaultSpecifier
    • 「...」から何かをインポートします
  • ExportDefaultDeclaration を使用して、デフォルトのエクスポート ステートメントのみを検索します。
    • デフォルトのものをエクスポートします;
  • ExportNamedDeclaration を使用してインポート デフォルト ステートメントとエクスポート デフォルト ステートメントを検索します。
    • 「...」から { デフォルトとして何か } をエクスポートします - デフォルトのエクスポート
    • import { デフォルトを何かとして } from '...' - デフォルトのインポート
    • export {default} from '...' - デフォルトのインポートとデフォルトのエクスポートを同時に実行します
  • ImportExpression を使用して動的インポートを検索し、そのファイルを必要に応じてマークし、デフォルトのエクスポートを保持します。 React.lazy などの一部のツールは、デフォルトのエクスポートでのみ機能します。
    • インポート('...')
  • また、プロキシ ファイルに関する情報を保存しました。これは、デフォルトの何かをインポートし、その何かをデフォルトとしてエクスポートするファイルです。
    • これを使用して、任意のファイル内の名前付きエクスポートの新しい名前を検索します: file a ->ファイルb ->ファイルc

技術的な考慮事項と既知の制限事項
このツールは機能しますが、まだ処理されていないいくつかの特殊なケースがあります:

namespace.default の使用法
次のコードはまだ変換されません:

// Button/Button.tsx
const Button = () => {};
export default Button;

// Button/index.ts
export { default } from './Button.tsx';

// SomePage1.tsx
import OldButton from './component/Button';

// SomePage2.tsx
import TestButton from './component/Button';
ログイン後にコピー
ログイン後にコピー

プロキシ ファイルの競合
出典:

// Button/Button.tsx
export const Button = () => {};

// Button/index.ts
export { Button } from './Button.tsx';

// SomePage1.tsx
import { Button as OldButton } from './component/Button';

// SomePage2.tsx
import { Button as TestButton } from './component/Button';
ログイン後にコピー
ログイン後にコピー

結果:

import * as allConst from './const';
console.log(allConst.default);
ログイン後にコピー


のようなめちゃくちゃなエクスポート 出典:

export { Modals as default } from './Modals';
export { Modals } from './Modals';
ログイン後にコピー

実装が異なる 2 つの同じエクスポートがあるため、ロジックが壊れます:

export { Modals } from './Modals';
export { Modals } from './Modals';
ログイン後にコピー

前のエンティティのインポートも手動で修正する必要があります
出典:

export class GhostDataProvider {}
export default hoc()(GhostDataProvider);
ログイン後にコピー

結果:

export class GhostDataProvider {}
const GhostDataProviderAlias = hoc()(GhostDataProvider);
export { GhostDataProviderAlias as GhostDataProvider };
ログイン後にコピー

これらの制限にもかかわらず、残りのエラーを 15 ~ 20 分で手動で修正し、実際のプロジェクトを正常に起動しました。 rewrite-default-exports.

リンク

  • jscodeshift
  • アストエクスプローラー

以上です。以下のコメントを歓迎します! ?

以上がデフォルトのエクスポートを書き換えるための Codemod ツールを構築するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート