【GAS】webpackでテンプレートを作成してGit/Local開発しよう

# Google Apps Scriptの開発をローカルで行うためにWebpackによる自作テンプレート作成をしてみる

今回はWebpackを使った自作テンプレート作成をやったことない人向けのチュートリアルになります。
ちゃんと調べて真面目な記事のつもりですが、Webpack歴1週間くらいの人間が書いているので間違ってそうなところは編集リクエストお願いします。。。:pray:

## 想定している記事の読者層

GoogleAppsScriptはとても便利なスクリプトの実行環境なので、Qiitaの記事でも人気で本日付けで1713の記事が公開されていますね。

とても便利なので**Webからさくっと作成して**、そのまま放置している人も多いと思います。またプロダクトとして公開していたり、**社内BotでSlack通知させていたり**と、結構がっつり使っているのにGit管理されていないであったり、**社内の誰からもレビュー受けていない状態**になっていませんか？

であるので以下のような考えを持つかたはこの記事が参考になるかと思います。

- **ウェブ上でGAS**を書いたことがある人 && **プロダクトで使っている**
- **GitHub/GitLabでコードを管理したい**、レビューしてもらいたい
- どうせなら**npmモジュール**使ったり**ES6**とかTypescriptで書きたい（GASはES５までの構文のみ）

また自分はVue-cliとかでなんかいい感じにWebpackとかBabelがコンパイルしてくれるのでしっかり自分で定義とかしたことなかったので、そのあたりをテンプレート作成を通して学んでいきたいと思います。

なので、以下のような人が読むと、Webpac筋のレベルがあがるのではないでしょうか。

- Vue.js/Reactとか使っているときに**Webpackを使っているはず**だけど、設定したことない
- **babel?トランスパイル？？**なにそれ食べれるの。

## 参考にさせてもらった記事

#### [Google Apps Script のモダンな開発環境を求めて](https://kenchan0130.github.io/post/2018-12-03-1)

こちらの記事は2018年末におけるGAS開発のいろんなものを丁寧にまとめてくださっているので、３往復くらい読ませていただきました。

#### [Google Apps Script をローカル環境で快適に開発するためのテンプレートを作りました - Qiita](https://qiita.com/howdy39/items/0e799a9bfc1d3bccf6e5)

今回作ろうと思ったテンプレートの一番参考になった記事です。

2018/03/28 - gas-clasp-starter という Google Apps Script を ローカル環境で開発するためのテンプレートを作りました。 2018年に登場した、google/clasp をベースに webpack, TypeScript, TSLint, Prettier, Jest を利用したテンプレートになっています。

## 開発
### グローバルに必要なパッケージ

今回コードはyarnのサンプルなのでnpmでやるかたは適宜読み替えてください。

```shell-session
$ node -v
v12.4.0
$ npm -v
6.9.0
$ yarn -v
1.16.0
$ clasp -v
2.1.0
```

webpack-cliと、claspはGoogleが提供しているGAS用のSDK的なものなのでインストールとログインしておく。

```shell-session
npm i @webpack-cli/init -g
npm i @google/clasp -g
clasp login
```

### Webpack設定

```
mkdir qiita-gas-template
cd qiita-gas-template/
```

```shell-session
yarn add gas-webpack-plugin webpack webpack-cli @webpack-cli/init --dev
npx webpack-cli init
# A name parameter is required to create a storage
```

`npx` は `./node_modules/webpack-cli/bin/cli.js` 的なパスをわざわざ叩かなくても `npx webpack-cli` だけで実行できる魔法のスクリプトです。気になるかたは調べてみて。

nameエラー出るので `package.json` に `"name": "qiita-gas-template"` を追加修正します。

```package.json
{
  "name": "qiita-gas-template", 
  "devDependencies": {
```

もう一度 `webpack-cli` で初期化します。

```shell-session
npx webpack-cli init

ℹ INFO  For more information and a detailed description of each question, have a look at: https://github.com/webpack/webpack-cli/blob/master/INIT.md
ℹ INFO  Alternatively, run "webpack(-cli) --help" for usage info

? Will your application have multiple bundles? No
? Which will be your application entry point? src/index
? In which folder do you want to store your generated bundles? dist
? Will you use one of the below JS solutions? ES6
? Will you use one of the below CSS solutions? No
 conflict package.json
? Overwrite package.json? overwrite
```

選択肢は適当に必要なものを選んでください。Typescript使いたい人はここでES6じゃなくてTS
があるはず。

initのときいろいろハマったので、ハマった人はこのあたり読むといいかも
https://github.com/webpack/webpack-cli/blob/master/INIT.md

次に `package.json` を確認して、実行コマンドを確かめます。

```package.json
  "scripts": {
    "build": "webpack",
    "start": "webpack-dev-server"
```

`yarn build` でコンパイルしてくれそうですね。

```shell-session
$ yarn build
# index.html
# main.xxxx.js
```

`? In which folder do you want to store your generated bundles? dist` 先程 `dist` というディレクトリをコンパイル先に指定したので確認してみます。


```shell-session
$ tree -L 2 dist
dist
├── index.html
└── main.55d8ca80f289d8387835.js
```

`index.js` が作られてほしかったのに違う感じになったので一旦削除する。

```shell-session
rm -fr dist/
```

index.jsが作られないのでwebpackの設定を確認する。

```webpack.config.js
// html系のプラグインはいらないので
// 代わりに最初にyarnで追加したGAS用のwebpackプラグインを入れる
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 削除
plugins: [new webpack.ProgressPlugin(), new HtmlWebpackPlugin()], // HtmlWebpackPluginだけ削除
// それぞれの行を以下の用に書き換える
const GasWebpackPlugin = require('gas-webpack-plugin'); // 追加
plugins: [new webpack.ProgressPlugin(), new GasWebpackPlugin()], // GasWebpackPluginを追加。

output: {
  filename: 'index.js', // ここをindex.jsに書き換える
  path: path.resolve(__dirname, 'dist')
},
```

最終的には以下のような感じになっていると思う。

```webpack.config.js
const path = require('path');
const webpack = require('webpack');
const GasWebpackPlugin = require('gas-webpack-plugin');
module.exports = {
	mode: 'development',
	entry: './src/index.js',

	output: {
		filename: 'index.js',
		path: path.resolve(__dirname, 'dist')
	},

	plugins: [new webpack.ProgressPlugin(), new GasWebpackPlugin()],

```

コンパイルが成功するか確認する。

```shell-session
$ yarn build
$ tree -L 2 dist
dist
└── index.js
```

`dist/index.js` が作られてそうです！

```src/index.js
// dist/index.jsじゃないよ！ src/index.jsだよ！
global.doGet = () => {
  return HtmlService.createHtmlOutput("<h1>Getのサンプル</h1>");
}
```

と書き換える。

```shell-session
yarn build
```

コンパイルすると

```dist/index.js
// (いろいろ長いのでこのあたりまで省略)
eval("global.doGet = function () {\n  return HtmlService.createHtmlOutput(\"<h1>Getのサンプル</h1>\");\n};\n\n//# sourceURL=webpack:///./src/index.js?");
```

`() => {}` が `function(){}` に変換されているので問題なさそうですが、全部evalの中で見にくいですね。

```webpack.config.js
entry: './src/index.js',
devtool: false, // ここの行を追加する
```

ちょっと今回は以上の設定を追加してみます。

```shell-session
yarn build
```

webpackのその他の記述がたくさんあるので、必要なところだけ見ると

```dist/index.js
// (略)

/* WEBPACK VAR INJECTION */(function(global) {global.doGet = function () {
  return HtmlService.createHtmlOutput("<h1>Getのサンプル</h1>");
};

// (略)
```

いい感じにコンパイルされてそうですね。

### GoogleAppsScriptの設定

次にGASアプリ作成する。standaloneを選ぶ。

```shell-session
clasp create --rootDir dist
# loginから求められるかも。
# appsscript.jsonが作成される
# https://script.google.com/d/xxxxxx/edit という文字列が出力される
# こちらの画面からコードが編集できるようになる
```

間違えて `--rootDir dist` をつけ忘れたらclaspからpushするときにルートディレクトリをすべてpushしようとするので修正が必要です。 `.clasp.json` にrootのオプション `{"rootDir": "dist","scriptId": "xxx"}` を追記して、 `appsscript.json` を `dist/` 以下に配置しなおします。

```shell-session
$ clasp push
> no such file or directory, open 'dist/appsscript.json'
```

`dist/appsscript.json` がないって怒れれたので移動させる。

```shell-session
mv appsscript.json dist/appsscript.json
clasp push
```

`clasp push` 二回目以降は `? Manifest file has been updated. Do you want to push and overwrite? Yes` って聞かれるので上書きしたげる。

https://script.google.com/home ここから確認するか
https://script.google.com/d/<.clasp.jsonのscriptIdをここにいれる>

どっちかから確認してみましょう

<img width="681" alt="スクリーンショット 2019-06-22 0.38.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/1fdfbaa8-59ab-5dfd-3475-a793de290a35.png">

アップロードされていたらOK◎

公開>ウェブアプリケーションとして導入>導入

<img width="694" alt="スクリーンショット 2019-06-22 0.32.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/b9071382-7331-7bda-78b3-c6febcd30464.png">

で 表示される `https://script.google.com/macros/s/xx_それっぽいコード_xx/exec` にアクセスすると。

<img width="740" alt="スクリーンショット 2019-06-22 0.38.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/94c73898-da1f-d737-ef83-422167c2ebf7.png">

と表示されてたらOKです！！！

### テンプレート化する

```shell-session
rm .clasp.json
rm -rf dist/
rm -rf node_modules/
```

gitignoreを追加しとく

`vim .gitignore`

```.gitignore
/node_modules
/test/js
/test/browsertest/js
/test/fixtures/temp-cache-fixture
/benchmark/js
/benchmark/fixtures
/examples/**/dist
/coverage
/dist
.DS_Store
*.log
.idea
.vscode
.eslintcache
package-lock.json

# 参考 https://github.com/webpack/webpack/blob/master/.gitignore
# distも保存しないほうがいいと思うので追加
```

テンプレートを含む `gas-dev` みたいなルートディレクトリに `qiita-gas-template` をいれて、それをコピーして使い回せば完成です。

```shell-session
$ tree -L 1 
.
├── gas-dir
└── qiita-gas-template
```

以降はこのディレクトリをコピペしてそのディレクトリで以下のコマンドを実行して、GASを作りまくる感じです。

```shell-session
cp -r qiita-gas-template gas-dir
cd gas-dir
yarn
clasp create --rootDir dist
yarn build
clasp push
// https://script.google.com/home
```

以上、テンプレート作成でした。

あとはGitHub/GitLabのプライベートレポジトリとかでレビューしてもらったりいろいろやってみてください。

```shell-session
$ clasp pull                                                                                                                                           (git)-[master]
Could not find script.
Did you provide the correct scriptId?
Are you logged in to the correct account with the script?
```

共同編集者（GASに権限のないアカウント）がpullしようとするとエラーがでるので、GAS画面上から共有して編集権限を与えてあげてください。
（Git管理を至上として、 `clasp pull` は禁止にしたほうがいいと思いますが）

`yarn` して `yarn deploy` をすれば、別の人も開発に加われると思います。

### もっといいテンプレートを使う

[Google Apps Script をローカル環境で快適に開発するためのテンプレートを作りました - Qiita](https://qiita.com/howdy39/items/0e799a9bfc1d3bccf6e5)

最初の方に紹介したテンプレートのほうが出来がいいのでこちらを使うことを推奨します。
いまWebpack周りを解説したので多分コード読めるようになっていると思います。

具体的にはTypescriptを導入しているんですよね。

```package.json
  "devDependencies": {
    "@types/google-apps-script": "0.0.53",
    "@types/jest": "24.0.13",
```

なので `Logger.lo` とかまで打つと、VSCodeとかだとタイプヒンティングでGAS上で開発するときに必要なGASのAPIが表示されたり、かなりローカルの開発効率があがります。

<img width="602" alt="スクリーンショット 2019-06-22 0.53.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/b904422d-f8c1-08dd-1632-890c28ead199.png">

今回私が作ったテンプレートでも、以下のような感じでごにょっとpackegeとか追加してあげると、タイプヒンティング自体は動くようになるので、（たぶん、、）ちょっとやってみるといいかもしれません。howdy39さんのコードとか読んでみるといいかも。。。（力つきた。）

```shell-session
yarn add --dev typescript @types/google-apps-script ts-loader
touch tsconfig.json
mv src/index.js src/index.ts
```


## 最後に

今回はGASをローカルで開発する方法とGit管理しようぜって話と、テンプレート作ってみようぜ！って話をすべて込めてみました。最後まで読んでいただきありがとうございました！


.