@gasboost/vite 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -0
- package/dist/dev.d.ts +3 -2
- package/dist/gasboost.d.ts +2 -0
- package/dist/globals.d.ts +2 -2
- package/dist/index.js +94 -124
- package/dist/loadAppsScript.d.ts +4 -0
- package/dist/runtime.d.ts +3 -0
- package/package.json +5 -2
- package/dist/analyzer.d.ts +0 -6
package/README.md
CHANGED
|
@@ -643,6 +643,58 @@ export default defineConfig(({ mode }) => {
|
|
|
643
643
|
|
|
644
644
|
`build` を通常の frontend build に含めると、server entry が Vite の build input になるため、frontend と backend の build は分離してください。
|
|
645
645
|
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## GAS ランタイム
|
|
649
|
+
|
|
650
|
+
`@gasboost/vite` は、build 時と local RPC 実行時に server entry を実際に評価します。
|
|
651
|
+
|
|
652
|
+
一般的な GAS API は、デフォルトのローカルランタイムから提供されます。
|
|
653
|
+
|
|
654
|
+
```ts
|
|
655
|
+
import { gasboost } from "@gasboost/vite";
|
|
656
|
+
|
|
657
|
+
export default gasboost({
|
|
658
|
+
entry: "./src/server.ts",
|
|
659
|
+
});
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
entry の初期化時に追加の GAS API が必要な場合は、`runtime` から差し込めます。
|
|
663
|
+
|
|
664
|
+
```ts
|
|
665
|
+
import { SpreadsheetAppStub } from "@gasboost/sheetorm";
|
|
666
|
+
import { gasboost } from "@gasboost/vite";
|
|
667
|
+
|
|
668
|
+
export default gasboost({
|
|
669
|
+
entry: "./src/server.ts",
|
|
670
|
+
runtime: {
|
|
671
|
+
SpreadsheetApp: SpreadsheetAppStub,
|
|
672
|
+
},
|
|
673
|
+
});
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
`runtime` に渡した値は、デフォルトのローカルランタイムを上書きします。
|
|
677
|
+
|
|
678
|
+
このランタイムは build 時の entry 評価と、local RPC 実行時の両方で利用されます。
|
|
679
|
+
|
|
680
|
+
基本的には、module 初期化時に必要な API だけを差し込み、実際の GAS API 呼び出しは handler 実行時まで遅延させることを推奨します。
|
|
681
|
+
|
|
682
|
+
## ランタイム解析
|
|
683
|
+
|
|
684
|
+
GET / POST / RPC の登録状態は、entry ファイルを静的解析するのではなく、実際に評価された `AppsScript` インスタンスから取得します。
|
|
685
|
+
|
|
686
|
+
そのため、import 先、ヘルパー関数、loop、`AppsScript.calls()` 経由の登録にも対応できます。
|
|
687
|
+
|
|
688
|
+
```ts
|
|
689
|
+
const app = new AppsScript();
|
|
690
|
+
|
|
691
|
+
for (const [name, handler] of Object.entries(handlers)) {
|
|
692
|
+
app.call(name, handler);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export default app;
|
|
696
|
+
```
|
|
697
|
+
|
|
646
698
|
## 関連パッケージ
|
|
647
699
|
|
|
648
700
|
- `@gasboost/app` — GAS バックエンドランタイムと RPC 定義
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
1
|
+
import { type Plugin } from "vite";
|
|
2
|
+
import type { GasboostOptions } from "./gasboost";
|
|
3
|
+
export declare function createDevPlugin(options: GasboostOptions): Plugin;
|
package/dist/gasboost.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
+
export type GasRuntime = Readonly<Record<string, unknown>>;
|
|
2
3
|
export interface GasboostOptions {
|
|
3
4
|
entry: string;
|
|
4
5
|
envDir?: string;
|
|
6
|
+
runtime?: GasRuntime;
|
|
5
7
|
}
|
|
6
8
|
export declare function gasboost(options: GasboostOptions): {
|
|
7
9
|
build: Plugin;
|
package/dist/globals.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function createGlobalCode(analysis:
|
|
1
|
+
import type { AppsScriptDescription } from "@gasboost/app";
|
|
2
|
+
export declare function createGlobalCode(analysis: AppsScriptDescription): string;
|
package/dist/index.js
CHANGED
|
@@ -1,91 +1,12 @@
|
|
|
1
|
-
import e from "node:
|
|
2
|
-
import t from "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { API as l } from "typescript/unstable/sync";
|
|
6
|
-
//#region src/analyzer.ts
|
|
7
|
-
function u(r) {
|
|
8
|
-
let i = t.resolve(r);
|
|
9
|
-
if (!e.existsSync(i)) throw Error(`Entry file not found: ${i}`);
|
|
10
|
-
let s = d(i, e.readFileSync(i, "utf8")), c = f(s);
|
|
11
|
-
if (c.length === 0) throw Error("AppsScript instance not found.");
|
|
12
|
-
if (c.length > 1) throw Error("Multiple AppsScript instances found in entry.");
|
|
13
|
-
let l = c[0];
|
|
14
|
-
g(s, l);
|
|
15
|
-
let u = {
|
|
16
|
-
hasGet: !1,
|
|
17
|
-
hasPost: !1,
|
|
18
|
-
calls: []
|
|
19
|
-
}, p = /* @__PURE__ */ new Set();
|
|
20
|
-
function m(e) {
|
|
21
|
-
if (!a(e.expression)) return;
|
|
22
|
-
let t = e.expression, n = t.name.text;
|
|
23
|
-
if (!h(t.expression, l)) return;
|
|
24
|
-
if (n === "get") {
|
|
25
|
-
if (u.hasGet) throw Error("Duplicate GET handler registration.");
|
|
26
|
-
u.hasGet = !0;
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (n === "post") {
|
|
30
|
-
if (u.hasPost) throw Error("Duplicate POST handler registration.");
|
|
31
|
-
u.hasPost = !0;
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (n !== "call") return;
|
|
35
|
-
let r = e.arguments[0];
|
|
36
|
-
if (!r) throw Error(".call() requires a function name.");
|
|
37
|
-
if (!o(r)) throw Error(".call() function name must be a string literal.");
|
|
38
|
-
if (p.has(r.text)) throw Error(`Duplicate RPC registration: "${r.text}".`);
|
|
39
|
-
p.add(r.text), u.calls.unshift(r.text);
|
|
40
|
-
}
|
|
41
|
-
function _(e) {
|
|
42
|
-
n(e) && m(e), e.forEachChild(_);
|
|
43
|
-
}
|
|
44
|
-
return _(s), u;
|
|
45
|
-
}
|
|
46
|
-
function d(e, t) {
|
|
47
|
-
let n = "/entry.ts", r = "/tsconfig.json", i = c({
|
|
48
|
-
[r]: JSON.stringify({ files: [n] }),
|
|
49
|
-
[n]: t
|
|
50
|
-
}), a = new l({
|
|
51
|
-
cwd: "/",
|
|
52
|
-
fs: i
|
|
53
|
-
}).updateSnapshot({ openProject: r }).getProject(r);
|
|
54
|
-
if (!a) throw Error("Failed to create TypeScript project.");
|
|
55
|
-
let o = a.program.getSourceFile(n);
|
|
56
|
-
if (!o) throw Error(`Failed to parse entry: ${e}`);
|
|
57
|
-
return o;
|
|
58
|
-
}
|
|
59
|
-
function f(e) {
|
|
60
|
-
let t = [];
|
|
61
|
-
function n(e) {
|
|
62
|
-
s(e) && p(e) && t.push(e.name.text), e.forEachChild(n);
|
|
63
|
-
}
|
|
64
|
-
return n(e), t;
|
|
65
|
-
}
|
|
66
|
-
function p(e) {
|
|
67
|
-
return !r(e.name) || !e.initializer ? !1 : m(e.initializer);
|
|
68
|
-
}
|
|
69
|
-
function m(e) {
|
|
70
|
-
return i(e) && r(e.expression) && e.expression.text === "AppsScript" ? !0 : n(e) && a(e.expression) ? m(e.expression.expression) : !1;
|
|
71
|
-
}
|
|
72
|
-
function h(e, t) {
|
|
73
|
-
return r(e) && e.text === t || i(e) && r(e.expression) && e.expression.text === "AppsScript" ? !0 : n(e) && a(e.expression) ? h(e.expression.expression, t) : !1;
|
|
74
|
-
}
|
|
75
|
-
function g(e, t) {
|
|
76
|
-
let n = !1;
|
|
77
|
-
for (let r of e.statements) if (r.getText() === `export default ${t};`) {
|
|
78
|
-
n = !0;
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
if (!n) throw Error("AppsScript instance must be default exported.");
|
|
82
|
-
}
|
|
83
|
-
//#endregion
|
|
1
|
+
import e from "node:path";
|
|
2
|
+
import { createServer as t, isRunnableDevEnvironment as n } from "vite";
|
|
3
|
+
import { InMemoryCacheService as r, InMemoryContext as i, InMemoryPropertiesService as a, InMemorySession as o, SecurityPolicy as s } from "@gasboost/fake-core";
|
|
4
|
+
import { NodeUtilities as c } from "@gasboost/fake-node";
|
|
84
5
|
//#region src/config.ts
|
|
85
|
-
function
|
|
86
|
-
let n =
|
|
6
|
+
function l(t) {
|
|
7
|
+
let n = e.resolve(t.entry);
|
|
87
8
|
return {
|
|
88
|
-
envDir:
|
|
9
|
+
envDir: t.envDir,
|
|
89
10
|
build: {
|
|
90
11
|
target: "es2019",
|
|
91
12
|
outDir: "dist",
|
|
@@ -94,7 +15,7 @@ function _(e) {
|
|
|
94
15
|
input: n,
|
|
95
16
|
output: {
|
|
96
17
|
format: "cjs",
|
|
97
|
-
entryFileNames:
|
|
18
|
+
entryFileNames: e.basename(n, e.extname(n)) + ".js"
|
|
98
19
|
}
|
|
99
20
|
}
|
|
100
21
|
}
|
|
@@ -102,75 +23,124 @@ function _(e) {
|
|
|
102
23
|
}
|
|
103
24
|
//#endregion
|
|
104
25
|
//#region src/globals.ts
|
|
105
|
-
var
|
|
106
|
-
function
|
|
107
|
-
if (
|
|
26
|
+
var u = /* @__PURE__ */ new Set(["doGet", "doPost"]);
|
|
27
|
+
function d(e) {
|
|
28
|
+
if (u.has(e)) throw Error(`RPC name "${e}" is reserved by Google Apps Script.`);
|
|
108
29
|
if (!/^[$A-Z_a-z][$\w]*$/u.test(e)) throw Error(`RPC name "${e}" is not a valid JavaScript identifier.`);
|
|
109
30
|
}
|
|
110
|
-
function
|
|
31
|
+
function f(e) {
|
|
111
32
|
let t = [];
|
|
112
33
|
e.hasGet && t.push("function doGet() {}"), e.hasPost && t.push("function doPost() {}");
|
|
113
|
-
for (let n of e.calls)
|
|
34
|
+
for (let n of e.calls) d(n), t.push(`function ${n}() {}`);
|
|
114
35
|
return t.join("\n");
|
|
115
36
|
}
|
|
116
37
|
//#endregion
|
|
38
|
+
//#region src/runtime.ts
|
|
39
|
+
var p = new i("", "", {
|
|
40
|
+
type: "WEB_APP",
|
|
41
|
+
executeAs: "USER"
|
|
42
|
+
}, new s([]), "en", "UTC"), m = {
|
|
43
|
+
CacheService: new r(),
|
|
44
|
+
PropertiesService: new a(),
|
|
45
|
+
Session: new o(p),
|
|
46
|
+
Utilities: new c()
|
|
47
|
+
};
|
|
48
|
+
function h(e = {}) {
|
|
49
|
+
Object.assign(globalThis, {
|
|
50
|
+
...m,
|
|
51
|
+
...e
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/loadAppsScript.ts
|
|
56
|
+
async function g(e, r) {
|
|
57
|
+
h(e.runtime);
|
|
58
|
+
let i = await t({
|
|
59
|
+
configFile: !1,
|
|
60
|
+
root: r.root,
|
|
61
|
+
mode: r.mode,
|
|
62
|
+
envDir: e.envDir,
|
|
63
|
+
appType: "custom",
|
|
64
|
+
ssr: { external: ["@gasboost/app"] },
|
|
65
|
+
server: {
|
|
66
|
+
middlewareMode: !0,
|
|
67
|
+
hmr: !1
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
try {
|
|
71
|
+
let t = i.environments.ssr;
|
|
72
|
+
if (!n(t)) throw Error("Vite SSR environment is not runnable.");
|
|
73
|
+
let r = await t.runner.import(e.entry);
|
|
74
|
+
if (!r.default) throw Error("AppsScript entry must have a default export.");
|
|
75
|
+
return r.default.describe();
|
|
76
|
+
} finally {
|
|
77
|
+
await i.close();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
117
81
|
//#region src/build.ts
|
|
118
|
-
function
|
|
119
|
-
let t;
|
|
82
|
+
function _(e) {
|
|
83
|
+
let t, n;
|
|
120
84
|
return {
|
|
121
85
|
name: "gasboost:build",
|
|
122
86
|
apply: "build",
|
|
123
87
|
config() {
|
|
124
|
-
return
|
|
88
|
+
return l(e);
|
|
125
89
|
},
|
|
126
|
-
|
|
127
|
-
t =
|
|
90
|
+
configResolved(e) {
|
|
91
|
+
t = e;
|
|
128
92
|
},
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
93
|
+
async buildStart() {
|
|
94
|
+
n = await g(e, t);
|
|
95
|
+
},
|
|
96
|
+
generateBundle(e, t) {
|
|
97
|
+
let r = f(n);
|
|
98
|
+
for (let e of Object.values(t)) e.type === "chunk" && e.isEntry && (e.code = `${r}\n\n${e.code}`);
|
|
132
99
|
}
|
|
133
100
|
};
|
|
134
101
|
}
|
|
135
102
|
//#endregion
|
|
136
103
|
//#region src/dev.ts
|
|
137
|
-
var
|
|
104
|
+
var v = class extends Error {
|
|
138
105
|
constructor(e) {
|
|
139
106
|
super(e), this.name = "InvalidRpcRequestError";
|
|
140
107
|
}
|
|
141
108
|
};
|
|
142
|
-
function
|
|
109
|
+
function y(e) {
|
|
110
|
+
let { runtime: t } = e;
|
|
143
111
|
return {
|
|
144
112
|
name: "gasboost:dev",
|
|
145
113
|
apply: "serve",
|
|
146
|
-
configureServer(
|
|
147
|
-
t.middlewares.use(async (
|
|
148
|
-
if (!
|
|
149
|
-
|
|
114
|
+
configureServer(r) {
|
|
115
|
+
h(t), r.middlewares.use(async (t, i, a) => {
|
|
116
|
+
if (!t.url) {
|
|
117
|
+
a();
|
|
150
118
|
return;
|
|
151
119
|
}
|
|
152
|
-
let
|
|
153
|
-
if (!
|
|
154
|
-
|
|
120
|
+
let o = t.url.split("?")[0];
|
|
121
|
+
if (!o.startsWith("/__gasboost/")) {
|
|
122
|
+
a();
|
|
155
123
|
return;
|
|
156
124
|
}
|
|
157
|
-
let
|
|
158
|
-
if (!
|
|
159
|
-
|
|
125
|
+
let s = o.slice(12);
|
|
126
|
+
if (!s || s.includes("/")) {
|
|
127
|
+
a();
|
|
160
128
|
return;
|
|
161
129
|
}
|
|
162
|
-
if (
|
|
163
|
-
|
|
130
|
+
if (t.method !== "POST") {
|
|
131
|
+
i.statusCode = 405, i.setHeader("Content-Type", "application/json; charset=utf-8"), i.end(JSON.stringify({ error: {
|
|
164
132
|
name: "MethodNotAllowedError",
|
|
165
133
|
message: "Only POST is allowed."
|
|
166
134
|
} }));
|
|
167
135
|
return;
|
|
168
136
|
}
|
|
169
137
|
try {
|
|
170
|
-
let { args:
|
|
171
|
-
|
|
138
|
+
let { args: a } = await b(t), o = r.environments.ssr;
|
|
139
|
+
if (!n(o)) throw Error("Vite SSR environment is not runnable.");
|
|
140
|
+
let c = await (await o.runner.import(e.entry)).default.dispatch(decodeURIComponent(s), ...a);
|
|
141
|
+
i.statusCode = 200, i.setHeader("Content-Type", "application/json; charset=utf-8"), i.end(c.contents);
|
|
172
142
|
} catch (e) {
|
|
173
|
-
|
|
143
|
+
i.statusCode = e instanceof v ? 400 : 500, i.setHeader("Content-Type", "application/json; charset=utf-8"), i.end(JSON.stringify({ error: e instanceof Error ? {
|
|
174
144
|
name: e.name,
|
|
175
145
|
message: e.message,
|
|
176
146
|
stack: e.stack
|
|
@@ -183,7 +153,7 @@ function C(e) {
|
|
|
183
153
|
}
|
|
184
154
|
};
|
|
185
155
|
}
|
|
186
|
-
function
|
|
156
|
+
function b(e) {
|
|
187
157
|
return new Promise((t, n) => {
|
|
188
158
|
let r = [];
|
|
189
159
|
e.on("data", (e) => {
|
|
@@ -198,11 +168,11 @@ function w(e) {
|
|
|
198
168
|
try {
|
|
199
169
|
i = JSON.parse(e);
|
|
200
170
|
} catch {
|
|
201
|
-
n(new
|
|
171
|
+
n(new v("Invalid RPC request body. Expected valid JSON."));
|
|
202
172
|
return;
|
|
203
173
|
}
|
|
204
174
|
if (typeof i != "object" || !i || !("args" in i) || !Array.isArray(i.args)) {
|
|
205
|
-
n(new
|
|
175
|
+
n(new v("Invalid RPC request body. Expected { args: unknown[] }."));
|
|
206
176
|
return;
|
|
207
177
|
}
|
|
208
178
|
t({ args: i.args });
|
|
@@ -211,11 +181,11 @@ function w(e) {
|
|
|
211
181
|
}
|
|
212
182
|
//#endregion
|
|
213
183
|
//#region src/gasboost.ts
|
|
214
|
-
function
|
|
184
|
+
function x(e) {
|
|
215
185
|
return {
|
|
216
|
-
build:
|
|
217
|
-
dev:
|
|
186
|
+
build: _(e),
|
|
187
|
+
dev: y(e)
|
|
218
188
|
};
|
|
219
189
|
}
|
|
220
190
|
//#endregion
|
|
221
|
-
export {
|
|
191
|
+
export { x as gasboost };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AppsScriptDescription } from "@gasboost/app";
|
|
2
|
+
import { type ResolvedConfig } from "vite";
|
|
3
|
+
import type { GasboostOptions } from "./gasboost";
|
|
4
|
+
export declare function loadAppsScript(options: GasboostOptions, config: ResolvedConfig): Promise<AppsScriptDescription>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gasboost/vite",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A Vite plugin for building @gasboost/app applications for Google Apps Script.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"google-apps-script",
|
|
@@ -33,11 +33,14 @@
|
|
|
33
33
|
},
|
|
34
34
|
"type": "module",
|
|
35
35
|
"devDependencies": {
|
|
36
|
+
"@types/google-apps-script": "^2.0.12",
|
|
36
37
|
"vite": "^8.2.2"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
40
|
+
"@gasboost/fake-core": "^0.1.2",
|
|
41
|
+
"@gasboost/fake-node": "^0.1.2",
|
|
39
42
|
"typescript": "^7.0.2",
|
|
40
|
-
"@gasboost/app": "1.0
|
|
43
|
+
"@gasboost/app": "1.3.0"
|
|
41
44
|
},
|
|
42
45
|
"publishConfig": {
|
|
43
46
|
"access": "public"
|