最近在工作中,我們決定遷移到命名導出/導入並添加 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 工具,可以自動將預設匯出/匯入重寫為命名的匯出/匯入。
我已經開發出來了! ? ? 劇透
第一個想法
它發生在我之前的實驗可視化反應組件樹之後,第一個想法是重用babel 和webpack 插件來迭代所有模組並解析AST,但是為什麼,如果jscodeshift 已經有了解析器,並且如果我找到了替代品webpack插件我將能夠編寫一個與捆綁器無關的工具,這很棒嗎?
工具
好的,我有一個 jscodeshift 作為解析器。但是為了找到從入口點開始的所有檔案之間的關係,我找到了resolve包,它有助於解析像原生nodejs require.resolve這樣的路徑,但它更類似於解析像bundlers這樣的路徑,你可以更好地控制擴展,同步/異步行為等
設計兩步驟流程
我的工具的初始版本就像一個腳本中的所有內容。然而,為了提高靈活性和效能,並透過調試簡化開發過程,我將該工具重構為兩個階段:
資料收集:第一階段收集程式碼庫中預設導入和匯出的所有實例
轉換:收集資料後,第二階段將預設導出重寫為命名導出。使用 jscodeshift,我可以輕鬆地並行轉換原始程式碼。
分為以下兩個步驟:
隨著案例開始累積(例如動態導入、重新導出預設值、不同的導出實體:變數、函數和類別以及已使用的變數問題名稱),我花了更多的時間來設定測試案例。在大約 30 分鐘內,我有了一個可靠的測試設置,使我能夠轉向測試驅動開發(TDD)
。相信我,花時間在 TDD 上這些工具是值得的,因為它們有大量的案例。您走得越遠,您從測試案例中感受到的價值就越大。我想說的是,在覆蓋了一半的情況後,如果你沒有測試,在一個巨大的項目上運行和調試將成為一場噩夢,因為每次你需要添加一些更改,它可能會破壞很多其他情況。
AST:
技術注意事項與已知限制
儘管該工具可以正常運行,但仍有一些邊緣情況尚未處理:
命名空間.預設用法
以下程式碼還不會被轉換:
// 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 分鐘內手動修復了其餘錯誤,並成功啟動了我們的真實專案。重寫預設導出。
就是這樣,歡迎下方留言! ?
以上是建置用於重寫預設導出的 Codemod 工具的詳細內容。更多資訊請關注PHP中文網其他相關文章!