@ciderjs/gasnuki 0.3.0 → 0.3.1
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.ja.md +1 -1
- package/README.md +1 -1
- package/dist/cli.cjs +2 -2
- package/dist/cli.mjs +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +2 -2
- package/dist/shared/{gasnuki.BYl_yopE.mjs → gasnuki.Bcijv2Ub.mjs} +22 -22
- package/dist/shared/{gasnuki.CxoM0Mts.cjs → gasnuki.Cbwq1CES.cjs} +22 -22
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +3 -2
- package/.docs/vite-plugin/design.md +0 -220
- package/.docs/vite-plugin/requirements.md +0 -72
package/README.ja.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# gasnuki
|
|
2
2
|
|
|
3
|
-
[](https://github.com/luthpg/gasnuki)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://www.npmjs.com/package/@ciderjs/gasnuki)
|
|
6
6
|
[](https://github.com/luthpg/gasnuki/issues)
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ciderjs/gasnuki
|
|
2
2
|
|
|
3
|
-
[](https://github.com/luthpg/gasnuki)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://www.npmjs.com/package/@ciderjs/gasnuki)
|
|
6
6
|
[](https://github.com/luthpg/gasnuki/issues)
|
package/dist/cli.cjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const commander = require('commander');
|
|
6
6
|
const index = require('./index.cjs');
|
|
7
|
-
const config = require('./shared/gasnuki.
|
|
7
|
+
const config = require('./shared/gasnuki.Cbwq1CES.cjs');
|
|
8
8
|
require('chokidar');
|
|
9
9
|
require('consola');
|
|
10
10
|
require('node:fs');
|
|
@@ -25,7 +25,7 @@ function _interopNamespaceCompat(e) {
|
|
|
25
25
|
|
|
26
26
|
const path__namespace = /*#__PURE__*/_interopNamespaceCompat(path);
|
|
27
27
|
|
|
28
|
-
const version = "0.3.
|
|
28
|
+
const version = "0.3.1";
|
|
29
29
|
|
|
30
30
|
const parseArgs = async (command) => {
|
|
31
31
|
const cliOpts = command.opts();
|
package/dist/cli.mjs
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { generateTypes } from './index.mjs';
|
|
5
|
-
import { l as loadConfig } from './shared/gasnuki.
|
|
5
|
+
import { l as loadConfig } from './shared/gasnuki.Bcijv2Ub.mjs';
|
|
6
6
|
import 'chokidar';
|
|
7
7
|
import 'consola';
|
|
8
8
|
import 'node:fs';
|
|
9
9
|
import 'ts-morph';
|
|
10
10
|
import 'jiti';
|
|
11
11
|
|
|
12
|
-
const version = "0.3.
|
|
12
|
+
const version = "0.3.1";
|
|
13
13
|
|
|
14
14
|
const parseArgs = async (command) => {
|
|
15
15
|
const cliOpts = command.opts();
|
package/dist/index.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const chokidar = require('chokidar');
|
|
5
5
|
const consola = require('consola');
|
|
6
|
-
const config = require('./shared/gasnuki.
|
|
6
|
+
const config = require('./shared/gasnuki.Cbwq1CES.cjs');
|
|
7
7
|
require('node:fs');
|
|
8
8
|
require('ts-morph');
|
|
9
9
|
require('jiti');
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
import * as chokidar from 'chokidar';
|
|
3
3
|
import { consola } from 'consola';
|
|
4
|
-
import { g as generateAppsScriptTypes } from './shared/gasnuki.
|
|
5
|
-
export { d as defineConfig } from './shared/gasnuki.
|
|
4
|
+
import { g as generateAppsScriptTypes } from './shared/gasnuki.Bcijv2Ub.mjs';
|
|
5
|
+
export { d as defineConfig } from './shared/gasnuki.Bcijv2Ub.mjs';
|
|
6
6
|
import 'node:fs';
|
|
7
7
|
import 'ts-morph';
|
|
8
8
|
import 'jiti';
|
|
@@ -77,6 +77,12 @@ const generateAppsScriptTypes = async ({
|
|
|
77
77
|
const exportedDeclarationNames = /* @__PURE__ */ new Set();
|
|
78
78
|
const exportedFunctions = [];
|
|
79
79
|
for (const sourceFile of sourceFiles) {
|
|
80
|
+
const filePath = sourceFile.getFilePath().replace(/\\/g, "/");
|
|
81
|
+
const srcPath = absoluteSrcDir.replace(/\\/g, "/");
|
|
82
|
+
const projectRelativeSrcPath = path.join(projectPath, srcDir).replace(/\\/g, "/");
|
|
83
|
+
if (!filePath.startsWith(srcPath) && !filePath.startsWith(projectRelativeSrcPath)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
80
86
|
for (const iface of sourceFile.getInterfaces()) {
|
|
81
87
|
if (!iface.getName()?.endsWith("_")) {
|
|
82
88
|
exportedDeclarations.push(iface);
|
|
@@ -193,31 +199,14 @@ const generateAppsScriptTypes = async ({
|
|
|
193
199
|
continue;
|
|
194
200
|
}
|
|
195
201
|
const sourceFile = declaration.getSourceFile();
|
|
196
|
-
|
|
202
|
+
const sourceFilePath = sourceFile.getFilePath();
|
|
203
|
+
if (sourceFilePath.includes("node_modules")) {
|
|
197
204
|
continue;
|
|
198
205
|
}
|
|
199
206
|
processedSymbols.add(symbolName);
|
|
200
|
-
const
|
|
201
|
-
if (
|
|
202
|
-
|
|
203
|
-
const declText = declaration.getText();
|
|
204
|
-
inlineDefinitions.set(symbolName, declText);
|
|
205
|
-
const tempSourceFile = project.createSourceFile(
|
|
206
|
-
`__temp_${symbolName}.ts`,
|
|
207
|
-
declText
|
|
208
|
-
);
|
|
209
|
-
for (const descendant of tempSourceFile.getDescendantsOfKind(
|
|
210
|
-
SyntaxKind.TypeReference
|
|
211
|
-
)) {
|
|
212
|
-
const newSymbol = descendant.getType().getAliasSymbol() ?? descendant.getType().getSymbol();
|
|
213
|
-
if (newSymbol) {
|
|
214
|
-
symbolsToProcess.add(newSymbol);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
tempSourceFile.delete();
|
|
218
|
-
}
|
|
219
|
-
} else {
|
|
220
|
-
let modulePath = path.relative(absoluteOutDir, sourceFile.getFilePath()).replace(/\\/g, "/");
|
|
207
|
+
const isExternal = !sourceFilePath.replace(/\\/g, "/").startsWith(absoluteSrcDir.replace(/\\/g, "/")) && !sourceFilePath.replace(/\\/g, "/").startsWith(path.join(projectPath, srcDir).replace(/\\/g, "/"));
|
|
208
|
+
if (isExternal) {
|
|
209
|
+
let modulePath = path.relative(absoluteOutDir, sourceFilePath).replace(/\\/g, "/");
|
|
221
210
|
modulePath = modulePath.replace(/\.(d\.)?ts$/, "");
|
|
222
211
|
if (modulePath.endsWith("/index")) {
|
|
223
212
|
modulePath = modulePath.slice(0, -6);
|
|
@@ -232,6 +221,17 @@ const generateAppsScriptTypes = async ({
|
|
|
232
221
|
importsMap.set(modulePath, /* @__PURE__ */ new Set());
|
|
233
222
|
}
|
|
234
223
|
importsMap.get(modulePath)?.add(symbolName);
|
|
224
|
+
} else {
|
|
225
|
+
const declText = declaration.getText();
|
|
226
|
+
inlineDefinitions.set(symbolName, declText);
|
|
227
|
+
}
|
|
228
|
+
for (const descendant of declaration.getDescendantsOfKind(
|
|
229
|
+
SyntaxKind.TypeReference
|
|
230
|
+
)) {
|
|
231
|
+
const newSymbol = descendant.getType().getAliasSymbol() ?? descendant.getType().getSymbol();
|
|
232
|
+
if (newSymbol) {
|
|
233
|
+
symbolsToProcess.add(newSymbol);
|
|
234
|
+
}
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
if (!fs.existsSync(absoluteOutDir)) {
|
|
@@ -94,6 +94,12 @@ const generateAppsScriptTypes = async ({
|
|
|
94
94
|
const exportedDeclarationNames = /* @__PURE__ */ new Set();
|
|
95
95
|
const exportedFunctions = [];
|
|
96
96
|
for (const sourceFile of sourceFiles) {
|
|
97
|
+
const filePath = sourceFile.getFilePath().replace(/\\/g, "/");
|
|
98
|
+
const srcPath = absoluteSrcDir.replace(/\\/g, "/");
|
|
99
|
+
const projectRelativeSrcPath = path__namespace.join(projectPath, srcDir).replace(/\\/g, "/");
|
|
100
|
+
if (!filePath.startsWith(srcPath) && !filePath.startsWith(projectRelativeSrcPath)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
97
103
|
for (const iface of sourceFile.getInterfaces()) {
|
|
98
104
|
if (!iface.getName()?.endsWith("_")) {
|
|
99
105
|
exportedDeclarations.push(iface);
|
|
@@ -210,31 +216,14 @@ const generateAppsScriptTypes = async ({
|
|
|
210
216
|
continue;
|
|
211
217
|
}
|
|
212
218
|
const sourceFile = declaration.getSourceFile();
|
|
213
|
-
|
|
219
|
+
const sourceFilePath = sourceFile.getFilePath();
|
|
220
|
+
if (sourceFilePath.includes("node_modules")) {
|
|
214
221
|
continue;
|
|
215
222
|
}
|
|
216
223
|
processedSymbols.add(symbolName);
|
|
217
|
-
const
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
const declText = declaration.getText();
|
|
221
|
-
inlineDefinitions.set(symbolName, declText);
|
|
222
|
-
const tempSourceFile = project.createSourceFile(
|
|
223
|
-
`__temp_${symbolName}.ts`,
|
|
224
|
-
declText
|
|
225
|
-
);
|
|
226
|
-
for (const descendant of tempSourceFile.getDescendantsOfKind(
|
|
227
|
-
tsMorph.SyntaxKind.TypeReference
|
|
228
|
-
)) {
|
|
229
|
-
const newSymbol = descendant.getType().getAliasSymbol() ?? descendant.getType().getSymbol();
|
|
230
|
-
if (newSymbol) {
|
|
231
|
-
symbolsToProcess.add(newSymbol);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
tempSourceFile.delete();
|
|
235
|
-
}
|
|
236
|
-
} else {
|
|
237
|
-
let modulePath = path__namespace.relative(absoluteOutDir, sourceFile.getFilePath()).replace(/\\/g, "/");
|
|
224
|
+
const isExternal = !sourceFilePath.replace(/\\/g, "/").startsWith(absoluteSrcDir.replace(/\\/g, "/")) && !sourceFilePath.replace(/\\/g, "/").startsWith(path__namespace.join(projectPath, srcDir).replace(/\\/g, "/"));
|
|
225
|
+
if (isExternal) {
|
|
226
|
+
let modulePath = path__namespace.relative(absoluteOutDir, sourceFilePath).replace(/\\/g, "/");
|
|
238
227
|
modulePath = modulePath.replace(/\.(d\.)?ts$/, "");
|
|
239
228
|
if (modulePath.endsWith("/index")) {
|
|
240
229
|
modulePath = modulePath.slice(0, -6);
|
|
@@ -249,6 +238,17 @@ const generateAppsScriptTypes = async ({
|
|
|
249
238
|
importsMap.set(modulePath, /* @__PURE__ */ new Set());
|
|
250
239
|
}
|
|
251
240
|
importsMap.get(modulePath)?.add(symbolName);
|
|
241
|
+
} else {
|
|
242
|
+
const declText = declaration.getText();
|
|
243
|
+
inlineDefinitions.set(symbolName, declText);
|
|
244
|
+
}
|
|
245
|
+
for (const descendant of declaration.getDescendantsOfKind(
|
|
246
|
+
tsMorph.SyntaxKind.TypeReference
|
|
247
|
+
)) {
|
|
248
|
+
const newSymbol = descendant.getType().getAliasSymbol() ?? descendant.getType().getSymbol();
|
|
249
|
+
if (newSymbol) {
|
|
250
|
+
symbolsToProcess.add(newSymbol);
|
|
251
|
+
}
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
if (!fs__namespace.existsSync(absoluteOutDir)) {
|
package/dist/vite.cjs
CHANGED
package/dist/vite.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
import { consola } from 'consola';
|
|
3
|
-
import { l as loadConfig, g as generateAppsScriptTypes } from './shared/gasnuki.
|
|
3
|
+
import { l as loadConfig, g as generateAppsScriptTypes } from './shared/gasnuki.Bcijv2Ub.mjs';
|
|
4
4
|
import 'node:fs';
|
|
5
5
|
import 'ts-morph';
|
|
6
6
|
import 'jiti';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ciderjs/gasnuki",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Type definitions and utilities for Google Apps Script client-side API",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"test:coverage:update": "pnpm test:coverage && pnpm update-coverage-badge",
|
|
37
37
|
"prepublish": "pnpm run generate && pnpm run check && pnpm run test:coverage:update && pnpm run build",
|
|
38
38
|
"pg:react": "pnpm -C playground/react",
|
|
39
|
+
"pg:react-vite": "pnpm -C playground/react-with-vite-plugin",
|
|
39
40
|
"pg:vue": "pnpm -C playground/vue3"
|
|
40
41
|
},
|
|
41
42
|
"keywords": [
|
|
@@ -62,7 +63,7 @@
|
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
65
|
"@biomejs/biome": "^2.2.6",
|
|
65
|
-
"@types/node": "^24.
|
|
66
|
+
"@types/node": "^24.8.0",
|
|
66
67
|
"@vitest/coverage-v8": "3.2.4",
|
|
67
68
|
"typescript": "^5.9.3",
|
|
68
69
|
"unbuild": "^3.6.1",
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
# `@ciderjs/gasnuki` Viteプラグイン 設計書
|
|
2
|
-
|
|
3
|
-
## 1. はじめに
|
|
4
|
-
|
|
5
|
-
### 1.1. 目的
|
|
6
|
-
|
|
7
|
-
本ドキュメントは、先に作成された「`@ciderjs/gasnuki` Viteプラグイン機能追加 要件定義書」に基づき、Viteプラグイン機能の具体的なアーキテクチャ、実装詳細、データ構造などを定義することを目的とする。
|
|
8
|
-
|
|
9
|
-
### 1.2. 対象読者
|
|
10
|
-
|
|
11
|
-
`@ciderjs/gasnuki` の開発者およびコントリビューターを対象とする。
|
|
12
|
-
|
|
13
|
-
## 2. アーキテクチャ概要
|
|
14
|
-
|
|
15
|
-
`@ciderjs/gasnuki` のViteプラグインは、Viteの開発サーバーのライフサイクルにフックすることで動作する。既存の型生成コアロジック (`generateAppsScriptTypes`) を再利用し、Viteの開発環境とシームレスに連携する。
|
|
16
|
-
|
|
17
|
-
### 2.1. 構成図
|
|
18
|
-
|
|
19
|
-
```mermaid
|
|
20
|
-
graph TD
|
|
21
|
-
subgraph Vite開発環境
|
|
22
|
-
A[vite.config.ts] --> B(Vite Dev Server);
|
|
23
|
-
B -- 起動 --> C{gasnuki Plugin};
|
|
24
|
-
B -- ファイル変更通知 --> C;
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
subgraph gasnuki Plugin
|
|
28
|
-
C -- configResolvedフック --> D[設定解決ロジック];
|
|
29
|
-
C -- configureServerフック --> E[初回生成 & ウォッチャー設定];
|
|
30
|
-
E -- ファイル変更イベント --> F[型再生成トリガー];
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
subgraph gasnuki コアモジュール
|
|
34
|
-
G[./modules/config.ts];
|
|
35
|
-
H[./modules/generate.ts];
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
D -- 読み込み --> G;
|
|
39
|
-
E -- 実行 --> H;
|
|
40
|
-
F -- 実行 --> H;
|
|
41
|
-
|
|
42
|
-
style Vite開発環境 fill:#e6f2ff,stroke:#b3d9ff
|
|
43
|
-
style gasnukiコアモジュール fill:#fff2e6,stroke:#ffdab3
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### 2.2. 処理フロー
|
|
47
|
-
|
|
48
|
-
1. **設定解決**: Vite開発サーバー起動時、`configResolved` フックが発火。Viteの設定情報 (`config.root`) を元にプロジェクトルートを特定し、プラグインオプション、`gasnuki.config.ts`、デフォルト値をマージして最終的な設定を確定する。
|
|
49
|
-
2. **初回生成**: `configureServer` フックで、開発サーバーの起動処理の一環として、型定義の初回生成 (`generateAppsScriptTypes`) を実行する。
|
|
50
|
-
3. **ファイル監視と再生成**: 同じく `configureServer` フック内で、Viteのファイルウォッチャー (`server.watcher`) を利用して、設定されたソースディレクトリ (`srcDir`) を監視する。ファイルの追加・変更・削除を検知すると、再度 `generateAppsScriptTypes` を実行して型定義を更新する。
|
|
51
|
-
|
|
52
|
-
## 3. ファイル構成
|
|
53
|
-
|
|
54
|
-
| ファイルパス | 状態 | 役割 |
|
|
55
|
-
| :------------------- | :--- | :-------------------------------------------------------------------------------------------------- |
|
|
56
|
-
| `src/vite.ts` | 新規 | Viteプラグインのロジックを実装する。プラグインのファクトリ関数 `gasnuki()` をエクスポートする。 |
|
|
57
|
-
| `package.json` | 変更 | `exports` フィールドに `./vite` エントリーポイントを追加し、Viteプラグインを外部から利用可能にする。 |
|
|
58
|
-
| `build.config.ts` | 変更 | `unbuild` のビルド対象に `src/vite.ts` を追加する。 |
|
|
59
|
-
| `src/modules/generate.ts` | 変更 | (必要であれば) Vite環境でのロギングやエラーハンドリングを改善するために軽微な修正を行う可能性がある。 |
|
|
60
|
-
| `src/modules/config.ts` | 変更なし | 既存のコンフィグローダーをそのまま利用する。 |
|
|
61
|
-
|
|
62
|
-
## 4. 詳細設計
|
|
63
|
-
|
|
64
|
-
### 4.1. Viteプラグインエントリーポイント (`src/vite.ts`)
|
|
65
|
-
|
|
66
|
-
プラグインは `gasnuki(options?: UserConfig): Plugin` というファクトリ関数として提供する。
|
|
67
|
-
|
|
68
|
-
```typescript:gasnuki Vite Plugin:src/vite.ts
|
|
69
|
-
import type { Plugin, ResolvedConfig, ViteDevServer } from 'vite';
|
|
70
|
-
import * as path from 'node:path';
|
|
71
|
-
import { consola } from 'consola';
|
|
72
|
-
import { generateAppsScriptTypes } from './modules/generate';
|
|
73
|
-
import { loadConfig, type UserConfig } from './modules/config';
|
|
74
|
-
import type { GenerateOptions } from './index';
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* A factory function to create the gasnuki Vite plugin.
|
|
78
|
-
* @param options - The user-defined configuration for gasnuki.
|
|
79
|
-
* @returns The Vite plugin object.
|
|
80
|
-
*/
|
|
81
|
-
export function gasnuki(options?: UserConfig): Plugin {
|
|
82
|
-
let projectRoot: string;
|
|
83
|
-
let gasnukiOptions: Omit<GenerateOptions, 'watch' | 'project'>;
|
|
84
|
-
|
|
85
|
-
// Debounce mechanism to prevent rapid-fire regeneration
|
|
86
|
-
let debounceTimer: NodeJS.Timeout | null = null;
|
|
87
|
-
const debounce = (func: () => void, timeout = 300) => {
|
|
88
|
-
if (debounceTimer) {
|
|
89
|
-
clearTimeout(debounceTimer);
|
|
90
|
-
}
|
|
91
|
-
debounceTimer = setTimeout(func, timeout);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const runGeneration = (triggeredBy?: string) => {
|
|
95
|
-
debounce(async () => {
|
|
96
|
-
const reason = triggeredBy ? ` (triggered by: ${triggeredBy})` : '';
|
|
97
|
-
consola.info(`[gasnuki] Generating AppsScript types${reason}...`);
|
|
98
|
-
try {
|
|
99
|
-
await generateAppsScriptTypes({
|
|
100
|
-
...gasnukiOptions,
|
|
101
|
-
project: projectRoot,
|
|
102
|
-
});
|
|
103
|
-
consola.success('[gasnuki] Type generation complete.');
|
|
104
|
-
} catch (e) {
|
|
105
|
-
consola.error(`[gasnuki] Type generation failed: ${(e as Error).message}`);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
name: 'vite-plugin-gasnuki',
|
|
112
|
-
apply: 'serve', // Apply only in serve mode
|
|
113
|
-
|
|
114
|
-
async configResolved(config: ResolvedConfig) {
|
|
115
|
-
projectRoot = config.root;
|
|
116
|
-
const fileConfig = await loadConfig(projectRoot);
|
|
117
|
-
const defaultOptions: Partial<GenerateOptions> = {
|
|
118
|
-
srcDir: 'server',
|
|
119
|
-
outDir: 'types',
|
|
120
|
-
outputFile: 'appsscript.ts',
|
|
121
|
-
};
|
|
122
|
-
gasnukiOptions = {
|
|
123
|
-
...defaultOptions,
|
|
124
|
-
...fileConfig,
|
|
125
|
-
...(options || {}),
|
|
126
|
-
};
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
async configureServer(server: ViteDevServer) {
|
|
130
|
-
const targetDir = path.resolve(projectRoot, gasnukiOptions.srcDir);
|
|
131
|
-
|
|
132
|
-
// Initial generation on server startup
|
|
133
|
-
runGeneration('server startup');
|
|
134
|
-
|
|
135
|
-
// Watch for file changes
|
|
136
|
-
server.watcher.on('all', (event, filePath) => {
|
|
137
|
-
// Normalize paths for reliable comparison
|
|
138
|
-
const normalizedFilePath = path.normalize(filePath);
|
|
139
|
-
const normalizedTargetDir = path.normalize(targetDir);
|
|
140
|
-
|
|
141
|
-
if (normalizedFilePath.startsWith(normalizedTargetDir)) {
|
|
142
|
-
runGeneration(path.relative(projectRoot, filePath));
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### 4.2. 設定解決ロジック (`configResolved` フック内)
|
|
151
|
-
|
|
152
|
-
1. `config.root` からプロジェクトのルートパスを取得する。
|
|
153
|
-
2. 既存の `loadConfig(projectRoot)` を呼び出し、`gasnuki.config.ts` の内容を読み込む。
|
|
154
|
-
3. 以下の優先順位で設定をマージし、`gasnukiOptions` として内部に保持する。
|
|
155
|
-
1. **高**: `vite.config.ts` でプラグインに渡された `options`
|
|
156
|
-
2. **中**: `gasnuki.config.ts` の内容
|
|
157
|
-
3. **低**: プラグイン内部で定義されたデフォルト値 (`srcDir`, `outDir`, `outputFile`)
|
|
158
|
-
|
|
159
|
-
### 4.3. ファイル監視ロジック (`configureServer` フック内)
|
|
160
|
-
|
|
161
|
-
- Viteの `server.watcher` を利用することで、`chokidar` を直接依存関係に追加する必要はない。
|
|
162
|
-
- `on('all', callback)` を使用し、ファイルの追加(`add`)、変更(`change`)、削除(`unlink`) すべてのイベントを捕捉する。
|
|
163
|
-
- イベントのコールバック内で、変更されたファイルパスが `gasnukiOptions.srcDir` 内に存在するかをチェックし、関係のないファイルの変更で再生成が走らないようにする。
|
|
164
|
-
- 頻繁なファイル変更による過剰な再生成を防ぐため、300ms程度のデバウンス処理を実装する。
|
|
165
|
-
|
|
166
|
-
## 5. データ構造・インターフェース
|
|
167
|
-
|
|
168
|
-
### 5.1. `UserConfig` (既存)
|
|
169
|
-
|
|
170
|
-
既存の `UserConfig` 型をそのままプラグインオプションの型として利用する。
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
// src/modules/config.ts (既存)
|
|
174
|
-
export type UserConfig = Partial<Omit<GenerateOptions, 'watch' | 'project'>>;
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## 6. シーケンス図
|
|
178
|
-
|
|
179
|
-
### 6.1. 開発サーバー起動時
|
|
180
|
-
|
|
181
|
-
```mermaid
|
|
182
|
-
sequenceDiagram
|
|
183
|
-
participant User
|
|
184
|
-
participant Vite
|
|
185
|
-
participant gasnukiPlugin as gasnuki Plugin
|
|
186
|
-
participant Core as gasnuki Core
|
|
187
|
-
|
|
188
|
-
User->>Vite: `vite dev` を実行
|
|
189
|
-
Vite->>gasnukiPlugin: configResolved フックを呼び出し
|
|
190
|
-
gasnukiPlugin->>Core: loadConfig()
|
|
191
|
-
Core-->>gasnukiPlugin: 設定ファイルを返す
|
|
192
|
-
gasnukiPlugin->>gasnukiPlugin: 設定をマージ
|
|
193
|
-
Vite->>gasnukiPlugin: configureServer フックを呼び出し
|
|
194
|
-
gasnukiPlugin->>gasnukiPlugin: runGeneration('server startup')
|
|
195
|
-
gasnukiPlugin->>Core: generateAppsScriptTypes()
|
|
196
|
-
Core-->>gasnukiPlugin: 生成完了
|
|
197
|
-
Note right of gasnukiPlugin: ファイルウォッチャーを設定
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### 6.2. ファイル変更時
|
|
201
|
-
|
|
202
|
-
```mermaid
|
|
203
|
-
sequenceDiagram
|
|
204
|
-
participant User
|
|
205
|
-
participant ViteWatcher
|
|
206
|
-
participant gasnukiPlugin as gasnuki Plugin
|
|
207
|
-
participant Core as gasnuki Core
|
|
208
|
-
|
|
209
|
-
User->>ViteWatcher: ソースファイルを変更・保存
|
|
210
|
-
ViteWatcher->>gasnukiPlugin: 'all' イベントを発火
|
|
211
|
-
gasnukiPlugin->>gasnukiPlugin: runGeneration('path/to/file.ts')
|
|
212
|
-
Note right of gasnukiPlugin: (デバウンス処理)
|
|
213
|
-
gasnukiPlugin->>Core: generateAppsScriptTypes()
|
|
214
|
-
Core-->>gasnukiPlugin: 再生成完了
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
## 7. その他考慮事項
|
|
218
|
-
|
|
219
|
-
- **エラー表示**: `generateAppsScriptTypes` が例外をスローした場合、`consola.error` を使用してViteのコンソールにスタックトレースを含む詳細なエラー情報を出力する。これにより、開発者はエラーの原因を特定しやすくなる。
|
|
220
|
-
- **テスト**: Viteプラグインの動作を検証するための単体テスト・結合テストを追加する。`vite` を `devDependencies` に追加し、テスト環境内でViteサーバーをプログラム的に起動してプラグインの各フックが正しく動作するかを確認する。
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# `@ciderjs/gasnuki` Viteプラグイン機能追加 要件定義書
|
|
2
|
-
|
|
3
|
-
## 1. 概要
|
|
4
|
-
|
|
5
|
-
本ドキュメントは、npmパッケージ `@ciderjs/gasnuki` にViteプラグイン機能を追加するための要件を定義するものである。
|
|
6
|
-
|
|
7
|
-
## 2. 背景・目的
|
|
8
|
-
|
|
9
|
-
`@ciderjs/gasnuki` は、Google Apps Script (GAS) のサーバーサイドコードからTypeScriptの型定義を生成するCLIツールである。現状では、開発者はCLIを単独で、または `--watch` モードで実行する必要がある。
|
|
10
|
-
|
|
11
|
-
Viteを利用したフロントエンド開発プロジェクトにおいて、`vite dev` コマンドによる開発サーバーのライフサイクルと `gasnuki` の型生成プロセスを連携させることで、以下の目的を達成する。
|
|
12
|
-
|
|
13
|
-
- **開発体験の向上**: 開発者は `vite dev` を実行するだけで、GASコードの変更が自動的にクライアントサイドの型定義に反映されるようになり、手動でのコマンド実行や別プロセスの管理が不要になる。
|
|
14
|
-
- **設定の統合**: `gasnuki` に関する設定を `vite.config.ts` 内に集約し、プロジェクト設定の一元管理を促進する。
|
|
15
|
-
- **シームレスな統合**: Viteの開発エコシステムに `gasnuki` をスムーズに統合し、導入のハードルを下げる。
|
|
16
|
-
|
|
17
|
-
## 3. スコープ
|
|
18
|
-
|
|
19
|
-
### 3.1. スコープ内
|
|
20
|
-
|
|
21
|
-
- Viteの開発サーバー (`vite dev` / `serve`) 実行時に動作するViteプラグインの開発。
|
|
22
|
-
- プラグイン経由での `gasnuki` の設定。
|
|
23
|
-
- 開発サーバー起動時の初回型定義生成。
|
|
24
|
-
- ソースファイルの変更を監視し、型定義を自動で再生成する機能。
|
|
25
|
-
|
|
26
|
-
### 3.2. スコープ外
|
|
27
|
-
|
|
28
|
-
- Viteのビルドプロセス (`vite build`) へのフック。`gasnuki` は開発時の型安全性を目的とするため、本番ビルドには関与しない。
|
|
29
|
-
- CLI機能の変更・廃止。既存のCLI機能はそのまま維持する。
|
|
30
|
-
|
|
31
|
-
## 4. 機能要件
|
|
32
|
-
|
|
33
|
-
| ID | 機能名 | 種別 | 概要 |
|
|
34
|
-
| :--- | :----------------------------- | :--- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
-
| F-01 | **プラグインインターフェース** | 新規 | `import { gasnuki } from '@ciderjs/gasnuki/vite'` のようにインポートできるエントリーポイントを提供する。<br>プラグインは `gasnuki(options?: UserConfig)` という形式のファクトリ関数として提供する。 |
|
|
36
|
-
| F-02 | **設定の解決** | 変更 | プラグインに `options` が渡された場合はその設定を優先する。<br>`options` がない場合は、プロジェクトルートの `gasnuki.config.ts` 等を自動で読み込む。<br>プロジェクトルートの特定にはViteのコンフィグを利用する。 |
|
|
37
|
-
| F-03 | **初回型定義生成** | 新規 | `vite dev` コマンドで開発サーバーが起動する際に、型定義の生成処理を一度実行する。 |
|
|
38
|
-
| F-04 | **ファイル変更時の自動再生成** | 新規 | 開発サーバーの起動中、設定された `srcDir` 内の `.ts` ファイルの変更(追加・更新・削除)を監視し、変更があった場合に型定義の再生成を自動で実行する。 |
|
|
39
|
-
| F-05 | **既存機能との連携** | 変更 | 型定義の生成ロジック (`generateAppsScriptTypes`) や設定ファイルの読み込みロジック (`loadConfig`) など、既存のコア機能を再利用して実装する。 |
|
|
40
|
-
|
|
41
|
-
## 5. 非機能要件
|
|
42
|
-
|
|
43
|
-
| ID | 要件項目 | 概要 |
|
|
44
|
-
| :---- | :----------------- | :----------------------------------------------------------------------------------------------------- |
|
|
45
|
-
| NF-01 | **パフォーマンス** | ファイル変更時の再生成処理が、開発サーバーの応答性やパフォーマンスに大きな影響を与えないこと。 |
|
|
46
|
-
| NF-02 | **ユーザビリティ** | `vite.config.ts` に数行追加するだけで導入できる、シンプルで分かりやすいAPIを提供すること。 |
|
|
47
|
-
| NF-03 | **フィードバック** | 型定義の生成・再生成の開始、成功、エラー発生時には、Viteのコンソールに適切なメッセージを出力すること。 |
|
|
48
|
-
| NF-04 | **互換性** | 既存のCLI機能とViteプラグイン機能が共存でき、互いに影響を与えないこと。 |
|
|
49
|
-
|
|
50
|
-
## 6. 実装方針
|
|
51
|
-
|
|
52
|
-
### 6.1. パッケージ構成
|
|
53
|
-
|
|
54
|
-
- プラグインのロジックを実装するファイル (例: `src/vite.ts`) を新設する。
|
|
55
|
-
- `package.json` の `exports` フィールドに、Viteプラグイン用のエントリーポイント (例: `./vite`) を追加する。
|
|
56
|
-
- ビルド設定 (`build.config.ts`) を更新し、Viteプラグイン用のファイルを成果物として出力する。
|
|
57
|
-
|
|
58
|
-
### 6.2. Viteプラグインフックの利用
|
|
59
|
-
|
|
60
|
-
- **`name`**: プラグインを識別するための一意な名前 (例: `vite-plugin-gasnuki`) を設定する。
|
|
61
|
-
- **`apply: 'serve'`**: 開発サーバーでのみプラグインが動作するように制限する。
|
|
62
|
-
- **`configResolved(config)`**: Viteの設定が解決された後に呼び出されるフック。`config.root` を基準にプロジェクトルートを特定し、`gasnuki` の最終的な設定オブジェクトをここで確定させる。
|
|
63
|
-
- **`configureServer(server)`**: 開発サーバーが構成された際に呼び出されるフック。
|
|
64
|
-
- サーバー起動処理の一環として、初回の型生成処理を非同期で実行する。
|
|
65
|
-
- `server.watcher` (Viteが内部で使用する `chokidar` インスタンス) を利用して、対象ソースファイルの変更イベントを購読する。
|
|
66
|
-
- ファイル変更イベント発生時に、型定義の再生成処理を呼び出す。
|
|
67
|
-
|
|
68
|
-
## 7. ドキュメント
|
|
69
|
-
|
|
70
|
-
- `README.md` および `README.ja.md` を更新し、「Viteプラグインとしての利用」のセクションを追加する。
|
|
71
|
-
- `vite.config.ts` への基本的な設定例を記載する。
|
|
72
|
-
- プラグインに渡せるオプションについて説明する。
|