Gazelle
2020年10月03日更新 1561 Views

npm moduleを作成してローカル環境とgithubでprivateに使う

同じコードを複数のアプリでそれぞれ持つと、修正をアプリ毎に行わなければならず面倒である。そこで各アプリで共通して使えるコードをnpmモジュールとして切り出し、一元管理を行いたい。本ページでは

  • npmモジュールの作成方法
  • npmモジュールをローカルから取り込む方法
  • npmモジュールをgithub private repositoryにpushして、取り込む方法

を試してみる。

npmモジュールを作成

sayHello関数のみを持つmoduleAを取り込み、その関数を叩くだけのappAを作成する。コンソールろぐに"Hello World"が出力されたら終わり。

まず最初に大元のアプリappAを作成する。

appAを作成

$ mkdir appA
$ cd appA
$ npm init

これでpackage.jsonだけまず作成される。package nameはappaとしておく。

moduleAを作成

同様の方法でmoduleAも作成。package nameはmoduleaとしておく。ちなみに最後のaは大文字にしたかったが、package名は小文字にしろと怒られてしまう。

また、index.jsも作成しておく。package.jsonに"main": "index.js"という記述があるが、index.jsがモジュールをロードする時のエントリーポイントとなる。すなわちappAからmoduleAを利用する時はappAのindex.jsを参照することになる。

index.jsはnodeのcommonJSの記法で次のようにsayHello関数をexportする。

module.exports = {
  sayHello: () => {
    console.log('Hello World');
  }
};

これでnpmモジュールは完成である。index.jsとpackage.jsonのみ。

npm install 相対pathでローカルのnpmモジュールを取り込み

npmモジュールの取り込みは簡単で下記のコマンドをappAのrootで叩くだけである。

$ npm install ../moduleA

するとpackage.jsonにdependenciesが追加されて下記のようになる。

{
  "name": "appa",
  "version": "1.0.0",
  "description": "just say hello from module",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "modulea": "file:../moduleA"
  }
}

また、node_modules以下にmoduleaが取り込まれていることも確認できる。

実際に使えるかを確認するためappA側のindexjs側に下記を記述

const moduleA = require('modulea');
moduleA.sayHello();

nodeコマンドを叩いてみると無事Hello Worldが出力される事が確認できた。

$ node index.js
Hello World

.npmignoreが効かない点は注意

ブラックリスト形式で、npm installする時に不要に含まれてしまうファイルを除いてくれる.npmignoreというファイルがある。

通常、このファイルをmoduleのルートディレクトリにおいておけばよろしく不要ファイルを除いてinstallを行ってくれるのであるが、localからinstallする時は上手く行かなかった。

これはStack Overflowでも質問として上がっている。バグなのか、仕様なのかはよく分からないが気を付けよう。ちなみに次に紹介するnpm linkでもこの問題は同様である。

ローカルモジュールのアンインストールはmodule名で

インストールの逆で、npm uninstall ../moduleAとしてもアンインストールはできない。module名を直接していしてアンインストールを行ってやろう。

$ npm uninstall modulea

これでnode_module及びpackage.jsonからmoduleaの記述が消える。

npm linkでより効率的に開発できるかも

npmでローカルのモジュールを取り込むと、相対パスが記述されてしまい。今後github等に移行する場合にはその記述を消す必要がある。また相対パスを指定するのも面倒だったりする。

より手軽な方法としてはnpm linkがある。

使い方としてはmodule側のルートでnpm linkとまず打つ。

$ npm link 
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN modulea@1.0.0 No description
npm WARN modulea@1.0.0 No repository field.

up to date in 0.312s
found 0 vulnerabilities

C:\Program Files\nodejs\node_modules\modulea -> C:\Users\tsuto\webapp\temp\npmtest\moduleA

次にappAのルートに行き、モジュール名を叩く

$ npm link modulea
C:\Users\tsuto\webapp\temp\npmtest\moduleA\node_modules\modulea -> C:\Program Files\nodejs\node_modules\modulea -> C:\Users\tsuto\webapp\temp\npmtest\moduleA

これで完了である。

良く見るとただのシンボリックリンクであり、図示すると次のようになる。
image.png

ln -sとか使ってシンボリックリンク貼ってもできるが、npm linkだと面倒なコマンドを叩かずによろしくやってくれるところが良い。

ちなみにlinkを解きたい時は

$ npm unlink modulea

でできる。

package.jsonの中身を変更せずにローカルのmoduleを参照できるところが肝である。

相対パスのnpm installも実のところシンボリックリンク

"dependencies": {
  "modulea": "file:../moduleA"
}

でnpm installした時にnode_modules以下を見るとmodulea -> /c/Program Files/nodejs/node_modules/modulea/とあるので、相対パスによるnpm installも結局のところシンボリックリンクをしているだけである。

よって .npmigonreは当然無視されるし、moduleAを書き替えると別にnpm installをしなくても更新されている

github private repoからmoduleaを取り込む

まずPrivate Repositoryをgithubで作成する。
image.png

httpsでcloneしても良いが、毎回ユーザ名とパスワードを聞かれるのも面倒であるため、SSH Keyを登録しておこう。Putty Genなどでopen ssh形式のキーペアを作成してgithubに登録すれば良いが、登録方法は別の記事に任せるとする。

SSHで登録できたら下記でcloneして、そのRepositoryにmoduleAのファイルを移してpushしておく。

git clone git@github.com:tomsongazeru/moduleA.git

次にappAからgithub private repositoryに置いたmoduleAを取り込むためにappAのルートで下記を叩く

npm install git+ssh://git@github.com:tomsongazeru/moduleA.git

package.jsonが更新されていることも確認できる。

{
  "name": "appa",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
+  "dependencies": {
+    "modulea": "git+ssh://git@github.com/tomsongazeru/moduleA.git"
+  }
}

gitからインストールされるファイルは.npmignoreファイルがローカルの場合と異なりきちんと反映される。

ブランチを変更してnpm install

dependenciesの記述の最後に#ブランチ名を付ける事で、特定ブランチで作成したnpm moduleを取り込むことができる。

たとえば新たにdevelopブランチを作成した場合、package.jsonにdevelopを作成して再度npm installをしてやれば良い。

"dependencies": {
  "modulea": "git+ssh://git@github.com/tomsongazeru/moduleA.git#develop"
}

ローカル開発かgithub repositoryどちらがおすすめ?

github repositoryではmodule側でpushして、app側でpullするという手間が掛かるため迅速なトライアンドエラーが難しい。よってappAもmoduleAもどちらも開発するプロジェクトならば、基本的にnpm linkで開発し、保存や共有目的としてgithub repositoryを使うと良いだろう。

関連記事

yumでインストールしても古いVersionのnodeしか入らないので最新を入れるための方法をサクッと解説
2020年08月09日
ホームへ戻る