株式会社スマレジの開発部でスマレジのサーバサイドを作っています

Redmine APIをGASで実行して、ガントチャートを同期する(5: webpackを導入し、複数のツールを1つのシートで使えるようにする)

こんばんは!株式会社スマレジ、開発部のmasaです。

スマレジでも全体的なテレワークが前回のブログのすぐあとから導入され、僕も自宅で作業をしています。

僕の仕事内容の中には、他部署との折衝をする部分もあるので、コミュニケーションコストは在宅のほうが圧倒的に高くなるなぁと感じています。チャットよりも、直接やり取りしたほうが早いことも多いので(-_-;)

管理職の方はこの辺りがもっと大変そうですね・・・。 この苦労とその対策をしっかりと記録して、After Coronaの世界でも戸惑わないでやっていけるようにしたいですね。

さて、今回はwebpackのお話です。

複数のツールを同時に開発して、一つのスプレッドシートで使う

社内ツールは、だれか(例えばAさん)が一つ使えるモノを作ると、その後の運用やほかのニーズに合わせて、他の人(例えばBさん)が手を入れたくなることはよくあると思います。

マネジメントの観点で言っても、すでに環境が決まっている&構築済みである分、ゼロからツールを別に作るよりも開発コストは低いので、 ムダな工数を省く意味でも、ガンガン追加改修で対応してもらいたいでしょう。 (これをやりすぎるとAP基盤系のナレッジがAさんに集中してしまうので、属人化のリスクがあるのが悩ましいところですが・・・。社内ツールのドキュメントとなると、書かない・書いても雑なことも多いでしょうし。)

今回は、上記のようなユースケースを想定して、webpackを利用したツール管理を考えます。

構成図の変更

以前、下記の記事で作るツールの構成図を記載したと思います。 masa2019.hatenablog.com

その時の構成図↓

f:id:masa2019:20200322192812p:plain
以前のツールの構成図

これは、あくまで一つのアプリケーションの実を想定した構成になっており、今回のようなニーズには対応できていません。 なので、下記のように構成を変更します。

f:id:masa2019:20200503222957p:plain
Front Controllerの導入

各ツール(図内のApp 1やApp 2のこと)を振り分けるFrontController層を作って、そこでツールの呼び分けを行う恰好です。

GAS上では、standaloneとwebapi以外ではonOpenイベントハンドラを使う感じですね。詳しくは↓の記事をご覧ください。

www.yukibnb.com

webpackでツールごとにビルドファイルを作るようにする。

次に各ツールごとにwebpackでコンパイルしたときに、ツールごとにビルドファイルを吐き出すようにwebpack.config.jsを記載します。 この時のプロジェクトのファイル構成は↓の画像の通り。

f:id:masa2019:20200503230507p:plain
ファイル構成。ctmフォルダ配下に各ツールのメタ情報を記載する。

ctm(Clasp Tool Managerのつもり...)の配下に、各ツールに関する情報を記載していくイメージです。 取り急ぎは、app.jsonにツール名と各ツールのエントリーポイントを下記のように記載しておきます。

{
    "tool_a": "./src/tool_a/index.ts",
    "tool_b": "./src/tool_b/index.ts"
}

また、概念図のFrontControllerもbuild内に含みます。これは、clasp pushする際のディレクトリがbuildに指定されているためです。

最後に、app.jsonを読み込むように、webpack.config.jsを下記のように記載します。

const path = require('path');
const fs = require('fs');
const toolList = JSON.parse(fs.readFileSync("./ctm/resources/app.json", 'utf8')); // ここで読み込んで・・・

module.exports = {
  mode: 'none',
  entry: toolList, // ここでbindする
  module: {
    rules: [
        {
          test: /\.tsx?$/,
          use: 'ts-loader',
          exclude: /node_modules/,
        },
      ],
  },
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].bundle.js'
  },
  resolve: {
    modules: [path.resolve(__dirname, "src"), "node_modules"],
    extensions: [".js", ".json", ".jsx", ".css", ".ts"]
  }
};