@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # gasnuki
2
2
 
3
- [![Test Coverage](https://img.shields.io/badge/test%20coverage-84.76%25-green)](https://github.com/luthpg/gasnuki)
3
+ [![Test Coverage](https://img.shields.io/badge/test%20coverage-90.91%25-brightgreen)](https://github.com/luthpg/gasnuki)
4
4
  [![License](https://img.shields.io/badge/license-ISC-blue.svg)](LICENSE)
5
5
  [![npm version](https://img.shields.io/npm/v/@ciderjs/gasnuki.svg)](https://www.npmjs.com/package/@ciderjs/gasnuki)
6
6
  [![GitHub issues](https://img.shields.io/github/issues/luthpg/gasnuki.svg)](https://github.com/luthpg/gasnuki/issues)
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ciderjs/gasnuki
2
2
 
3
- [![Test Coverage](https://img.shields.io/badge/test%20coverage-84.76%25-green)](https://github.com/luthpg/gasnuki)
3
+ [![Test Coverage](https://img.shields.io/badge/test%20coverage-90.91%25-brightgreen)](https://github.com/luthpg/gasnuki)
4
4
  [![License](https://img.shields.io/badge/license-ISC-blue.svg)](LICENSE)
5
5
  [![npm version](https://img.shields.io/npm/v/@ciderjs/gasnuki.svg)](https://www.npmjs.com/package/@ciderjs/gasnuki)
6
6
  [![GitHub issues](https://img.shields.io/github/issues/luthpg/gasnuki.svg)](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.CxoM0Mts.cjs');
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.0";
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.BYl_yopE.mjs';
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.0";
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.CxoM0Mts.cjs');
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.BYl_yopE.mjs';
5
- export { d as defineConfig } from './shared/gasnuki.BYl_yopE.mjs';
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
- if (sourceFile.getFilePath().includes("node_modules")) {
202
+ const sourceFilePath = sourceFile.getFilePath();
203
+ if (sourceFilePath.includes("node_modules")) {
197
204
  continue;
198
205
  }
199
206
  processedSymbols.add(symbolName);
200
- const isLocalDefinition = declaration.getParent()?.getKind() !== SyntaxKind.SourceFile;
201
- if (isLocalDefinition) {
202
- if (returnValueSymbols.has(symbol)) {
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
- if (sourceFile.getFilePath().includes("node_modules")) {
219
+ const sourceFilePath = sourceFile.getFilePath();
220
+ if (sourceFilePath.includes("node_modules")) {
214
221
  continue;
215
222
  }
216
223
  processedSymbols.add(symbolName);
217
- const isLocalDefinition = declaration.getParent()?.getKind() !== tsMorph.SyntaxKind.SourceFile;
218
- if (isLocalDefinition) {
219
- if (returnValueSymbols.has(symbol)) {
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  const path = require('node:path');
4
4
  const consola = require('consola');
5
- const config = require('./shared/gasnuki.CxoM0Mts.cjs');
5
+ const config = require('./shared/gasnuki.Cbwq1CES.cjs');
6
6
  require('node:fs');
7
7
  require('ts-morph');
8
8
  require('jiti');
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.BYl_yopE.mjs';
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.0",
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.7.2",
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
- - プラグインに渡せるオプションについて説明する。