{"id":13475378,"url":"https://github.com/hisasann/gas-typescript-starter","last_synced_at":"2025-03-20T22:31:56.078Z","repository":{"id":40838888,"uuid":"253748451","full_name":"hisasann/gas-typescript-starter","owner":"hisasann","description":"🍡 Google Apps Script + TypeScript + lerna 🍼","archived":false,"fork":false,"pushed_at":"2023-12-15T05:51:56.000Z","size":874,"stargazers_count":8,"open_issues_count":21,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-17T18:33:48.289Z","etag":null,"topics":["googleappsscript","jest","lerna","monorepo","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hisasann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-04-07T09:33:34.000Z","updated_at":"2023-11-05T10:55:07.000Z","dependencies_parsed_at":"2024-01-16T07:23:34.230Z","dependency_job_id":"6d81b115-88fa-4b23-8214-668187e6b5af","html_url":"https://github.com/hisasann/gas-typescript-starter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hisasann%2Fgas-typescript-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hisasann%2Fgas-typescript-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hisasann%2Fgas-typescript-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hisasann%2Fgas-typescript-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hisasann","download_url":"https://codeload.github.com/hisasann/gas-typescript-starter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244703992,"owners_count":20496224,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["googleappsscript","jest","lerna","monorepo","typescript"],"created_at":"2024-07-31T16:01:19.903Z","updated_at":"2025-03-20T22:31:55.774Z","avatar_url":"https://github.com/hisasann.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# 🍍 gas-typescript-starter 🍩\n\n## Stack\n\n* [Google Apps Script](https://developers.google.com/gsuite/aspects/appsscript?hl=ja)\n* [TypeScript](https://www.typescriptlang.org/)\n* [lerna](https://github.com/lerna/lerna/tree/master/commands/add#readme)\n\n## Prerequisites\n\n以下のモジュールをインストールしておきます。\n\n### google/clasp\n\nUsing npm,\n\n```bash\n$ npm install -g @google/clasp\n```\n\nUsing yarn,\n\n```bash\n$ yarn add global @google/clasp\n```\n\n[google/clasp: 🔗 Command Line Apps Script Projects](https://github.com/google/clasp)\n\n### lerna\n\nUsing npm,\n\n```bash\n$ npm install -g lerna\n```\n\nUsing yarn,\n\n```bash\n$ yarn add global lerna\n```\n\n[lerna/lerna: A tool for managing JavaScript projects with multiple packages.](https://github.com/lerna/lerna)\n\n## Usage\n\n### npm モジュールをインストールする\n\nルートディレクトリで、 npm モジュールをインストールします。\n\n```bash\n$ yarn install\n```\n\npackages の中のモジュール互換をインストールするために、以下を実行します。\n\n```bash\n$ lerna bootstrap\n```\n\n### lerna パッケージを作成する\n\n一般公開する npm module ではないので `private: true` で作成します。\n\n```bash\nlerna create hello --private\n```\n\n### GAS にデプロイする\n\nlerna や WebStorm からの Run だとうまく動かない。\n\nパス的なのが何か問題になっているのかもしれない。\n\nなので、確実なのは `yarn` 経由で実行すること。\n\n```bash\n$ cd packages/xxx\n$ yarn push\n```\n\n### GAS を開く\n\n```bash\n$ cd packages/xxx\n$ yarn open\n```\n\n## Google Apps Script APIの有効化する\n\n```bash\n$ clasp create hello\nError: Permission denied. Enable the Apps Script API:\nhttps://script.google.com/home/usersettings\n```\n\n`clasp create` でエラーがでてしまう場合は、\n\n`https://script.google.com/home/usersettings`\n\nにて、 **オン** にしてあげます。（ぼくはデフォルトはオフでした）\n\n[GAS のGoogle謹製CLIツール clasp - Qiita](https://qiita.com/HeRo/items/4e65dcc82783b2766c03#google-apps-script-api%E3%81%AE%E6%9C%89%E5%8A%B9%E5%8C%96)\n\n## clasp create コマンドを実行する\n\n```bash\n$ clasp login\n$ clasp create hello\nCreated new script: https://script.google.com/d/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/edit\nCloned 1 file.\n└─ appsscript.json\n```\n\nこれで `appsscript.json` （プロジェクトのマニフェストファイル）と `clasp.json` が作成されます。\n\n## clasp open コマンドを実行する\n\nブラウザが起動し、 `Code.gs` ですぐプログラムがすぐに書ける状態になります。\n\n```bash\n$ clasp open\n```\n\n## clasp.json を編集する\n\n`clasp.json` では、 GAS としてのルートパスを指定できるので、ここに ts のビルド後の .js ファイルが置かれる場所をしてしています。\n\n```\n{\n  \"scriptId\": \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\",\n  \"rootDir\": \"./dist\"\n}\n```\n\n## Web アプリとして公開する方法\n\n**doGet** 関数を用意します。\n\n```js\nfunction doGet() {\n  const template = 'myfunction';    // html ファイル名から拡張子を抜いた文字列\n  return HtmlService.createTemplateFromFile(template).evaluate();\n}\n```\n\n次に、 GAS 上のメニューで「Publish」→「Deploy as web app」を選択します。\n\n表示されるダイアログで `Project version` を **New** を選んでバージョンをインクリメントします。\n\nこれをしないと、 `clasp push` したとしても自動的にバージョンアップしてくれないので注意が必要です。\n\n[GASでwebアプリの作成とパラメータの確認方法(doGet、doPost) | BREEZE](https://breezegroup.co.jp/201906/gas-get/)\n\n[Google Apps Script でHTMLファイルを作って表示。 - Qiita](https://qiita.com/taromorimotohf/items/5e52cb9062600e8ccac3)\n\n[Google Apps Scriptを使ってサーバーレスファンクションを作る - Qiita](https://qiita.com/hakaicode/items/0e2bc0e5b5e43ca12b42)\n\n## GAS でのマルチファイルについて\n\n    おそらく、サーバー側ではファイル名順にすべてのスクリプトファイルを結合してから、指定した関数を実行しているものと思われます。\n\n[Google Apps Script（GAS）のプロジェクト内に複数のスクリプトファイルがある場合の実行順序 - Qiita](https://qiita.com/munieru_jp/items/0119ca5ee38caa23b8e4)\n\n## HTML 側から GAS の関数を実行する方法について\n\n### GAS 側の関数を呼び出す方法\n\n```html\n\u003cbutton onClick=\"google.script.run.myFunction()\"\u003eCall myFunction\u003c/button\u003e\n```\n\n### GAS 側の関数を呼び出し、その後コールバック関数を呼び出してもらう方法\n\n```html\n\u003cbutton onClick=\"google.script.run.withSuccessHandler(initializer).getSpreadsheetName('hoge')\"\u003eCall initializer\u003c/button\u003e\n\u003cscript\u003e\n  window.initializer = function initializer(arg) {\n    console.log(`initialized! ${arg}`);\n  };\n\u003c/script\u003e\n```\n\n[GASのWebアプリでクライアント側JavaScriptからサーバー側の関数を呼び出す方法](https://tonari-it.com/gas-web-app-google-script-run/)\n\n## 権限まわりでエラーが出た場合\n\n    Exception: No item with the given ID could be found, or you do not have permission to access it.\n    \n以下の記事のように、\n\n「File」-\u003e「Project properties」-\u003e「Scopes」何もなければ、 OAuth の認証がされていない可能性があります。\n\nその場合は、適当な function を実行して権限を付与するダイアログを出して付与しましょう（この画面もけっこうわかりにくいです）\n\n[GASのエラー”呼び出す権限がありません”はマニフェストファイルに問題あり - Qiita](https://qiita.com/h-sto/items/fdde0905d0a4070d18fc)\n\n## 環境変数の使い方\n\nハードコードしないほうがよさそうなものは、 **Script properties** （環境変数）を使いましょう。\n\n「File」-\u003e「Project properties」-\u003e「Script properties」こちらに KeyValue を入れます。\n\n```javascript\nconst token = PropertiesService.getScriptProperties().getProperty('TOKEN');\n```\n\n[GASをClaspとTypescriptでローカル開発してみた。(Clasp / Typescript / TSLint / Prettier)](https://medium.com/@kosa3/gas%E3%82%92clasp%E3%81%A8typescript%E3%81%A7%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E9%96%8B%E7%99%BA%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F-e7835d1763ed)\n\n[【初心者向けGAS】スクリプトプロパティを操作してそのデータを取り出す方法](https://tonari-it.com/gas-properties-script-property/)\n\n## GAS でライブラリを使う場合\n\nGAS のメニューから追加することもできるが、それだと `clasp push` されたタイミングでクリアされてしまうので、実用的ではなかった。\n\n代わりに、以下のように `appsscript.json` に追加するのが妥当のようでした。\n\n```json\n{\n  \"dependencies\": {\n    \"libraries\": [\n      {\n        \"userSymbol\": \"Moment\",\n        \"libraryId\": \"MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48\",\n        \"version\": \"9\"\n      }\n    ]\n  },\n}\n```\n\n[[GAS]Claspでライブラリを使う方法｜kazuya_saito/イデアルファーロ株式会社 CEO｜note](https://note.com/miraisouzoukan/n/n1dd76f67aaf9)\n\n## GAS を API として JSON を返す方法\n\n[🥔 feat: add read-google-spreadsheet · hisasann/gas-typescript-starter@962e45b](https://github.com/hisasann/gas-typescript-starter/commit/962e45b927467ea108f48e2b92f429a46e5b1a6e)\n\ndoGet のレスポンスを **JSON** にすることで対応可能。\n\n## 複数の Google Form からの送信をひとつの Google Spreadsheet にまとめてシートわける方法\n\nSpreadsheet を Create せずに Select する方法を使うといけました。\n\n## Google Form で送信されたときに自動返信メールを送る方法\n\n基本的には、以下のように `GmailApp.sendEmail` するのだが、問題は Google Form が送られてきたタイミングに Hook する方法です。\n\n```javascript\nGmailApp.sendEmail(送信先アドレス, 件名, 本文, オプション)\n```\n\n[Google Apps Scriptでフォーム送信時にメッセージを送るスクリプトの作り方](https://tonari-it.com/gas-on-form-submit-gmail/)\n\n    Google Form -\u003e Google Apps Scripts -\u003e Trigger（ここで Form と GAS を紐付け）\n\nで作る場合は、 Trigger の部分に Google Form に Hook するプルダウンが出てきますが、 GAS を `clasp push` の場合はでこなかったです。\n\nなので、スクリプト側からトリガーを作りました。\n\n```javascript\nScriptApp.newTrigger(callbackName).forForm(form).onFormSubmit().create();\n```\n\n[[GAS]スクリプトからトリガーをセットする話 - Qiita](https://qiita.com/s_maeda_fukui/items/2fcbd34d7db5e8e7a2b8)\n\n## GAS のクライアントサイドで CSS と JavaScript を実行する方法\n\n[🍵 feat: add to use css and js in html with gas · hisasann/gas-typescript-starter@f11b8e6](https://github.com/hisasann/gas-typescript-starter/commit/f11b8e679bfff2c04ae7946e2b89d86f42069527)\n\n---\n\nGAS の doGet で Web アプリとして使われる `main.html`\n\nmain.html から `HtmlService.createHtmlOutputFromFile('css').getContent();` 経由で呼び出される CSS ファイルの `css.html`\n\nと JavaScript ファイルの `js.html`\n\n---\n\nこれで、 main.html 内から css.html と js.html の中身を include することができます。\n\n[Google Apps ScriptのWebアプリケーションにCSSでスタイル設定をする](https://tonari-it.com/gas-html-css/)\n\n[GASでクライアントJavaScriptを使用する超簡単なプログラム](https://tonari-it.com/gas-button-event-javascript/)\n\n[GASとJavaScriptフレームワークVue.jsを使ってWebアプリを作成するための最初の一歩](https://tonari-it.com/gas-web-app-vue-js/)\n\n## GAS で Spreadsheet の値を絞り込む\n\n```javascript\nfunction myFunction() {\n  var ss = SpreadsheetApp.getActiveSpreadsheet();　//アクティブなスプレッドシートを取得\n  var sh = ss.getActiveSheet();　//アクティブなスプレッドシートのアクティブシートを取得\n  \n  var filter = sh.getFilter();\n  if (filter !== null) {\n    sh.getFilter().remove(); //フィルタをオフにする\n  }\n\n  sh.getRange(1,1,5,2).createFilter();\n\n  var filterRange = sh.getRange(2,1,sh.getLastRow(),1).getValues();\n  var hidden = getHiddenValueArray(filterRange, 'テスト2');\n  \n  var criteria = SpreadsheetApp.newFilterCriteria()\n// .setVisibleValues(['テスト1','テスト3'])\n  .setHiddenValues(hidden)\n  .build();\n\n  sh.getFilter().setColumnFilterCriteria(1, criteria);\n}\n\nfunction getHiddenValueArray(colValueArr,visibleValueArr){\n  var flatUniqArr = colValueArr.map(function(e){return e[0];})\n  .filter(function(e,i,a){return (a.indexOf(e.toString())==i \u0026\u0026 visibleValueArr.indexOf(e.toString()) ==-1); })\n  return flatUniqArr;\n}\n```\n\n`setVisibleValues` 関数がググるとよく出てくるが、どうやらこちらは今はサポートされていないようです。\n\nかわりに、 `setHiddenValues` 関数で非表示にしたいデータ分値を配列で渡す必要があります。\n\n    Exception: Visible values are not currently supported. As an alternative specify a list of hidden values that excludes the values that should be visible. (line 17, file \"Code\")\n    \n[google apps script - Apply basic filter to multiple values in a spreadsheet column - Stack Overflow](https://stackoverflow.com/questions/51497982/apply-basic-filter-to-multiple-values-in-a-spreadsheet-column)\n\n[【初心者向けGAS】スプレッドシートのセル範囲を行数・列数を使って取得する](https://tonari-it.com/gas-getrange-row-column/)\n\n## GAS で G Suite メールアドレスを取得して閲覧制限のやり方\n\n## Web アプリとして公開するときの設定\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/hisasann/gas-typescript-starter/blob/master/internals/images/%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E5%8F%96%E5%BE%972.png?raw=true\" alt=\"\" width=\"50%\"\u003e\n\u003c/p\u003e\n\n[【GAS Webアプリ】ホワイトリストを使ってWebページのアクセス制御する - Qiita](https://qiita.com/merarli/items/143eb3324325c76e22bc)\n\n`Session.getActiveUser().getEmail()` でログインしているユーザーのメールアドレスが取得できるのですが、自分としてアプリケーションを実行するにした場合、\n\nどうやってもメールアドレスが取得できませんでした。\n\nなので、アクセスしたユーザーとして実行にしました。\n\nまた、 GAS やユーザー一覧を記載している Google Spreadsheet などは見れる状態にしないと実行できないので、そちらもやらなければなりません。\n\nですが、\n\n    these restrictions generally do not apply if the developer runs the script themselves or belongs to the same G Suite domain as the user.\n \n [Class Session  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/reference/base/session#getActiveUser())\n \n と書いてあるので、 `G Suiteドメイン` が同じであれば制限はもう少し緩いのかもしれません。\n\n### Code.gs\n\n```javascript\nfunction doGet() {\n  // 現在ログインしているユーザーのメールアドレスを取得\n  const LOGIN_USER = Session.getActiveUser().getEmail();\n\n  console.log(LOGIN_USER);\n  \n  let template = 'error';\n  // ログインしているユーザーのメールアドレスを使ってアクセス権を確認\n  if (LOGIN_USER === 'hoge@gmail.com' || LOGIN_USER === 'fuga@gmail.com') {\n    template = 'index';\n  }\n  \n  return HtmlService.createTemplateFromFile(template).evaluate();\n}\n```\n\n### appsscript.json\n\n```json\n{\n  \"timeZone\": \"Asia/Tokyo\",\n  \"dependencies\": {\n  },\n  \"webapp\": {\n    \"access\": \"ANYONE\",\n    \"executeAs\": \"USER_DEPLOYING\"\n  },\n  \"exceptionLogging\": \"STACKDRIVER\",\n  \"oauthScopes\": [\"https://www.googleapis.com/auth/spreadsheets\", \"https://www.googleapis.com/auth/userinfo.email\"],\n  \"runtimeVersion\": \"V8\"\n}\n```\n\n### index.html\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cbase target=\"_top\"\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    見えて良い\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n### error.html\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cbase target=\"_top\"\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    見えちゃだめ\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n[google apps script - getActiveUser() doesn't return the user? - Stack Overflow](https://stackoverflow.com/questions/57847178/getactiveuser-doesnt-return-the-user)\n\n[Authorization Scopes  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes)\n\n[アカウントにアクセスできるアプリ](https://myaccount.google.com/permissions)\n\n## GAS から HTTP POST する方法\n\n```javascript\nfunction myFunction() {\n  var message = 'google apps scriptより';\n  sendHttpPost(message);\n}\n\n/*\n* messageをpostする関数\n*/\nfunction sendHttpPost(message) {\n  var payload =\n    {\n      'message': message,\n    };\n\n  var options =\n    {\n      'method': 'post',\n      'payload': payload,\n    };\n\n  try {\n    // httpbin.org - https://httpbin.org/\n    const res = UrlFetchApp.fetch('https://httpbin.org/post', options);\n    const status = res.getResponseCode();\n    console.log(status);\n  } catch (e) {\n    console.log(e);\n  }\n}\n```\n\n[Google Apps ScriptからHTTP POST - Qiita](https://qiita.com/n0bisuke/items/a31a99232e50461eb00f)\n\n`UrlFetchApp.fetch` に関しては以下のドキュメントを参考。\n\n[Class UrlFetchApp  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app)\n\n`fetch` エラーだが\n\n1. 成功した\n1. 失敗した（catch に入る）\n1. http status が 200 かどうか\n\nの3種類をカバーする必要があるようだ。以下を参照。\n\n[UrlFetchApp.fetchの応答3種 タイムアウトがつらい - Qiita](https://qiita.com/khsk/items/7d90513c88559e3d703f)\n\n## Google Spreadsheet のメニューにオリジナルメニューを追加する\n\n```javascript\nfunction onOpen() {\n  var sheet = SpreadsheetApp.getActiveSpreadsheet();\n  var items = [\n    {name: 'こんにちは', functionName: 'myfunction'}\n  ];\n  sheet.addMenu('こんばんは', items);\n}\n    \nfunction myfunction() {\n  console.log('はい、こんにちは');\n}\n```\n\n## GAS 対応した monorepo の一つのパッケージの最終形態はこちら\n\n    ├── README.md\n    ├── __tests__\n    │   └── hello.test.ts\n    ├── jest.config.js\n    ├── node_modules\n    ├── package.json\n    ├── src\n    │   ├── appsscript.json\n    │   └── hello.ts\n    ├── .clasp.json\n    └── tsconfig.json\n\n## 参考記事\n\n[howdy39/gas-clasp-starter: A starter template for Google Apps Script by clasp](https://github.com/howdy39/gas-clasp-starter)\n\n[GAS のGoogle謹製CLIツール clasp - Qiita](https://qiita.com/HeRo/items/4e65dcc82783b2766c03)\n\n## Author\n\n- [github/hisasann](https://github.com/hisasann)\n- [twitter/hisasann](https://twitter.com/hisasann)\n\n## License\n\nMIT © [hisasann (Yoshiyuki Hisamatsu)](https://github.com/hisasann)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhisasann%2Fgas-typescript-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhisasann%2Fgas-typescript-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhisasann%2Fgas-typescript-starter/lists"}