will and way

ただの自分用メモを人に伝える形式で書くことでわかりやすくまとめてるはずのブログ

TypeScriptでGoogle App Scriptを実装する環境を作る

TypeScriptは型をもっていてJSに変換できる言語みたいな感じでしょ?くらいの経験値しかないですが、Google App Scriptをスクリプトエディタでずっとやっていてつらみを感じたので、
ローカル開発環境を作りつつ、TypeScriptに手を出してみようと思いました。

nodeを実践的に使ったこともTypeScriptも書いたことなかったので、備忘録としてまとめてみる。

TL; DR

browserifyやらbabelでGoogle App Scriptランタイムに変換してあげればOK

GitHub - matsuokah/gas-launchpad

Google App Scriptをローカルで開発する環境を作るメリット

  • gitで履歴を追跡できる
  • モジュール化
  • テストがしやすい
  • JSではなくTypeScriptで開発できる
  • IDEの恩恵が受けられる

個人的にはモジュール化や設計もできるならやりたかったので、やりやすさが増すというのはありがたい

Google App Scriptのランタイムを理解する

  1. もろもろのimport周りが使えない
  2. ES5(?)
  3. DOM操作はできない。consoleなどで使えるwindowオブジェクト等もない。(それらはブラウザのグローバル変数)という認識出会っているだろうか...

つまり、TypeScriptで開発したいなら、ES5にして1ファイルにしてあげるのが一番よさそう。

TypeScriptをGASにまとめる手順

  1. TypeScriptをJS(ES5)に変換する
  2. プロジェクト内の依存関係をGAS使える形にする

使ったもの

  • gulp -> JSのビルドフローを定義する
  • browserify -> 1ファイルにまとめる
  • tsify -> browserifyで解決した依存関係をJavaScriptに変換する.ここでtsconfig.jsonが読み込まれる
  • baberify -> require, import構文をランタイムに依存しない形式に変換する(?)
  • gasify -> browserifyGASが使える形式に変換する

基本的にはbrowserifyの恩恵を受けて、プラグインで変換していく感じ。

これらを使うことで、プレーンなTSをGASにアップロード可能なJSに変換ができるようになりました。

gulpfile.js

TS > JSの変換部分

const outputFile = 'main.js';
const distributionDirName = 'dist';

gulp.task('build:ts', () => {
    browserify({
        entries: './src/app.ts'
    })
        .plugin('tsify')
        .transform('babelify')
        .plugin('gasify')
        .bundle()
        .pipe(source(outputFile))
        .pipe(gulp.dest(distributionDirName));
});

あとは、tsを書くだけ。

src/core/hello.ts

export function world(): void {
    Logger.log("hello");
}

src/core/world.ts

export function world(): void {
    Logger.log("world");
}

src/app.ts

import {hello} from "./core/hello";
import {world} from "./core/world";

declare namespace glFunctions {
    interface global {
        main(): void;
    }
}

declare var global: glFunctions.global;

global.main = () => {
    hello();
    world();
};

ここが重要

declare namespace glFunctions {
    interface global {
        main(): void;
    }
}


declare var global: glFunctions.global;

global.main = () => { }

app.tsに記述したglFunctionsのglobalに対してmainのような関数を定義し、実装をつけてあげることで、GAS化した時の関数候補にすることができます。

この場合だと、mainという関数がコンボボックスの関数候補に現れるようになります。

f:id:matsuokah:20180707151646p:plain

Google App Scriptで使えるGsuit系のオブジェクトの補完を有効にする

package.jsonのdevDependencyに下記を入れてあげる。そうするとtsで補完が効くようになる。頭いい。

    "@types/google-apps-script": "0.0.24",

リポジトリ

基本的に下記の’リポジトリをテンプレートとして使えば、幸せになれるはず!

github.com

参考

【GoogleAppsScript】ES6を使ったGoogle Apps Scriptの開発 - Qiita

anyenvのndenvを使ってnode.jsのバージョンを管理する - Qiita

gulp + browserify + tsifyを利用してTypeScriptコンパイル環境を作る - $shibayu36->blog;

GitHub - naoki-sawada/gas-typescript-webpack: This is an example of writing Google Apps Script in TypeScript and building with webpack