首頁 > web前端 > js教程 > 建置用於重寫預設導出的 Codemod 工具

建置用於重寫預設導出的 Codemod 工具

DDD
發布: 2024-11-03 02:36:02
原創
995 人瀏覽過

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';
登入後複製
登入後複製

我在網路上找到的每個解決方案都只是一個程式碼模組,用於獨立轉換每個文件,而不知道該文件以外的任何其他內容。

我開始夢想有一個解析器能夠:

  1. 解析專案中的所有匯入並儲存檔案之間的關係
  2. 收集有關預設導入/匯出的資訊
  3. 為命名匯出建立新的識別碼名稱
  4. 替換儲存庫中的所有條目?

因此,我接受了新的挑戰,開發了一個 codemod 工具,可以自動將預設匯出/匯入重寫為命名的匯出/匯入。

劇透

我已經開發出來了! ? ?

開發流程

第一個想法
它發生在我之前的實驗可視化反應組件樹之後,第一個想法是重用babel 和webpack 插件來迭代所有模組並解析AST,但是為什麼,如果jscodeshift 已經有了解析器,並且如果我找到了替代品webpack插件我將能夠編寫一個與捆綁器無關的工具,這很棒嗎?

工具
好的,我有一個 jscodeshift 作為解析器。但是為了找到從入口點開始的所有檔案之間的關係,我找到了resolve包,它有助於解析像原生nodejs require.resolve這樣的路徑,但它更類似於解析像bundlers這樣的路徑,你可以更好地控制擴展,同步/異步行為等

設計兩步驟流程
我的工具的初始版本就像一個腳本中的所有內容。然而,為了提高靈活性和效能,並透過調試簡化開發過程,我將該工具重構為兩個階段:

  1. 資料收集:第一階段收集程式碼庫中預設導入和匯出的所有實例

    • 我引入了一個環境變數 IS_GATHER_INFO 來控制這個階段。腳本使用resolve來尋找預設匯出/匯入的每個用法
    • 另一個環境變數 ENTRY 包含程式碼庫入口點的相對路徑,從該檔案開始,所有導入都會被解析和分析
  2. 轉換:收集資料後,第二階段將預設導出重寫為命名導出。使用 jscodeshift,我可以輕鬆地並行轉換原始程式碼。

    • 我引入了一個環境變數 IS_TRANSFORM 來控制這個階段

分為以下兩個步驟:

  • 我能夠將資料收集與轉換分離,減少開發和偵錯期間執行的程式碼量和花費的時間
    • 這是查看 GatherInfo 函數的結果、分析它、重新運行程式碼的非常方便的方法
    • 測試轉換,無需重複運行整個管道並收集數據
  • 如果您需要針對不同的入口點執行此工具但重複使用收集的資料
  • ,收集資料轉儲會很有幫助

隨著案例開始累積(例如動態導入、重新導出預設值、不同的導出實體:變數、函數和類別以及已使用的變數問題名稱),我花了更多的時間來設定測試案例。在大約 30 分鐘內,我有了一個可靠的測試設置,使我能夠轉向測試驅動開發(TDD)

。相信我,花時間在 TDD 上這些工具是值得的,因為它們有大量的案例。您走得越遠,您從測試案例中感受到的價值就越大。我想說的是,在覆蓋了一半的情況後,如果你沒有測試,在一個巨大的項目上運行和調試將成為一場噩夢,因為每次你需要添加一些更改,它可能會破壞很多其他情況。

AST:

我使用了以下類型的 AST 節點:
  • ImportDefaultSpecifier 僅查找導入預設語句
    • 從「...」導入一些內容
  • ExportDefaultDeclaration 僅尋找匯出預設語句
    • 導出預設的東西;
  • ExportNamedDeclaration 用於尋找導入預設值和匯出預設值語句
    • 從 '...' 匯出 { 預設值 } - 預設導出
    • 從 '...' 匯出 { default as Something } - 預設導入
    • export { default } from '...' - 同時預設導入和預設導出
  • ImportExpression 尋找動態匯入並根據需要標記該檔案以保留預設匯出。有些工具(例如 React.lazy)僅適用於預設導出。
    • 導入('...')
  • 此外,我保存了有關代理文件的信息,它是導入預設內容並將該內容導出為預設內容的文件
    • 用它來找出任何檔案中指定匯出的新名稱:file a ->檔案b->檔案 c

技術注意事項與已知限制
儘管該工具可以正常運行,但仍有一些邊緣情況尚未處理:

命名空間.預設用法
以下程式碼還不會被轉換:

// 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';
登入後複製

將導致邏輯損壞,因為現在它有兩個具有不同實現的相同導出:

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 分鐘內手動修復了其餘錯誤,並成功啟動了我們的真實專案。重寫預設導出。

連結

  • jscodeshift
  • astexplorer

就是這樣,歡迎下方留言! ?

以上是建置用於重寫預設導出的 Codemod 工具的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板