條條大路通 Rome。在 Rome 尚未發佈 NPM 正式版之際。咱們圍繞 JavaScript 工具鏈爲核心點,來看看前往 Rome 的路上都有什麼;以及 Rome 自己,意味着什麼?
二月的最後一天,我在爲「開源愛好者月刊」搜尋本月最新的開源項目時,偶遇一個名叫 Rome 的倉庫霸榜,眼前着實一亮。「一個實驗性的 JavaScript 工具鏈」、「包括編譯器、lint、格式化程序、捆綁器、測試框架等」以及「旨在成爲與 JavaScript 源碼處理相關的全部功能的綜合工具」短短几句話展示了一個宏大的目標。如今,是時候入坑瞭解一波並在我的能力範圍內做一個淺要的分享。javascript
Rome 由就任於 Facebook 同時是 Babel 和 Yarn 做者的 Sebastian McKenzie 主導開源,開源以前,Rome 基本是他的我的項目,如今 Facebook 願意付薪水讓他潛心開發。截止如今(2020 年 04 月初),Rome 的提交記錄已經從 70+ 到 600+,貢獻者拓展到了 40+ 位,產生了 30+ issues 和 170+ Pull Request。前端
此外,或許也能從側面呼應我曾在月刊第三期中收錄的一句關於「創業公司和大公司開源出發點有何不一樣」的話:大公司可能在一個項目的早期便開源,憑藉其號召力但願更多人一塊兒「貢獻」迭代,初創團隊則會在產品相對成熟的時候再開放,但願儘快吸引用戶深度「使用」,注重完善產品在工業環境下的綜合表現。java
正文 & 背景 & 乾貨開始。node
從官網不難看出,Rome 旨在成爲與 JavaScript 源代碼處理相關的全部功能的綜合工具,其中包括「編譯器、Linter、格式化程序、捆綁器、依賴管理器和測試框架」等。Rome 源於對整個項目的擴展範圍一致性的渴望。ios
同時,Rome 也來源於 Babel 做者自己對 Babel 的一些不知足而新創,就像 Deno 之於 Node 同樣。git
本節根據 README.md 和官網首頁的介紹,來以問答形式展現 Rome 的背景和想要達到具體目標。github
在計算機科學中只有兩件難事:緩存失效和命名。 ——Phil Karlton
在版本控制系統中,monorepo(單聲道存儲庫的音節縮寫)是一種軟件開發策略,其中許多項目的代碼存儲在同一存儲庫中。 截至 2017 年,這種軟件工程實踐已有十多年的歷史,但直到最近才被命名。——Monorepo,維基百科
在學習一個工具以前,每每咱們應該先去了解這個工具能夠用來解決什麼樣的問題;一樣的,當咱們遇到一個問題的時候,咱們也應該帶着這個問題去找工具解決。
——阿里巴巴集團 高級前端工程師 葉俊星
成熟的軟件項目必然遵循的良好的開發規範,也擁有屬於自身獨特的軟件開發生命週期,編程實踐只佔整個開發週期的很小一部分。當一個 JavaScript 軟件被創建時一般還會遇到哪些須要解決的問題?這便涉及到了 JavaScript 項目的技術選型,而 JavaScript 生態圈的明星項目數不勝數,如下做一個縱覽,不涉及各個工具的具體使用方式。web
所以能夠看出,技術選型即是針對能讓項目成功運轉各個環節尋找相應的解決方案;工做流(Workflow)是全部解決方案融合後的落實流程;而工具鏈(Toolchain)即是工做流下全部實現方式的彙總,同時一個工具也能表明一個解決方案。npm
簡而言之,JavaScript 工具鏈即是 JavaScript 工程師在開發過程當中會用到的一系列工具。編程
如今 Rome 並無直接在 Github 上發佈任何版本,但編譯後生成的 rome.json 能夠看出有一個 v0.0.52 的版本號,處於一個很早期的狀態,項目簡介也是「一個實驗性的 JavaScript 工具鏈」。
想要嘗試 Rome,就得從如下步驟逐步展開(因爲 Rome 沒有發佈正式版本,這裏無需過多涉及如何整合在 package.json 的腳本中使用等工程化過程)。
本章全部 Demo 均在 @hylerrix/demos 的 Rome 文件夾中。
既然 Rome 沒有正式發佈版本,咱們也沒法直接從 NPM 上直接安裝 Rome。現階段,Rome 提供了本地安裝的方式,只須要克隆到本地並本地編譯和本地 NPM 安裝便可使用。
注:安裝 Rome 前請確保本地已正常安裝 Node 和 NPM
# 克隆 Rome 項目到本地 $ git clone https://github.com/facebookexperimental/rome # 命令行進入 Rome 項目 $ cd rome # 方式一:編譯 Rome $ ./scripts/build-release dist # 方式二:編譯 Rome(Windows 10 的狀況下,使用 PowerShell 7) $ cd rome && node scripts/build-release dist # 安裝編譯後的 Rome 到本地全局環境中 $ npm install -g ./dist/ # 如今即可以使用 Rome 了 $ rome # No command specified. Run --help to see available commands.
rome init 命令會在當前目錄生成一個 rome.json 文件,使用推薦配置會初始化如下內容:
{ "version": "^0.0.52", "lint": { "enabled": true } }
該文件告訴 Rome 至少應爲 0.0.52 版本,以便與當前項目一塊兒使用。具體使用文檔還在開發中。
rome run 命令將運行傳遞給它的任何文件,一般與項目的主文件一塊兒使用。目前仍在開發中,可能沒法正確處理全部源文件。此時咱們爲測試 rome run 成功運行,創建一個 index.ts 和 api.ts 文件,以下。
// index.ts import { getData } from './api' async function setData () { const { success, data } = await getData() console.log('success:', success) console.log('data:', data) } await setData() // api.ts export const getData = () => Promise.resolve({ success: true, data: 'Hello World!' })
此時,運行以下命令即可以成功使用:
$ rome run index.ts # ℹ Bundling index.ts # success: true # data: 'Hello World!'
因爲我真的不喜歡在 JavaScript 應用裏面寫分號,這與主流規範有些不一樣,因此 rome lint 命令恰好派上了用場:rome 默認須要在 JavaScript 語句結尾寫分號。同時在 api.ts 中故意不導出一個 interface 且在 index.ts 中故意將其錯誤導入,重構後的有錯誤 index.ts 和 api.ts 以及 rome lint 後執行過程以下:
// 故意錯誤編寫的 index.ts import { getData } from './api' async function setData() { const {success, data} = await getData() console.log('success:', success) console.log('data:', data) } await setData() // 故意錯誤不導出的 api.ts interface Params { username: string token: string } export const getData = (params: Params) => Promise.resolve({ success: true, data: 'Hello World!' })
$ rome lint index.ts # index.ts lint/pendingFixes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # # ✖ Pending fixes # # 1 │ + import {getData, Params} from './api.ts'; # │ - import {·getData, Params·} from './api.ts' # 2 │ # .. │ # 4 │ const param: Params = { # 5 │ username: 'hylerrix', # 6 │ + token: 'ningowood', # 7 │ + }; # 8 │ + const {success, data} = await getData(param); # 9 │ + console.log('success:', success); # 10 │ + console.log('data:', data); # 11 │ } # 12 │ # 13 │ + await setData(); # 14 │ # # index.ts:1:18 resolver/unknownExport ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # # ✖ Couldn't find export Params in api.ts # # > 1 │ import { getData, Params } from './api.ts' # │ ^^^^^^ # 2 │ # 3 │ async function setData() { # # ℹ However we found a matching local variable in api.ts. Did you forget to export it? # # > 1 │ interface Params { # │ ^^^^^^ # 2 │ username: string # 3 │ token: string # # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # # ℹ Fixes available. Run `rome lint --fix` to apply. # ✖ Found 2 problems $ rome lint index.ts --fix # ...... # ✔ 1 file fixed # ✖ Found 2 problems
rome lint 命令在這裏提示咱們須要加分號並須要在 api.ts 中成功導出 interface。前者可使用 rome lint index.ts --fix 直接來修理(不會在 api.ts 中添加分號);後者須要手動修理,可是提供了十分完善的友好提示。
rome compile 命令將使用一組默認轉換來編譯文件。因爲在開發中,當前此命令沒有用於指定轉換子集的選項。使用這條命令後,輸出的結果已經沒有了 interface 的存在。
$ rome compile index.ts # import {getData, Params} from './api'; # # async function setData() { # const param = { # username: 'hylerrix', # token: 'ningowood', # }; # const {success, data} = await getData(param); # console.log('success:', success); # console.log('data:', data); # } # # await setData();
rome parse 命令將解析文件並輸出格式精美的 AST。
$ rome parse index.ts # Program { # comments: Array [] # corrupt: false # diagnostics: Array [] # directives: Array [] # filename: 'project-rome/index.ts' # hasHoistedVars: false # mtime: 1_586_498_633_476.8486 # sourceType: 'module' # syntax: Array ['ts'] # body: Array [ # ImportDeclaration { # source: StringLiteral {value: './api'} # namedSpecifiers: Array [ # ......
除了官網展現的幾個命令外,從源碼能夠看出還有不少內置的命令正在開發,能夠從 rome --help 中尋找答案。
# 分析並輸出文件的依賴 $ rome analyzeDependencies index.ts # 把 JavaScript 打包爲一個文件 $ rome bundle index.ts dist # 啓動 Web 服務器 $ rome develop # 計算文件路徑 $ rome resolve index.ts # 安全依賴,運行 Linter 和測試 $ rome ci # 運行測試 $ rome test # ...restart/start/status/stop/web # ...config/publish/run/evict/logs/rage
通過近幾年的蓬勃發展,JavaScript 早已再也不侷限於「前端開發」的領域中。所以本篇寫做的角度並非僅僅之前端開發爲主體探索,而是將 JavaScript 自己抽離出來,這也是本身逐步理清職業發展的一個重要改變。
本文經過學習和寫做分享對 Rome 進行了簡要的瞭解,但這還僅僅是入門。本身對 Babel 自己並不熟,還有不少學習過程當中產生的疑惑都沒法如今進行合適的解答,好比「Rome 和 Babel 的具體異同」、「如何看待 Rome 倉庫使用 Git 跟蹤 Node Modules」、「Rome 替代現有工具或進行集成方案的具體原理」以及「Rome 的打包流程有何特色」等,挖個坑能夠一塊兒交流。
不管最終是否使用 Rome,能引起對 JavaScript 工具鏈的從新思考也會頗有收穫。
最後,感謝你的閱讀,公衆號(@ningowood) 及配套羣聊歡迎加入,同時歡迎給如期更新了三期,即將支持線上 UI 界面瀏覽並提供更多拓展功能的「開源愛好者月刊(@ningowood/open-source-magazine)」倉庫點個 Star 吧!(Github 很久沒漲粉絲了,也歡迎關注我~)