npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gasboost/vite

v1.2.2

Published

A Vite plugin for building @gasboost/app applications for Google Apps Script.

Readme

@gasboost/vite

@gasboost/app で構築した Google Apps Script アプリケーションを、Vite でビルド・ローカル開発するためのプラグインです。

@gasboost/vite は用途ごとに2つの Vite Plugin を提供します。

  • build — Google Apps Script 向けの production build
  • dev — ローカル開発時の RPC 実行

インストール

pnpm add @gasboost/app
pnpm add -D @gasboost/vite vite

npm:

npm install @gasboost/app
npm install -D @gasboost/vite vite

Quick Start

まず @gasboost/app でバックエンドを定義します。

// src/server.ts

import { AppsScript } from "@gasboost/app";

const app = new AppsScript()
  .get(() => {
    return HtmlService.createHtmlOutput("Hello");
  })
  .post((request) => {
    return ContentService.createTextOutput(request.text());
  })
  .call("sum", (a: number, b: number) => a + b)
  .call("getUser", async (id: string) => ({
    id,
    name: "Taro",
  }));

export default app;

gasboost() から builddev を取得し、1つの vite.config.ts の中で mode に応じて使い分けます。

frontend と backend は成果物と build graph が異なるため、同時に1つの Vite build へ混在させません。

// vite.config.ts

import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import { viteSingleFile } from "vite-plugin-singlefile";
import { gasboost } from "@gasboost/vite";

const { build, dev } = gasboost({
  entry: "src/server.ts",
  envDir: ".env",
});

export default defineConfig(({ mode }) => {
  if (mode === "server") {
    return {
      plugins: [build],
      build: {
        outDir: "dist",
        emptyOutDir: false,
      },
    };
  }

  return {
    plugins: [react(), viteSingleFile(), dev],
    build: {
      outDir: "dist",
    },
  };
});

この例では、frontend build と backend build を mode で分離します。

{
  "scripts": {
    "dev": "vite",
    "build:client": "vite build --mode client",
    "build:server": "vite build --mode server",
    "build": "pnpm build:client && pnpm build:server"
  }
}
vite
  ↓
gasboost:dev

vite build --mode client
  ↓
frontend build

vite build --mode server
  ↓
gasboost:build

frontend 側の React、single-file 化などの設定は利用側プロジェクトの責務です。

gasboost()

const { build, dev } = gasboost({
  entry: "src/server.ts",
  envDir: "config",
});

戻り値:

{
  build: Plugin;
  dev: Plugin;
}

entry

必須です。

AppsScript を定義して default export している entry file を指定します。

gasboost({
  entry: "src/server.ts",
});

envDir

任意です。

build 時に Vite が環境変数ファイルを読み込むディレクトリを指定します。

gasboost({
  entry: "src/server.ts",
  envDir: "config",
});

Build Plugin

buildvite build のときだけ動作し、GAS backend build を担当します。

frontend / backend を同じ vite.config.ts で扱う場合は、server 用 mode のときだけ plugins に含めてください。

const { build } = gasboost({
  entry: "src/server.ts",
});

export default defineConfig(({ mode }) => {
  if (mode === "server") {
    return {
      plugins: [build],
    };
  }

  return {
    plugins: [],
  };
});

build plugin は次の処理を担当します。

  • entry file の静的解析
  • AppsScript に登録された GET / POST / RPC の検出
  • GAS が認識するグローバル関数宣言の生成
  • GAS 向け Vite build configuration
  • 環境変数の読み込み

グローバル関数の生成

例えば次のアプリケーションを定義した場合:

const app = new AppsScript()
  .get(...)
  .post(...)
  .call("getUser", ...)
  .call("sum", ...);

export default app;

build plugin は GAS が認識するためのグローバル関数宣言を生成します。

function doGet() {}
function doPost() {}
function getUser() {}
function sum() {}

これらは関数名を Google Apps Script に認識させるための宣言です。

実際のハンドラ登録と dispatch は @gasboost/app がランタイム上で行います。

@gasboost/vite
  static analysis
  global function declarations
        ↓
@gasboost/app
  runtime registration
  dispatch

静的解析

entry file には1つの AppsScript インスタンスを定義し、default export します。

const app = new AppsScript();

app.get(...);
app.call("getUser", ...);

export default app;

チェーン形式にも対応しています。

const app = new AppsScript()
  .get(...)
  .post(...)
  .call("getUser", ...);

export default app;

RPC 名は文字列リテラルで指定する必要があります。

app.call("getUser", handler);

次のような動的な名前は静的解析の対象外です。

const name = "getUser";

app.call(name, handler);

build 時に GAS のグローバル関数名を確定する必要があるためです。

検証

静的解析時には、曖昧なアプリケーション定義をエラーとして扱います。

  • GET ハンドラの重複
  • POST ハンドラの重複
  • RPC 名の重複
  • entry 内の複数 AppsScript インスタンス
  • default export されていない AppsScript

GAS 向け build configuration

現在の build 設定:

  • target: ECMAScript 2019
  • output format: CommonJS
  • output directory: dist
  • entry を build input として利用

環境変数

Vite 標準の環境変数機構を利用します。

config/
├── .env
├── .env.development
└── .env.production
const { build } = gasboost({
  entry: "src/server.ts",
  envDir: "config",
});

アプリケーションでは通常の Vite と同様に参照できます。

const apiUrl = import.meta.env.VITE_API_URL;

Dev Plugin

dev は Vite Dev Server 上でのみ動作します。

frontend の開発用 Vite config に追加して利用します。

const { dev } = gasboost({
  entry: "src/server.ts",
});

export default defineConfig({
  plugins: [dev],
});

dev plugin は、GAS にデプロイしなくてもローカル環境から AppsScript の RPC を実行できるエンドポイントを Vite Dev Server に追加します。

Local RPC

ローカル RPC のエンドポイントは次の形式です。

POST /__gasboost/{rpcName}

例えば、

const app = new AppsScript().call("sum", (a: number, b: number) => a + b);

export default app;

に対して、

POST /__gasboost/sum
Content-Type: application/json
{
  "args": [1, 2]
}

を送信すると、内部では次の dispatch が実行されます。

app.dispatch("sum", 1, 2);

レスポンス:

3

RPC Request

Request Body は次の形式です。

{
  args: unknown[];
}

例えば複数の引数を持つ RPC:

app.call(
  "example",
  (id: number, name: string, active: boolean, options: { value: number }) => {
    // ...
  },
);

に対して、

{
  "args": [
    1,
    "Taro",
    true,
    {
      "value": 4
    }
  ]
}

のように送信できます。

引数なし RPC

Request Body が空の場合は、引数なし RPC として扱われます。

POST /__gasboost/noArgs

内部では次のように dispatch されます。

app.dispatch("noArgs");

明示的に送る場合は次でも構いません。

{
  "args": []
}

RPC Response

@gasboost/appdispatch() が返す AppsScriptResponse.contents を、そのまま HTTP Response Body として返します。

例えば、

app.call("getUser", () => ({
  id: 1,
  name: "Taro",
}));

に対するレスポンスは:

{
  "id": 1,
  "name": "Taro"
}

となります。

Response の Content-Type は:

application/json; charset=utf-8

です。

Async RPC

非同期 RPC にも対応しています。

app.call("loadUser", async (id: string) => {
  const user = await loadUser(id);

  return user;
});

dev plugin は dispatch() の完了を待ってからレスポンスを返します。

開発中の module loading

RPC リクエストごとに、指定された entry を Vite の ssrLoadModule() で読み込みます。

POST /__gasboost/sum
        ↓
ssrLoadModule("src/server.ts")
        ↓
AppsScript.dispatch("sum", ...)

そのため、Vite Dev Server 上の最新のサーバーコードを利用して RPC を実行できます。

Error Response

不正な Request Body

JSON として不正な body や、args が配列でない body は 400 を返します。

{
  "args": "invalid"
}

レスポンス例:

{
  "error": {
    "name": "InvalidRpcRequestError",
    "message": "Invalid RPC request body. Expected { args: unknown[] }."
  }
}

JSON 自体が不正な場合も 400 です。

POST 以外

Local RPC endpoint は POST のみ受け付けます。

GET /__gasboost/sum

405 になります。

{
  "error": {
    "name": "MethodNotAllowedError",
    "message": "Only POST is allowed."
  }
}

未登録 RPC

存在しない RPC を呼び出した場合は 500 を返します。

POST /__gasboost/unknown
{
  "error": {
    "name": "Error",
    "message": "Function unknown is not registered."
  }
}

Handler Error

RPC handler 内で例外が発生した場合も 500 として JSON で返されます。

{
  "error": {
    "name": "TypeError",
    "message": "handler failed"
  }
}

Local RPC の対象 path

次の形式だけを RPC として扱います。

/__gasboost/{rpcName}

例えば以下は対象です。

/__gasboost/getUser
/__gasboost/sum

一方、次のような path は RPC として扱いません。

/__gasboost/
/__gasboost/foo/bar
/api/users

通常の Vite middleware chain に処理を渡します。

query string が付いていても RPC 名は正しく解決されます。

/__gasboost/hello?foo=bar

URL encoded な RPC 名も decode されます。

/__gasboost/hello%20world

Client Transport

@gasboost/client を利用している場合、dev plugin はローカル開発時の transport を自動で Local RPC に切り替えます。

アプリケーション側では development / production を判定する必要はありません。

import { appsScriptClient } from "@gasboost/client";
import type { App } from "./server";

const { client } = appsScriptClient<App>();

const result = await client.sum(1, 2);

Vite Dev Server 上では、appsScriptClient() に transport が指定されていない場合、dev plugin が自動的に FetchTransport を適用します。

appsScriptClient()
        ↓
FetchTransport
        ↓
POST /__gasboost/{rpcName}
        ↓
AppsScript.dispatch()

そのため、ローカル開発用に transport を切り替えるコードを書く必要はありません。

// 不要
appsScriptClient({
  transport: import.meta.env.DEV
    ? new FetchTransport({
        endpoint: "/__gasboost",
      })
    : new AppsScriptTransport(),
});

production build では dev plugin による差し替えは行われません。

GAS 上では appsScriptClient() のデフォルトである AppsScriptTransport がそのまま利用されます。

appsScriptClient()
        ↓
AppsScriptTransport
        ↓
google.script.run

つまり、同じ application code のまま local development と GAS production の両方で利用できます。

const { client } = appsScriptClient<App>();

また、明示的に transport を指定した場合はその transport が優先されます。

const { client } = appsScriptClient<App>({
  transport: customTransport,
});

dev plugin が明示指定された transport を上書きすることはありません。


HtmlTemplate variables

GAS の HtmlTemplate では、<?= variable ?> を使用してサーバー側の値を HTML に埋め込めます。

<script>
  const variables = "<?= variables ?>";
</script>

GAS 上では HtmlTemplate によって評価されますが、Vite Dev Server では GAS のテンプレート処理が実行されません。

@gasboost/vite では、template オプションを指定することで、開発時にこれらの template variables を置換できます。

import { gasboost } from "@gasboost/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    gasboost({
      entry: "./src/backend/main.ts",
      template: {
        variables: JSON.stringify({
          scriptId: "local-script-id",
          isSetupCompleted: true,
          isTermsAccepted: true,
        }),
      },
    }).dev,
  ],
});

例えば、次の HTML は、

<script>
  const variables = "<?= variables ?>";
</script>

Vite の開発環境では次のように変換されます。

<script>
  const variables =
    '{"scriptId":"local-script-id","isSetupCompleted":true,"isTermsAccepted":true}';
</script>

複数の template variable も指定できます。

gasboost({
  entry: "./src/backend/main.ts",
  template: {
    environment: "development",
    userName: "Tiger",
  },
});
<p>Environment: <?= environment ?></p>
<p>User: <?= userName ?></p>

template の型は Record<string, string> です。

値の serialize は @gasboost/vite では行いません。オブジェクトなどを埋め込む場合は、利用側で JSON.stringify() などを使用して文字列へ変換してください。

template による置換は Vite Dev Server でのみ行われます。production build では値を埋め込まず、GAS 上で実際の HtmlTemplate が template expression を評価します。


build と dev の責務

gasboost()
   │
   ├─ build
   │    ├─ AppsScript の静的解析
   │    ├─ GAS グローバル関数生成
   │    ├─ GAS 向け build config
   │    └─ backend production build
   │
   └─ dev
        ├─ Vite Dev Server middleware
        ├─ Local RPC endpoint
        ├─ entry の ssrLoadModule
        └─ AppsScript.dispatch

frontend と backend を1つの vite.config.ts で扱う場合は、mode で分岐します。

const { build, dev } = gasboost({
  entry: "src/server.ts",
});

export default defineConfig(({ mode }) => {
  if (mode === "server") {
    return {
      plugins: [build],
    };
  }

  return {
    plugins: [dev],
  };
});

build を通常の frontend build に含めると、server entry が Vite の build input になるため、frontend と backend の build は分離してください。


GAS ランタイム

@gasboost/vite は、build 時と local RPC 実行時に server entry を実際に評価します。

一般的な GAS API は、デフォルトのローカルランタイムから提供されます。

import { gasboost } from "@gasboost/vite";

export default gasboost({
  entry: "./src/server.ts",
});

entry の初期化時に追加の GAS API が必要な場合は、runtime から差し込めます。

import { SpreadsheetAppStub } from "@gasboost/sheetorm";
import { gasboost } from "@gasboost/vite";

export default gasboost({
  entry: "./src/server.ts",
  runtime: {
    SpreadsheetApp: SpreadsheetAppStub,
  },
});

runtime に渡した値は、デフォルトのローカルランタイムを上書きします。

このランタイムは build 時の entry 評価と、local RPC 実行時の両方で利用されます。

基本的には、module 初期化時に必要な API だけを差し込み、実際の GAS API 呼び出しは handler 実行時まで遅延させることを推奨します。

ランタイム解析

GET / POST / RPC の登録状態は、entry ファイルを静的解析するのではなく、実際に評価された AppsScript インスタンスから取得します。

そのため、import 先、ヘルパー関数、loop、AppsScript.calls() 経由の登録にも対応できます。

const app = new AppsScript();

for (const [name, handler] of Object.entries(handlers)) {
  app.call(name, handler);
}

export default app;

関連パッケージ

  • @gasboost/app — GAS バックエンドランタイムと RPC 定義
  • @gasboost/client — フロントエンド側の型安全 RPC クライアント

License

MIT