使用lerna&yarn工作区创建一个单一存储库

学习与学习

在过去的几年中, 单一存储库的概念成功确立,因为它可以大大简化开发模块化软件项目(例如基于微服务的基础结构)的过程。 这种架构方法的主要优点在实践中显而易见,因此,我建议从头开始创建自己的测试单一存储库 ,同时了解使用纱线工作区lerna的细微差别。 好吧,让我们开始吧!

考虑我们项目的结构,这将是位于packages /文件夹中的三个库以及根目录中的package.json

├── package.json └── packages ├── app │  ├── index.js │  └── package.json ├── first │  ├── index.js │  └── package.json └── second ├── index.js └── package.json 

可以理解,我们有两个独立的库firstsecond ,还有一个应用程序库,它将从前两个库导入函数。 为了方便起见,将所有三个软件包都放在packages目录中。 您可以将它们保留在根文件夹中,也可以将其放置在任何其他名称的目录中,但是为了遵循普遍接受的约定,我们将以这种方式放置它们。

为了简化实验, 第一个库和第二个库在index.js中仅包含一个函数,每个函数都代表模块返回一个hello字符串。 对于第一个示例,它将如下所示:

 // packages/first/index.js const first = () => 'Hi from the first module'; module.exports = first; 

应用程序模块中,我们将在控制台中显示来自应用程序的消息Hi ,以及来自其他两个软件包的问候:

 // packages/app/index.js const first = require('@monorepo/first'); const second = require('@monorepo/second'); const app = () => 'Hi from the app'; const main = () => { console.log(app()); console.log(first()); console.log(second()); }; main(); module.exports = { app, main }; 

为了使第一个第二个应用程序中可用,我们将它们表示为依赖项中的依赖项

另外,对于每个库,我们在包的主要名称前面的值名称中将前缀@ monorepo /添加到本地package.json

 // packages/app/package.json { "name": "@monorepo/app", "version": "1.0.0", "main": "index.js", "license": "MIT", "dependencies": { "@monorepo/first": "^1.0.0", "@monorepo/second": "^1.0.0" } } 

为什么在软件包名称npm(@ monorepo /)前面需要带有狗形图标的前缀?
添加前缀是可选的,但这恰恰是许多单一存储库遵循软件包命名约定babel
材质ui等。 事实是,每个用户或组织在npm网站上都有自己的作用域 ,因此可以保证所有带有@ somescope / postfix的模块都是由somescope团队创建的,而不是由攻击者创建的。 此外,可以调用已使用的模块名称。 例如,您不能仅仅采用并创建自己的utils模块,因为这样的库已经存在 。 但是,添加后缀@ myscopename /我们可以与二十一点和年轻女士一起获得utils( @ myscopename / utils )。

对于我们的测试项目,现实生活中的类似物可以是用于处理数据,验证工具,分析或仅一组UI组件的各种库。 如果我们假设要开发一个Web移动应用程序(例如,分别使用ReactReact Native ),并且我们拥有重用逻辑的一部分,那么可能值得将其放入单独的组件中,以供以后在其他项目中使用。 将其添加到Node.js上的服务器,您将获得一个真实的案例。

纱线工作区


创建完整的mono- 存储库之前的最后一步是在存储库根目录中设计package.json 。 注意工作空间属性-我们指定了packages / *的值,这意味着“ packages文件夹中的所有子项”。 在我们的例子中,这是app第一第二

 // package.json { "name": "monorepo", "version": "1.0.0", "main": "packages/app/index.js", "license": "MIT", "private": true, "workspaces": [ "packages/*" ] } 

另外,必须在package.json中指定“ private”:true ,因为工作空间仅在私有项目中可用。

为了使一切顺利,请从根目录中执行yarn命令(类似于yarn installnpm install )。 实际上,由于app模块中存在的依赖项被定义为根package.json中的 工作区 ,因此,我们不会从npm-registry下载任何内容,而只是绑定(“链接”)我们的包。

 yarn 

图片

现在我们可以执行node命令 从根目录运行,该根目录将从packages / app / index.js文件运行脚本。

 node . 

图片

让我们看看它是如何工作的。 通过调用yarn ,我们在node_modules中创建了指向 packages文件夹中目录的符号链接。

图片

由于依赖关系之间的关系,我们获得了一个很大的优势-现在,当在第一个第二个模块中进行更改时,我们的应用程序将收到这些程序包的当前版本,而无需重建。 实际上,这非常方便,因为 我们可以进行软件包的本地开发,但仍将它们定义为第三方依赖项(它们最终将成为第三方依赖项)。

纱线工作区获得的下一个重要优点是存储第三方依赖项的组织。

了解有关在顶层存储依赖项的更多信息。
假设我们要在第一第二个中使用lodash库。 通过从适当的目录中执行yarn add lodash命令 ,我们将收到对本地package.json的更新-软件包的当前版本将显示在dependencies中
 "dependencies": { "lodash": "^4.17.11" } 

至于lodash软件包本身 -实际上,该库将在根级别一次安装在node_modules中。
如果外部软件包的所需版本(在我们的示例中为lodash )在第一和第二个不同(例如, 首先您需要lodash v3.0.0 ,在第二个v4.0.0中 ),则具有较低版本( 3.0.0 )的软件包将到达根node_modules第二个模块的lodash版本将存储在本地包/ second / node_modules中
除了优点之外,该方法还可以具有较小的缺点,该纱线允许在附加标志的帮助下绕过纱线 。 您可以在官方文档中了解有关此类细微差别的更多信息。

添加莱娜


使用lerna的第一步是安装软件包。 通常,他们执行全局安装( yarn global add lernanpm i -g lerna ),但是如果不确定是否要使用此库,则可以使用npx进行调用。

我们将从根目录初始化lerna

 lerna init 

图片

实际上,我们借助一个命令一次执行了多个操作:创建了一个git存储库(如果之前未初始化过),创建了lerna.json文件并更新了根package.json

现在,在新创建的lerna.json文件中添加两行- “ npmClient”:“ yarn”“ useWorkspaces”:true 。 最后一行说我们已经在使用yarn工作区 ,不需要创建app / node_modules文件夹,其中有指向第一个第二个的符号链接。

 // lerna.json { "npmClient": "yarn", "packages": [ "packages/*" ], "version": "1.0.0", "useWorkspaces": true } 

Lerna测试


为了显示使用lerna的便利性, 为我们的库添加测试。
从根目录,我们安装用于测试的软件包-jest 。 运行命令:

 yarn add -DW jest 

为什么需要-DW标志?
需要-D(-dev)标志,以便将jest软件包作为dev依赖项安装,而-W标志(-ignore-workspace-root-check)允许在根级别进行安装(这是我们需要的)。

下一步是将一个测试文件添加到我们的软件包中。 为了方便我们的示例,我们将使所有测试相似。 对于第一个示例,测试文件将如下所示:

 // packages/first/test.js const first = require('.'); describe('first', () => { it('should return correct message', () => { const result = first(); expect(result).toBe('Hi from the first module'); }); }); 

我们还需要添加一个脚本来在每个库的package.json中运行测试:

 // packages/*/package.json ... "scripts": { "test": "../../node_modules/.bin/jest --colors" }, ... 

最后一步是更新根package.json 。 添加一个测试脚本,该脚本将调用lerna run test --streamlerna run之后的参数定义了将在package /文件夹中的每个软件包中调用的命令,并且--stream标志将使我们能够在终端中查看结果的输出。

结果,根目录中的package.json将如下所示:

 // package.json { "name": "monorepo", "version": "1.0.0", "main": "packages/app/index.js", "license": "MIT", "private": true, "workspaces": [ "packages/*" ], "scripts": { "test": "lerna run test --stream" }, "devDependencies": { "jest": "^24.7.1", "lerna": "^3.13.2" } } 

现在,要运行测试,我们只需要从项目的根目录运行命令:

 yarn test 

图片

Lerna的版本升级


lerna可以应付的下一个流行任务是更新软件包版本。 想象一下,在实施测试之后,我们决定将库从1.0.0升级到2.0.0。 为此,只需在根package.json脚本字段中添加“ update:version”:“ lerna version --no-push”行,然后从根目录中运行yarn更新:version 。 添加了--no-push标志,以便在更新版本后,更改不会发送到远程存储库,而lerna默认情况下会执行此操作(不带此标志)。

结果,我们的根package.json将如下所示:

 // package.json { "name": "monorepo", "version": "1.0.0", "main": "packages/app/index.js", "license": "MIT", "private": true, "workspaces": [ "packages/*" ], "scripts": { "test": "lerna run test --stream", "update:version": "lerna version --no-push" }, "devDependencies": { "jest": "^24.7.1", "lerna": "^3.13.2" } } 

运行版本更新脚本:

 yarn update:version 

接下来,将要求我们选择要切换到的版本:

图片

通过单击Enter,我们将获得更新版本的软件包列表。

图片

我们输入y确认更新,并收到有关更新成功的消息。

图片

如果我们尝试运行git status命令,则该消息什么也没有提交,工作树为clean ,因为 lerna version不仅会更新软件包版本,还会创建一个git commit和标记来指示新版本(本例中为v2.0.0)。

与lerna版本团队合作的功能
如果在根package.json脚本字段中添加“ version”:“ lerna version --no-push”行,而不是“ update:version”:“ lerna version --no-push”行 ,则很可能会遇到意外行为,并且红色控制台。 事实是,默认情况下, npm-scripts在更新软件包版本后立即调用version命令(保留脚本),从而导致递归调用lerna version 。 为了避免这种情况,只需为脚本指定其他名称即可,例如update:version ,就像我们在示例中所做的那样。

结论


这些示例显示了lerna纱线工作区结合的所有可能性的百分之一。 不幸的是,到目前为止,我还没有找到有关使用俄语的单一存储库的详细说明,因此我们可以假设这已经开始了!

链接到测试项目存储库。

Source: https://habr.com/ru/post/zh-CN448766/


All Articles