여러 Package를 한 Repository에서 관리한다.
git과 npm을 활용하여 mono-repo와 workflow를 최적화하는 도구다.
npm install -g lerna
mkdir {폴더 이름}
cd {폴더 이름}
// (Fixed/Locked mode)
lerna init
// (indepedent mode)
lerna init --independent)
Fixed/Locked mode (기본값)
independent mode
lerna bootstrap
는 lerna에서 자주 사용하는 명령어 중 하나로 패키지들 내에 공통 모듈에 symbolic link
를 걸어 중복 설치를 방지한다. 여기서 -hoist
옵션을 사용하면 가장 많이 사용되고 있는 의존성 모듈이 루트 node_modules
로 hoisting
된다. 이 명령어를 생략할 수 있는 것이 바로 yarn workspaces다.
yarn workspaces란 monorepo 내 멀티 패키지 의존성 관리를 도와주는 툴이다. 중복 모듈을 한 곳에 모아 관리할 수 있고, 공유 모듈의 참조가 쉬워진다는 것이 장점이다. lerna에선 npm을 패키지 관리 도구로 사용하고 있지만, npmClient를 yarn으로 설정하면 yarn을 이용할 수 있다. 다만 패키지별 버전 관리 및 배포에 용이한 것은 yarn보다 lerna이기 때문에 프로젝트의 배포와 버전관리는 lerna, 패키지 의존성 관리는 yarn으로 하는 것이다. (참고로 lerna도 yarn workspaces를 사용했다고 한다.)
npmClient
, useWorkspaces
설정 값 추가"npmClient": "yarn",
"useWorkspaces": true,
// "packages" # 배포와 관련된 packages 명시할 때 사용
workspaces
위치 추가{
"private: true" // 작업 영역은 publish목적이 아니므로 해당 코드 필수로 추가
"workspaces": [
"packages/*/*"
]
}
yarn
명령어를 입력하면, 루트의 package.json
과 각 패키지의 package.json
에 명시되어있는 dependency가 최대한 중복을 줄인 상태로 루트의 node_modules
안에 호이스팅되어 설치된다. 이 때, dependency로 명시되어있는 모듈은 symbolic link
가 연결되어 최신 버전이 아니라 로컬에서 의존하는 버전을 기준으로 설치된다.
https://classic.yarnpkg.com/blog/2018/02/15/nohoist/
예를 들어 package-1는 A와 ‘A를 의존성 모듈로 이용하는’ C를 의존성 모듈로 이용하고 있다. 동일한 의존성을 지니는 모듈을 중복 설치할 필요가 없기 때문에 함께 쓸 수 있도록 루트의 node_modules
로 hoisting
된다.
https://classic.yarnpkg.com/blog/2018/02/15/nohoist/
기본적으로 모노 레포의 각 패키지는, 위의 방식처럼 node_modules
를 가지고 dependency
를 관리한다. 그렇지만 여러 패키지에서 사용되는 모듈은 중복설치하지않고 루트의 node_modules
로 hoisting
하여 모듈을 공유하게된다.
can’t find module “B@2.0” from project root “monorepo” (not able to follow symlink)
can’t find module “A@1.0” from “package-1” (unaware of the module tree above in “monorepo”)
yarn 공식 문서에 따르면, 아직 yarn workspace에서 Dependency Module Hoisting 관리는 불안정한 상태라고 한다. 프로젝트 내에서 build할 때 위와 같은 에러메세지가 나타나는데, 그 이유는 특정 모듈이 해당 로컬 node_modules
만 참조하려하기 때문이다. 루트 node_moduls
로 hoisting된 모듈은 로컬 node_modules
에는 존재하지 않아서 can’t find
에러가 발생한다.
https://classic.yarnpkg.com/blog/2018/02/15/nohoist/
"workspaces": {
"nohoist": ["react-native", "react-native/**"]
}
nohoist
옵션을 사용하면 로컬 node_modules
에 설치되어 can’t find
에러를 피할 수 있다.
//some-dep 전역환경 설치
npm install -g some-dep
//some-dep으로 이동해서 다음 명령어 실행하면, global symlink 생성
npm link
//my-app으로 이동해서 다음 명령어 실행하면, some-dep의 global symlink를 my-app에서 사용
npm link some-dep
이미지 내 출처 표시 - fhinkel
//my-app으로 이동해서 다음 명령어 실행하면, 링크 제거 및 패키지 삭제. 두 개는 같은 명령어다.
npm unlink --no-save some-dep
//or
npm uninstall --no-save some-dep
//some-dep으로 이동해서 다음 명령어 실행하면, global symlink 삭제
npm uninstall
//root dependency 관리
yarn add <패키지명> -W (or --ignore-workspace-root-check)
yarn remove <패키지명> -W (or --ignore-workspace-root-check)
//package별 dependency 관리
yarn workspace <workspaceName> add <패키지명>
yarn workspace <workspaceName> remove <패키지명>
프로젝트가 git과 연결되어있는 경우, lerna 프로젝트의 패키지 버전관리 및 발행한다.
lerna publish: 마지막 릴리즈 이후 변경된 패키지 게시
lerna publish from-git: 현재 커밋에 태그가 지정된 패키지를 명시적으로 게시
lerna publish from-package: 레지스트리에 최신버전이 없는 패키지를 명시적으로 게시
변경사항이 존재하는 패키지의 version을 변경하고 changelog를 생성한다.
lerna version
hidden:true
하게 되면 해당 type은 changelog에 기록되지 않음"command": {
"version": {
"allowBranch": "release/*",
"conventionalCommits": true,
"changelogPreset": {
"name": "conventional-changelog-conventionalcommits",
"types": [
{
"type": "feat",
"section": ":rocket: New Features",
"hidden": false
},
{
"type": "fix",
"section": ":bug: Bug Fix",
"hidden": false
},
{
"type": "docs",
"section": ":memo: Documentation",
"hidden": false
},
{
"type": "style",
"section": ":sparkles: Styling",
"hidden": false
},
{
"type": "refactor",
"section": ":house: Code Refactoring",
"hidden": false
},
{
"type": "build",
"section": ":hammer: Build System",
"hidden": false
},
{
"type": "chore",
"section": ":mega: Other",
"hidden": false
}
]
}
}
}
}
lerna version --conventional-commits
"publish": {
"allowBranch": ["release/*"],
"conventionalCommits": true,
"message": "chore: publish",
}
//true면 배포 불가능, 보통 root package.json이 true 설정
❌
"private": "true"
//prefix에 package가 속한 github organization을 소문자 문자열로 설정
❌
"name": "my-app1"
✅
"name": "ongddree/my-app1"
// 부가정보
"license": "(MIT OR Apache-2.0)"
// "UNLICENSED"는 타인에게 비공개 패키지의 사용 권한을 부여하지 않을 때 사용.
"license": "UNLICENSED"
"author": "ongddree"
"description": "ongddree's my-app1"
"directory": "packages/@my-app1"
"repository": {
"type": "git",
"url": "https://github.com/ongddree/my-app1"
},
"publishConfig": {
"registry": "https://npm.pkg.github.com/"
},
$ npm config set //npm.pkg.github.com/:_authToken {token}
npm.pkg.github.com/:_authToken={PERSONAL_ACCESS_TOKEN}
@ong:registry=https://npm.pkg.github.com
//npm install 전에 preBuild
frontend:
phases:
preBuild:
commands:
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
- npm install