webworkerを含むnpmパッケージの作り方（仮）

### はじめに
先日、Virtual背景のnpmパッケージを作成しました。これは自分史上初めてのnpmパッケージ作成でした。
今度は、以前作成したマルチバーコードリーダのバーコード検出部位(SemanticSegmentation)をパッケージ化してみることにします。

このバーコード検出部位はwebworkerを用いて作っていたのですが、いろいろ検索してもwebworkerを用いたパッケージの作成方法がわからない。試行錯誤で作ってとりあえず動くところまで行ったので、その内容を備忘録も兼ねて記載します。
なお、試行錯誤の末動かしたので、正式なやり方になっているかは結構不安です。もし変なところがあれば是非ともご指摘のほどお願いします。

なお、今回パッケージ化するものは、大きな画像を分割してセマンティックセグメンテーションを行うもので、分割数をパラメータ化することで、精度と速度のトレードオフを可能にするものです。スマホなどの性能が限定されているデバイス上で使うと効果的かなと考えています。

挙動はこんな感じです。画面を分割するとSegmentationの精度が上がるが、速度が下がるという感じになっています。
![movie2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/425fef2a-7eef-9bc4-21e0-3960a4c8154e.gif)


### 前提
- npmのアカウントは作っておく。
- githubのアカウントとリポジトリは作っておく。
- 作成したgitリポジトリをクローンして、そのディレクトリに移動している状態。


### 作業の流れ
まずはじめに、npmの環境設定とログインをしておきましょう。

```
$ npm set init.author.name "xxxxxx"
$ npm set init.author.email "xxxx@xxxx.co.jp"
$ npm set init.author.url "https://qiita.com/wok"
$ npm adduser
```

それでは、プロジェクトの初期化とTypeScriptの開発で必要なパッケージとwebpackをインストールします。

```
npm init -y
npm install --save-dev webpack webpack-cli
npm install --save-dev typescript ts-loader
npm install -D tsconfig-paths
npm install -D rimraf npm-run-all
```

tsconfig.jsonを初期化し、必要な項目を設定します。
今回は、型宣言とソースマップ生成を有効にします。また、出力先を`./dist`にします。
ソースマップの生成は不要な方は作成しないで良いと思います。

```
npx tsc --init

$cat tsconfig.json
<snip>
"declaration": true, 
"sourceMap": true, 
"outDir": "./dist",

```

次にwebpackを設定します。
ここで`(1)entry`と`(2)filename`の部分をパッケージ名と同じ名前にしています。
これは試行錯誤の結果の一つなのです。今回worker-pluginを用いてwebworkerを作成したのですが、どうしても一つのファイルにwebworkerを入れ込むやり方がわかりませんでした[^1]。結果、webworkerのスクリプトは別ファイルとして作成して、インストールする際に手動でpublic直下に置いてもらうことにしています。（インストールスクリプトは提供します。）worker-pluginを用いてwebworkerを作成すると、メインのスクリプト名を引き継ぐようで、例えば`0.scalable-semantic-segmentation.worker.js`のようなファイル名でファイルが作成されます。このファイル名がインストール先のpublic内のファイル名となるべく同じにならないように、今回はメインのスクリプト名をパッケージ名としています[^2]。(^_^;)

[^1]: インライン化すればできそうですが、文字列化の際のエスケープが沼っぽいので諦めました
[^2]: より具体的にはwebworkerを用いるパッケージを２つインストールできるように。


```js
$ cat > webpack.config.js
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './src/scalable-semantic-segmentation.ts', // <-- (1)
    resolve: {
        extensions: [".ts", ".js"],
    },
    module: {
        rules: [
            { test: /\.ts$/, loader: 'ts-loader' },
        ],
    },
    output: {
        filename: 'scalable-semantic-segmentation.js', // <-- (2)
        path: path.resolve(__dirname, 'dist'),
        libraryTarget: 'umd',
        globalObject: 'typeof self !== \'undefined\' ? self : this'
    },
};

```

このwebpackの設定に合わせて、package.jsonのmainも変更します。また、scriptも設定しておきます。

```
  "main": "dist/scalable-semantic-segmentation.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "webpack": "npx webpack --config webpack.config.js",
    "clean": "rimraf dist/*",
    "build": "npm-run-all clean webpack"
  },
```

次にworker-pluginをインストールします。

```
npm install -D worker-plugin
```

worker-pluginを有効化するように、webpack.config.jsを編集します。
(1)と(2)の部分を追記します。

```
const path = require('path');
const WorkerPlugin = require('worker-plugin'); // <--- (1)
module.exports = {
    mode: 'development',
    entry: './src/scalable-semantic-segmentation.ts',
    resolve: {
        extensions: [".ts", ".js"],
    },
    module: {
        rules: [
            { test: /\.ts$/, loader: 'ts-loader' },
        ],
    },
    output: {
        filename: 'scalable-semantic-segmentation.js',
        path: path.resolve(__dirname, 'dist'),
        libraryTarget: 'umd',
        globalObject: 'typeof self !== \'undefined\' ? self : this'
    },
    plugins: [                                  // <--- (2)
        new WorkerPlugin()
    ]
};
```

上述のとおり、今回はwebworkerをメインのスクリプトとは別のスクリプトとして作成しています。
これをpublic直下(webのルート直下)にコピーする必要があります。この処理を行うスクリプトを作成してbinディレクトリに置きます。

```js

const fs = require('fs');
const path = require('path');
const package_name = 'scalable-semantic-segmentation-js'
const src = path.join('node_modules', package_name, 'dist', '0.scalable-semantic-segmentation.worker.js')
const dst = path.join(process.argv[2], '0.scalable-semantic-segmentation.worker.js')

try {
    fs.copyFileSync(src, dst, fs.constants.COPYFILE_EXCL);
    console.log('file is copied');
  } catch (error) {
    console.log(error);
  }
```

あとは、`.gitignore`と`.npmignore`を作成しておきます。

```
$ cat > .gitignore
*~
*#
node_modules
dist

$ cat > .npmignore
*~
*#
node_modules
demo
src

```

あとは、ソースコードを作成して、ビルドして、publishして完了です。

```
npm publish
```

## 成果物
https://www.npmjs.com/package/scalable-semantic-segmentation-js

お疲れ様でした。＞自分

次回はここで作成したScalableSemanticSegmentationのパッケージの使ったデモの開発を説明する予定です。

では、また。