npmパッケージを公開していざ使おうと思ってもimportできなかったので調べたこと

[こちらの記事](https://qiita.com/mitsuyacider/items/56e8a692c2977ec1fed3)でも書きましたが、アメーバのような形状をp5.jsで作り、[generator](https://github.com/mitsuyacider/p5-ameba-generator)も公開しました。この記事はこれを機にこのアメーバーの生成をnpmパッケージにして公開してみようと思い、公開方法を調べた記録です。HowToではないです。つまづいた点などの記録ですので、作り方などは私も参考にしていた以下の方の記事が良いと思います。

##### 参考
[初めてのnpm パッケージ公開](https://qiita.com/TsutomuNakamura/items/f943e0490d509f128ae2)
[3分でできるnpmモジュール
](https://qiita.com/fnobi/items/f6b1574fb9f4518ed520)

## 公開用のフォルダ
パッケージの作り方を参考にして、npmにアカウント登録とpackage.jsonにライブラリ情報を記入し、`npm publish`で公開するところまでいけました。公開するフォルダの中身はwebpackでアプトプットしたデータです。package.jsonに以下を記入すると、プロジェクトディレクトリの中のデータが、例えば他のプロジェクトで`npm install p5-ameba`とインストールした時にnode_modulesに入ってきます。

```json:package.json

...
  "files": [
    "README.md",
    "lib"
  ]
...

```
上の例だと、README.mdとlibフォルダにあるデータですね。なので、使い手が`import ameba from 'p5-ameba'`とした時に必要なデータを書き込めば良いと思います。この`p5-ameba`はパッケージの`name` fieldで決められるので、`lib`フォルダの中に`p5-ameba.js`のようなファイルを用意する必要はないです。それで、`npm install p5-ameba`とした時に読み込む`jsファイル`は、package.jsonの`main` fieldで指定します。

```json:package.json
...
　"name": "", <- npm install するときの名前
  "main": "", <- importした時に読み込むファイル
  "files": [] <- node_modulesに入るファイルを指定
...
```

## 公開してもimportで読み込めない
なんとかパッケージを公開してみて、いざ使ってみようと思い、別プロジェクトで実際に`npm install p5-ameba`とし、`jsファイル`から`import ameba from p5-ameba`とやってみました。すると、以下のエラーが出て読み込みができません。

```
Uncaught SyntaxError: Unexpected identifier
```

開いた口が塞がりません。。。まだまだフロントエンドの勉強中なので、おそらく何かの知識が抜け落ちてるか、何となくの知識で使っている**何か**が原因だと思います。ただ、用語が多すぎたりでまだ自分の技術にできていません。仕方がないので、少しづつ原因を追っていきました。

## 確認作業
* 開発環境で配布したライブラリが本当に使えているのかを確かめる
　-> 使えた
* 配布したライブラリを、新しいプロジェクトで直接読み込んでみる -> 使えた(要は開発環境と同じ）
* 配布したライブラリを、`node_modules`から読み込む -> エラー

つまり、node_modulesにある状態だとなぜか読み込まれないことが分かりました。ほかのnpmパッケージのライブラリは`import`しても使えているので、`node_modules`にデータを置く過程か、データそのものに原因がありそうです。作成したライブラリは`ES2015`で書いているため、もしかしたら`babel`とか`webpack`も含めてこの辺りの知識の曖昧さが原因なのでは？と思い調べてみました。怪しいのはwebpackでアウトプットしたデータです。

## 他のパッケージの作りを研究
作成したアメーバーのアニメーションライブラリは、[`animejs`](https://github.com/juliangarnier/anime)というライブラリの構成をかなり参考にしています。初期化時にパラメータを指定するなど。なので、このanimejsのパッケージはどのような作りになっているのか調べました。

**Usage**

- ES6 modules
- CommonJS
- File include

パッケージの使い方で、ライブラリの使用方法が上記の3方法あります。このうち、CommonJS...???と恥ずかしながら私はこの用語を知りませんでした。。。少し調べてみると、

**CommonJS**

- モジュール管理の規格のひとつ。
- `require(<モジュール名>)`でライブラリを読み込む仕様。
-  [この記事](https://qiita.com/rooooomania/items/4c999d93ae745e9d8657)が参考になりました。
-  node.jsでも使用する場合は必須のよう

これを踏まえてanimejsのpackage.jsonを見てみると作りは以下になっています。


```json:animejsのpackage.jsonの一部
  "umd:main": "lib/anime.min.js", 
  "module": "lib/anime.es.js", 
  "main": "lib/anime.js", 
  "files": [ 
    "lib" 
  ], 
```

`main`に`umd:`とついてますが、**UMD**とはUniversal Module Definitionの略で、以下のように定義されています。([GitHub](https://github.com/umdjs/umd)より抜粋）

> The UMD pattern typically attempts to offer compatibility with the most popular script loaders of the day (e.g RequireJS amongst others). In many cases it uses AMD as a base, with special-casing added to handle CommonJS compatibility.

また不明な用語、、、AMDとはAsynchronous Module Definitionの略で、モジュールを非同期で読み込むため規格のようです。CommonJSの規格を非同期で読み込むための仕様。。。かな。それともう一つ、**module** fieldがあり、これの読み込み先のファイルがES2015基準のファイルを指していました。[npmの仕様](https://docs.npmjs.com/files/package.json)には

>The main field is a module ID that is the primary entry point to your program. That is, if your package is named foo, and a user installs it, and then does require("foo"), then your main module’s exports object will be returned.
This should be a module ID relative to the root of your package folder.
For most modules, it makes the most sense to have a main script and often not much else.

このように書かれてあり、どうやらmain fieldにはCommonJS規格のファイルを指定する必要があるようです。ではこの`module`fieldは何だろうとしらべてみると、[こちら](https://dwango-js.github.io/performance-handbook/startup/module-field/)に詳しく書いてありますが、

>"module"フィールドに書くファイルパスは名前のとおりECMAScript モジュール形式のコードで、このコードはimport/exportのままのコードを配置します。webpackなどのbundlerは"module"フィールドが存在する場合はそちらを優先して読み込みます。

ES2015形式で書かれたコードのファイルを指定するfieldのようです。

## CommonJS用にライブラリを書き出す
ライブラリを作成したのはES2015形式なので、これをCommonJSのファイルとして扱えるように書き出す必要があります。webpackでoutputしただけのファイルは、ライブラリのモジュールとしては使えないようです。やはりこれが読み込みができない原因と言えそうです。

#####ライブラリ用にビルドする
私が使っていたwebpackの設定はjsなどのアセットをまとめて、ブラウザで読み込める形に書き出すことはやりますが、ライブラリ用のモジュールとして書き出すことはしません。webpackでもライブラリ用にビルドする設定はあると思いますが、他にも調べてみると**Rollup**というモジュールバンドラがライブラリ開発に適しており、CommonJS形式に変換できるようです。基本的に[こちらの記事](https://qiita.com/karak/items/9bc75ee0bc3db37a001d)を参考にすると変換までできます。記事元ではアウトプットのフォーマットが`cjs`とCommonJSのみの変換になっていたので、これでも良いと思いますが、今回私が参考にした`animejs`のパッケージは`umd`規格のファイルになっていたので、私もこれに習って`umd`規格のファイルを作りました。

```javascript:rollup.config.js
export default {
    input: './lib/ameba.es.js',
    output: [{
      file: './lib/ameba.js',
      format: 'umd',
      name: "p5-ameba"
    }],
    external: [
        'p5'
      ]
  };
```

formatのfieldのプロパティを`umd`に変えただけです。これでビルドして、CommonJSファイルを作り、ようやくnpmパッケージが使えるようになりました。

## Conclusion
まだまだフロントエンドは分からないことが多く知らない用語がたくさんあります。それでも調べながらコツコツやっていくとレベルアップできるのではないかと感じました。このamebaライブラリはあまり役に立つものではないですが、パッケージ配布までの全体像が掴めたことは自分にとっては収穫です。その中で知らない単語を少しずつ吸収していけました。

ちなみに、ライブラリを開発していく中で、作ったライブラリを試すのにイチイチ`npm publish`とやって公開してから別プロジェクト作って`npm install p5-ameba`とやって確認作業していましたが、開発中のライブラリを擬似的に`node_modules`に入れて確認できるようです。

```bash:配布予定のライブラリを入れる/外す
npm link p5-ameba   <- node_modulesにいれる
npm unlink p5-ameba　<- node_modulesから外す
```

今回作成したnpmパッケージは[こちら](https://www.npmjs.com/package/p5-ameba)です。
[GitHub](https://github.com/mitsuyacider/p5-ameba)にもあります。

![sample.gif](https://qiita-image-store.s3.amazonaws.com/0/117549/e4718e4d-d021-b9ce-458a-a1e4a0b3e4a8.gif)