条条大路通 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 很久没涨粉丝了,也欢迎关注我~)