[node] lerna를 사용할 때 서브 모듈의 node_modules/.bin 의존성 해결하기
2020 / 06 / 12
lerna를 사용해 멀티 모듈 node 프로젝트를 구성한 뒤 lerna link convert
를 실행하면 devDependencies가 root의 packages.json에 모이게 됩니다. 개별 모듈에는 런타임 의존성(package.json의 dependencies)만 남습니다.
이렇게 모든 서브모듈에서 동일한 개발 의존성을 갖도록 할 수 있지만 문제가 하나 생기는데, 기존에는 개별 모듈의 node_modules/.bin
에 있던 실행 파일들이 사라지고 프로젝트 root의 node_modules/.bin
에 해당 파일들이 생성된다는 것입니다.
node에서 의존성을 찾을 때 현재 디렉토리의 node_modules
에 존재하지 않는 모듈은 상위 디렉토리의 node_modules
에서 찾게 됩니다. 그러나 실행파일은 해당 모듈의 node_modules/.bin
에서만 찾기 때문에 tsc
, next
등을 실행할 수 없게 됩니다.
그래서 시도한 해결책은, root의 node_modules/.bin
의 실행 파일들을 개별 모듈들이 복사해가기. 실행 파일들은 대부분 .js
파일의 심볼릭 링크(Symbolic link)라서 그냥 복사해버리면 실행이 안됩니다. .js
파일 하나만 덩그러니 생겨버리고 해당 파일이 의존하는 다른 모듈을 찾지 못하기 때문입니다.
따라서 그냥 복사하면 안 되고 심볼릭 링크 상태를 유지하면서 복사해야 합니다. 게다가 이 링크들은 상대 경로 (relative path)를 가지고 있기 때문에, 링크 상태를 유지하면서 복사하더라도 또 실행이 안됩니다. 상대 경로는 그대로 가져왔는데 해당 링크의 디렉토리는 달라졌으니 기존의 타겟 파일을 못 찾게 되는 것입니다.
고맙게도 superuser.com에 상대경로의 심볼릭 링크를 타겟을 잃지 않으면서 복사하는 방법이 있었고, 이를 바탕으로 상위 디렉토리의 node_modules/.bin
에서 심볼릭 링크들을 복사해오는 쉘 스크립트를 작성했습니다.
# run in the directory which has node_modules.
ROOT_BIN_DIR=../../../node_modules/.bin
THIS_BIN_DIR=node_modules/.bin
mkdir -p node_modules/.bin;
for i in $ROOT_BIN_DIR/*; do
ln -s ../../$ROOT_BIN_DIR/$(readlink $i) $THIS_BIN_DIR/${i##*/};
done
위 스크립트는 서브 모듈이 3단계 아래 있는 상태에서 작동하는 스크립트입니다. ${i##*/}
는 /
를 구분자로 삼아 변수 i의 값을 split한 뒤에 가장 마지막 토큰(즉 파일명)을 취한다는 의미입니다.
root/
├node_modules/.bin
└dir1/
└dir2/
└here/
└node_modules/.bin
위 스크립트를 root/dir1/dir2/here
에서 실행하면 root/node_modules/.bin
의 심볼릭 링크들을 타겟을 잃지 않으면서 복사해옵니다. 스크립트의 ROOT_BIN_DIR
의 ../../../
부분을 해당 서브모듈의 depth에 따라 조절하면 됩니다.
lerna bootstrap
으로 초기 의존성을 잡아준 뒤에 개별 모듈마다 위 스크립트를 실행해 root의 node_modules/.bin
을 복사해오면 직면했던 문제가 해결됩니다. 깔끔.