< Back

【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はES5までの構文のみ)

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

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

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

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

Google Apps Script のモダンな開発環境を求めて

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

Google Apps Script をローカル環境で快適に開発するためのテンプレートを作りました - Qiita

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

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

開発

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

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

$ 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的なものなのでインストールとログインしておく。

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

Webpack設定

mkdir qiita-gas-template
cd qiita-gas-template/
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 で初期化します。

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 でコンパイルしてくれそうですね。

$ yarn build
# index.html
# main.xxxx.js

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

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

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

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()],

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

$ 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>");
}

と書き換える。

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, // ここの行を追加する

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

yarn build

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

dist/index.js
// (略)

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

// (略)

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

GoogleAppsScriptの設定

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

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.jsondist/ 以下に配置しなおします。

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

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

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をここにいれる>

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

スクリーンショット 2019-06-22 0.38.25.png

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

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

スクリーンショット 2019-06-22 0.32.47.png

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

スクリーンショット 2019-06-22 0.38.14.png

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

テンプレート化する

rm .clasp.json
rm -rf dist/
rm -rf node_modules/

gitignoreを追加しとく

vim .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 をいれて、それをコピーして使い回せば完成です。

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

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

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のプライベートレポジトリとかでレビューしてもらったりいろいろやってみてください。

$ 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

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

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

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

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

スクリーンショット 2019-06-22 0.53.05.png

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

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

最後に

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

.