nagashigaki

フロントエンドや釣りで気になったことを記していきます。

Google App Scriptをモダンな開発っぽくする環境を作ってみた(ES6, browserify, eslint, mocha)

f:id:takuyantakuyan:20170408135601p:plain
お前は今更何を言ってるんだ?というテーマですが整理するために。
Google App Script(以下GAS)とはjavascriptでGoogle App(今ではG suite?)の豊富な機能を堪能でき、ある程度の作業を自動化することができます。しかも無料で!指定時間に勝手にスクリプトを起動することもできちゃいます。
例えば、複数のcsvをDriveにアップしてボタンをポチっとするだけで目的のデータが揃い、そのデータをメールで飛ばしたりできるのは非常にありがたいです。

導入する環境

このGASの機能をもっと使いこなしたい。具体的には

  • npmで管理
  • es6
  • gulp
  • browserifyでモジュール化
  • eslintでコードチェック
  • Mocha&Chaiでテスト

環境を整えるにあたって以下のサイトを参考にさせて頂きました。この記事がなかったら途中で挫折してしまったかも・・・
tech.speee.jp

上記参考サイトはDriveからGoogle Apps Scriptを新規作成するStandaloneタイプのスクリプトですが、今回は
「直感的にSpreadSheetから簡単に操作したい(*´∀`*)」
ということで、onOpenなどのトリガーが使えるSpreadSheetに紐付いたスクリプト(Container Bound Script)で作成します。つまりは、SpreadSheetのメニューのスクリプトエディタで起動させるスクリプトです。
この辺りはqiita.comに詳しく解説されてます。
参考サイトのようにwatchして自動的にスクリプトをアップロードするというスマートな方法はできず、コンパイルしてペタペタとスクリプトエディタに貼り付ける開発ですね。めんどくさいですが、いつも保存するたんびにコンパイルをしてるわけでもないので目をつむります。
では前提条件が整ったところで、早速環境を作っていきましょう!(*´ω`*)

環境を整える

f:id:takuyantakuyan:20170408140330p:plain
windowsなら日本語のパスがないようにCの中に開発ファイル作るのがおすすめです。node.jsベースになるので、入れてない方はまずnode.jsをインストール。

npmとgitの初期化
$ npm init
$ git init

gitも使うので今のうちに初期化

babel
$ npm install --save-dev babel babel-core babel-preset-es2015 babel-register 

gulpfileなど設定ファイルもES6にしたいのでpackage.jsonに

"babel": {
  "presets": [
    "es2015"
  ]
}

と記入。

gulpまわり
$ npm install --save-dev gulp gulp-babel gulp-exec gulp-plumber

globalにgulpを入れてない人は、globalにもインストール。使い慣れてる人はお好みのプラグインいれてください!

browserify

とりあえずbrowserifyとそれを使うためのvinylをインストール。恥ずかしながら、いつもwebpackだったのでbrowserifyはじめてさわった(^_^;)

$ npm install --save-dev browserify vinyl-source-stream

GAS用のbrowserifyプラグイン、gasifyがあるのでそれもインストール。こいつのおかげで、結合したファイルでもスクリプトエディタ上で起動する関数が認識されます。

$ npm install --save-dev gasify

つぎにbabelifyを導入するのですがここに落とし穴があります。現状最新のbabelifyではエラーが発生してしまうことがあります。自分の環境でもエラーが発生しました。ということで、今回はbabelify5系を指定してインストールします。

$ npm install --save-dev babelify@5.0.5
テストツール

テストフレームワークはmocha、アサーションライブラリはchaiを使います。

$ npm install --save-dev mocha chai gulp-mocha
eslint

最初の状態だと SpreadsheetAppなどを使ったときに
「こんなの定義されてないよヽ(`Д´)ノプンプン」
と怒られるので、GAS用のeslintプラグインのeslint-plugin-googleappsscriptもインストール。

$ npm install eslint eslint-plugin-googleappsscript

.eslintrcはpluginの利用と、globalも利用するのでだいたい以下のような感じになります。また、Atomを使っているのでgulpにeslintをチェックせず、コーディング中に確認します。

{
  "plugins": [
    "googleappsscript"
  ],
  "env": {
    "googleappsscript/googleappsscript": true,
    "es6": true,
    "mocha": true
  },
  "globals": {
        "global": true
    },
  "parserOptions": {
      "ecmaVersion": 6, 
      "sourceType": "module"
    },
  "rules": {
   //ルール
  }
}

適宜.eslintignoreにeslintを適用しないものを追記してください。

ファイル構成
┌─ dest コンパイル先
├─ src 開発フォルダ
└─ test test用フォルダ

で開発してますので、それに合わせます。

gulpfileの作成

ES6で書きたいので、gulpfile.babel.jsを

import gulp from 'gulp';
import browserify from 'browserify';
import source from 'vinyl-source-stream';
import mocha from 'gulp-mocha';

const compileFile = 'main.js';

gulp.task('dest', () => {
  browserify({
    entries: ['src/'+compileFile]
  })
  .transform('babelify')
  .plugin('gasify')
  .bundle()
  .pipe(source(compileFile))
  .pipe(gulp.dest('dist'));
});

gulp.task('test', () => {
  gulp.src('test/**/*.js', {read: false})
      .pipe(mocha({
        reporter: 'spec',
        compilers: 'js:babel-core/register'
      }));
});

gulp.task('default', ['test', 'dest'], () => {});

最小限これだけで大丈夫です!!

お疲れさまでした

これで環境は整えました。ちなみに最終的なpackage.jsonは

{
  "name": "gas",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel": "^6.23.0",
    "babel-core": "^6.24.0",
    "babel-preset-es2015": "^6.24.0",
    "babel-register": "^6.24.0",
    "babelify": "^5.0.5",
    "browserify": "^14.3.0",
    "chai": "^3.5.0",
    "gasify": "^0.1.0",
    "gulp": "^3.9.1",
    "gulp-babel": "^6.1.2",
    "gulp-exec": "^2.1.3",
    "gulp-mocha": "^4.3.0",
    "gulp-plumber": "^1.1.0",
    "mocha": "^3.2.0",
    "vinyl-source-stream": "^1.1.0"
  },
  "babel": {
    "presets": [
      "es2015"
    ]
  }
}

なので、足りない部分はコピペしてインストールしてください。

開発する

簡単なスクリプトを書いてみます。

src/main.js

import hello from './hello';

global.main = () => {
  const name = SpreadsheetApp.getActive().getName();
  Logger.log(hello(name));
};

global.xxx にすることでスクリプトエディタがxxxを認識し起動させることができます。

src/hello.js

export default function hello(name) {
  return 'hello '+name+'!';
}

test/helloTest.js

import {assert} from 'chai';
import hello from '../src/hello';

describe('hello', () => {
  it('should get hello world! when name is world', () => {
    assert.equal(hello('world'), 'hello world!');
  });
});

これでgulpとコマンドをうつと、テストを通してスクリプトエディタに貼り付ける用のスクリプトを吐き出します。
うまくできましたか??
貼り付けると、スクリプトエディタで保存すると関数を自動認識するので、それを起動してください!

雑談

最近、GASのエンジンが

  • const
  • let

をサポートしたのを知りました。でもObject.assignはサポートされてない・・・
対応リストがほしい・・・