🤷‍♂️

GASのV8ランタイムをOFFにしてもArray.find等を利用したい

2021/10/05に公開

タイトルの通りです。
GASはV8エンジンに対応してだいぶ便利になりましたが、一方でいくつか不具合のような挙動をすることが知られています。

今回は最初のリンクにあるように以下の条件に一致していました。

  • V8ランタイムが有効である。
  • ライブラリにdeveloperモードのものがある。
  • スクリプトのオーナー以外のユーザーによる実行である。

作成したライブラリは、利用者が意識せずに変更を加えられるように常に最新の状況を見るDeveloperモードに設定する必要がありました。
(逆に言うと、いったん承認しちゃうと権限の変更がない限り、どんな変更してもライブラリの利用者は気が付けないので上記のような制限がかかっているのかもしれませんね。)

clasp + TypeScriptを利用して開発をしていたので、ES3相当のトランスパイルがすでにされていました。

そこで、V8ランライムを無効にして処理を実行したところ、Array.findの関数が存在しないためエラーになってしまいました。
claspのissueにもある通り、一部サポートされていない関数があります。issueにあるspreadsheetで記載されている一覧がわかりやすいです。

Array.findを書き換えるのはさすがに面倒なので、既存のpolyfillのライブラリを利用することにしました。
エラーになっているコードに対して、polyfillを追加してもいいのですが、今後も同様の問題が発生しそうなので、汎用的に使えるようにライブラリにしました。

GASのライブラリを作る

意外とGASのライブラリで公開されているものが少ないので、自分の手で作成することにしました。
コードは以下に置いてあります。

polyfillとしてはcore-jsがよさそうなので利用することにしました。
core-jsを呼び出しているだけなので、一部を抜粋して紹介します。

なお、claspについては以下のリンクなどを参考にしてください

npm init -y

mkdir dist
clasp create --type standalone --rootDir dist
npm install --save core-js
npm install -D webpack webpack-cli
  • ソース。importだけです。
src/index.ts
import "core-js/stable/array";
import "core-js/stable/object";
import "core-js/stable/string";
  • webpackの設定をします。
webpack.config.js
const path = require('path');

module.exports = {
	mode: "development",
  entry: './src/index.js',
	target: ['web', 'es5'],
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
};
  • package.jsonでwebpackを追加します。
  "scripts": {
+    "build": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  • appscript.jsonでV8エンジンを無効にします。
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "DEPRECATED_ES5"
}
  • GASにpushします。
npm run build
clasp push
  • clasp createしたときのURL(.clasp.jsonのscriptId)にアクセスすると、pushしたものが反映されてることが確認できます。

はまったポイント

  • index.tsで import "core-js/stable"; をすれば基本的にすべてのpolyfillを使えるようになりますが、その状態でライブラリを使用すると実行時に Exceeded maximum stack depth のエラーが出ます。
    • おそらくpolyfillの処理が長すぎるために出ているっぽいので、importは一部にしてあげると意図した通りに動作するようになりました。
  • webpack.config.jsのtargetにwebを入れないとアロー関数式になるため、エラーになります。
  • webpack.config.jsのmodeをproductionにしてclasp pushすると axiosError: Syntax error: Invalid property ID. line: 1 file: main のエラーが出て、pushできません。
    • developmentだと成功するのでとりあえずdevelopmentにしています。

Discussion