建置用於重寫預設導出的 Codemod 工具
最近在工作中,我們決定遷移到命名導出/導入並添加 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這樣的路徑,你可以更好地控制擴展,同步/異步行為等
設計兩步驟流程
我的工具的初始版本就像一個腳本中的所有內容。然而,為了提高靈活性和效能,並透過調試簡化開發過程,我將該工具重構為兩個階段:
-
資料收集:第一階段收集程式碼庫中預設導入和匯出的所有實例
- 我引入了一個環境變數 IS_GATHER_INFO 來控制這個階段。腳本使用resolve來尋找預設匯出/匯入的每個用法
- 另一個環境變數 ENTRY 包含程式碼庫入口點的相對路徑,從該檔案開始,所有導入都會被解析和分析
-
轉換:收集資料後,第二階段將預設導出重寫為命名導出。使用 jscodeshift,我可以輕鬆地並行轉換原始程式碼。
- 我引入了一個環境變數 IS_TRANSFORM 來控制這個階段
分為以下兩個步驟:
- 我能夠將資料收集與轉換分離,減少開發和偵錯期間執行的程式碼量和花費的時間
- 這是查看 GatherInfo 函數的結果、分析它、重新運行程式碼的非常方便的方法
- 測試轉換,無需重複運行整個管道並收集數據
- 如果您需要針對不同的入口點執行此工具但重複使用收集的資料 ,收集資料轉儲會很有幫助
隨著案例開始累積(例如動態導入、重新導出預設值、不同的導出實體:變數、函數和類別以及已使用的變數問題名稱),我花了更多的時間來設定測試案例。在大約 30 分鐘內,我有了一個可靠的測試設置,使我能夠轉向測試驅動開發(TDD)
。相信我,花時間在 TDD 上這些工具是值得的,因為它們有大量的案例。您走得越遠,您從測試案例中感受到的價值就越大。我想說的是,在覆蓋了一半的情況後,如果你沒有測試,在一個巨大的項目上運行和調試將成為一場噩夢,因為每次你需要添加一些更改,它可能會破壞很多其他情況。
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中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。
