vite-plugin-gas-react 0.1.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/LICENSE +21 -0
- package/README.md +221 -0
- package/dist/chunk-DXCNTLXT.js +308 -0
- package/dist/chunk-DXCNTLXT.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/vite-plugin-gas-UCZ4473C.js +7 -0
- package/dist/vite-plugin-gas-UCZ4473C.js.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sarfraj Akhtar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# vite-plugin-gas-react
|
|
2
|
+
|
|
3
|
+
**Deploy React apps to Google Apps Script — with code splitting.**
|
|
4
|
+
|
|
5
|
+
> Write a standard React + Vite app. Run one command. Get a working GAS web app with lazy-loaded pages.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Why?
|
|
10
|
+
|
|
11
|
+
Google Apps Script only serves HTML files via `HtmlService`. External `<script>` tags are blocked by the CAJA sanitizer. That means:
|
|
12
|
+
|
|
13
|
+
- No ES modules, no `import()`, no `<script src="...">`
|
|
14
|
+
- You normally have to inline **everything** into a single HTML file
|
|
15
|
+
- Code splitting? Lazy loading? Forget it.
|
|
16
|
+
|
|
17
|
+
This plugin solves all of that. You write a normal React app with `React.lazy()` and Vite's natural code splitting — the plugin transforms the build output into something GAS can actually serve.
|
|
18
|
+
|
|
19
|
+
## How It Works
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
React App (Vite) → Build → GAS-compatible output → clasp push → Live web app
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The plugin runs at build time and:
|
|
26
|
+
|
|
27
|
+
1. **Stores all JS server-side** as `.gs` string variables (completely bypasses CAJA)
|
|
28
|
+
2. **Rewrites `import()`** calls to fetch chunks via `google.script.run.getPage()`
|
|
29
|
+
3. **Builds a dependency graph** so shared chunks load before the pages that need them
|
|
30
|
+
4. **Generates `Code.js`** with `doGet()`, `getEntryCode()`, and `getPage()` functions
|
|
31
|
+
5. **Generates `appsscript.json`** with the correct webapp configuration
|
|
32
|
+
|
|
33
|
+
At runtime, the entry JS is loaded via `google.script.run.getEntryCode()` and injected into a `<script>` tag. When you navigate to a lazy page, the chunk loader fetches its code (and any shared dependencies) the same way.
|
|
34
|
+
|
|
35
|
+
### Build Output
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
dist/
|
|
39
|
+
├── index.html ← Served by doGet() — contains the chunk loader
|
|
40
|
+
├── __gas_entry__.js ← Entry bundle stored as a .gs string variable
|
|
41
|
+
├── __gas_chunks__.js ← All lazy + shared chunks as .gs string variables
|
|
42
|
+
├── Code.js ← Server functions: doGet(), getEntryCode(), getPage()
|
|
43
|
+
└── appsscript.json ← GAS project manifest
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
### 1. Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install vite-plugin-gas-react
|
|
52
|
+
npm install -D vite @vitejs/plugin-react
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. Configure Vite
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
// vite.config.ts
|
|
59
|
+
import { createGASViteConfig } from 'vite-plugin-gas-react';
|
|
60
|
+
|
|
61
|
+
export default createGASViteConfig({
|
|
62
|
+
clientRoot: 'src',
|
|
63
|
+
appTitle: 'My App',
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
That's it. `createGASViteConfig()` sets up React, the GAS plugin, aliases, and dev mode automatically.
|
|
68
|
+
|
|
69
|
+
### 3. Write Your App
|
|
70
|
+
|
|
71
|
+
Use `React.lazy()` for page-level code splitting — Vite will split them into separate chunks, and the plugin will handle the rest:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// src/App.tsx
|
|
75
|
+
import { useState, Suspense, lazy } from 'react';
|
|
76
|
+
|
|
77
|
+
const Home = lazy(() => import('./pages/Home'));
|
|
78
|
+
const Settings = lazy(() => import('./pages/Settings'));
|
|
79
|
+
|
|
80
|
+
const pages = { Home, Settings };
|
|
81
|
+
|
|
82
|
+
export default function App() {
|
|
83
|
+
const [page, setPage] = useState('Home');
|
|
84
|
+
const Page = pages[page];
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
88
|
+
<Page />
|
|
89
|
+
</Suspense>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
// src/pages/Home.tsx
|
|
96
|
+
export default function Home() {
|
|
97
|
+
return <h1>Home Page</h1>;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. Set Up Clasp
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install -g @google/clasp
|
|
105
|
+
clasp login
|
|
106
|
+
clasp create --type webapp --rootDir dist
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 5. Build & Deploy
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx vite build
|
|
113
|
+
cd dist && clasp push
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Open the GAS web app URL and your React app is live.
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
### `createGASViteConfig(options?)`
|
|
121
|
+
|
|
122
|
+
Returns a complete Vite config. All options are optional:
|
|
123
|
+
|
|
124
|
+
| Option | Default | Description |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `clientRoot` | `'src/client'` | Path to client source directory |
|
|
127
|
+
| `outDir` | `'dist'` | Build output directory |
|
|
128
|
+
| `appTitle` | `'GAS App'` | Title shown in the browser tab |
|
|
129
|
+
| `devServerPort` | `3001` | Port for local dev API server |
|
|
130
|
+
| `devPort` | `5173` | Vite dev server port |
|
|
131
|
+
| `aliases` | `{}` | Additional path aliases (`@` → `src/` is automatic) |
|
|
132
|
+
| `plugins` | `[]` | Additional Vite plugins |
|
|
133
|
+
| `vite` | `{}` | Override/extend any Vite config option |
|
|
134
|
+
|
|
135
|
+
### `gasPlugin(options?)`
|
|
136
|
+
|
|
137
|
+
Use this if you're building your own Vite config instead of using `createGASViteConfig()`:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { gasPlugin } from 'vite-plugin-gas-react';
|
|
141
|
+
import react from '@vitejs/plugin-react';
|
|
142
|
+
|
|
143
|
+
export default {
|
|
144
|
+
plugins: [react(), gasPlugin({ appTitle: 'My App' })],
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Option | Default | Description |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `pagePrefix` | `'page_'` | Prefix for page chunk names |
|
|
151
|
+
| `appTitle` | `'GAS App'` | Web app title |
|
|
152
|
+
|
|
153
|
+
### `isLocalDev()`
|
|
154
|
+
|
|
155
|
+
Returns `true` when `GAS_LOCAL=true` is set in environment. Use to branch behavior between local development and GAS deployment:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
if (isLocalDev()) {
|
|
159
|
+
// Local dev: use mock data or local API
|
|
160
|
+
} else {
|
|
161
|
+
// Production: use google.script.run
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## How Code Splitting Works
|
|
166
|
+
|
|
167
|
+
Vite naturally splits your app into chunks:
|
|
168
|
+
|
|
169
|
+
- **Entry chunk** — React, your app shell, shared dependencies
|
|
170
|
+
- **Page chunks** — One per `React.lazy(() => import('./pages/X'))` call
|
|
171
|
+
- **Shared lib chunks** — Common dependencies used by multiple pages (e.g., MUI components)
|
|
172
|
+
|
|
173
|
+
The plugin transforms these into GAS-compatible form:
|
|
174
|
+
|
|
175
|
+
| Vite Output | Plugin Transform | GAS Runtime |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| `assets/index-abc.js` (entry) | `__GAS_ENTRY_CODE__` string variable | Loaded via `getEntryCode()` |
|
|
178
|
+
| `assets/Home-xyz.js` (page) | `__GAS_CHUNK_page_Home__` string variable | Loaded via `getPage('page_Home')` |
|
|
179
|
+
| `assets/Stack-def.js` (shared lib) | `__GAS_CHUNK_lib_Stack__` string variable | Auto-loaded as dependency |
|
|
180
|
+
|
|
181
|
+
Each chunk type gets its own **isolated namespace** to prevent variable collisions:
|
|
182
|
+
|
|
183
|
+
- Entry exports → `window.__gasEntry__`
|
|
184
|
+
- Shared lib exports → `window.__gasLib_<name>__`
|
|
185
|
+
- Page exports → `window.__gasChunkExports` (per-chunk, cleaned up after load)
|
|
186
|
+
|
|
187
|
+
The plugin automatically builds a dependency graph. When you navigate to `Home`, the loader first loads `lib_Stack` (if not cached), then loads `page_Home`. All subsequent navigations to pages sharing the same libs skip reloading them.
|
|
188
|
+
|
|
189
|
+
## Local Development
|
|
190
|
+
|
|
191
|
+
Set `GAS_LOCAL=true` to run your app with Vite's dev server instead of deploying to GAS:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
GAS_LOCAL=true npx vite
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
In local mode, `createGASViteConfig()`:
|
|
198
|
+
- Skips the GAS plugin entirely
|
|
199
|
+
- Injects `window.__GAS_DEV_MODE__ = true`
|
|
200
|
+
- Injects `window.__GAS_DEV_SERVER__` pointing to your local API server
|
|
201
|
+
- Enables Vite HMR and hot reload
|
|
202
|
+
|
|
203
|
+
You can use these globals in your app to switch between `google.script.run` calls (production) and `fetch()` calls (local dev).
|
|
204
|
+
|
|
205
|
+
## Requirements
|
|
206
|
+
|
|
207
|
+
- **Node.js** ≥ 18
|
|
208
|
+
- **Vite** ≥ 5
|
|
209
|
+
- **React** 18 or 19 (with `React.lazy()` for code splitting)
|
|
210
|
+
- **clasp** CLI for deployment (`npm install -g @google/clasp`)
|
|
211
|
+
|
|
212
|
+
## Limitations
|
|
213
|
+
|
|
214
|
+
- **GAS execution time limits** still apply (6 min/execution, 30 sec for web app requests)
|
|
215
|
+
- **Chunk loading** adds a round-trip per chunk on first navigation (chunks are cached after first load)
|
|
216
|
+
- **No SSR** — this is a client-side React app served via `HtmlService`
|
|
217
|
+
- **File size** — GAS has a 50MB total project size limit. Large apps with many dependencies should be fine, but keep an eye on it.
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
MIT
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// src/vite-plugin-gas.ts
|
|
2
|
+
import path from "path";
|
|
3
|
+
function gasPlugin(options = {}) {
|
|
4
|
+
const { pagePrefix = "page_", appTitle = "GAS App", serverEntry } = options;
|
|
5
|
+
const lazyPageNames = /* @__PURE__ */ new Set();
|
|
6
|
+
const fileToGasName = /* @__PURE__ */ new Map();
|
|
7
|
+
function toGasName(fileName, isLazyPage) {
|
|
8
|
+
const baseName = fileName.split("/").pop();
|
|
9
|
+
const cleanName = baseName.replace(/-.*$/, "").replace(/\.js$/, "");
|
|
10
|
+
return isLazyPage ? `${pagePrefix}${cleanName}` : `lib_${cleanName}`;
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
name: "vite-plugin-gas",
|
|
14
|
+
enforce: "post",
|
|
15
|
+
renderChunk(code, chunk) {
|
|
16
|
+
if (!chunk.isEntry) return null;
|
|
17
|
+
let modified = code;
|
|
18
|
+
let changed = false;
|
|
19
|
+
for (const dynamicImport of chunk.dynamicImports) {
|
|
20
|
+
const baseName = dynamicImport.split("/").pop();
|
|
21
|
+
const pageName = baseName.replace(/-.*$/, "").replace(/\.js$/, "");
|
|
22
|
+
const gasPageName = `${pagePrefix}${pageName}`;
|
|
23
|
+
lazyPageNames.add(pageName);
|
|
24
|
+
const escapedBase = baseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25
|
+
const importPattern = new RegExp(
|
|
26
|
+
`import\\(\\s*["']\\.\\/` + escapedBase + `["']\\s*\\)`,
|
|
27
|
+
"g"
|
|
28
|
+
);
|
|
29
|
+
const replacement = `__gasLoadChunk("${gasPageName}")`;
|
|
30
|
+
const newCode = modified.replace(importPattern, replacement);
|
|
31
|
+
if (newCode !== modified) {
|
|
32
|
+
modified = newCode;
|
|
33
|
+
changed = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return changed ? modified : null;
|
|
37
|
+
},
|
|
38
|
+
transformIndexHtml(html) {
|
|
39
|
+
const gasScript = `
|
|
40
|
+
<script>
|
|
41
|
+
(function() {
|
|
42
|
+
var deps = __GAS_DEPS_MAP__;
|
|
43
|
+
var chunkCache = {};
|
|
44
|
+
|
|
45
|
+
window.__gasLoadChunk = function(name) {
|
|
46
|
+
if (chunkCache[name]) return chunkCache[name];
|
|
47
|
+
|
|
48
|
+
var promise = new Promise(function(resolve, reject) {
|
|
49
|
+
var chunkDeps = deps[name] || [];
|
|
50
|
+
var depPromises = chunkDeps.map(function(dep) { return window.__gasLoadChunk(dep); });
|
|
51
|
+
|
|
52
|
+
Promise.all(depPromises).then(function() {
|
|
53
|
+
google.script.run
|
|
54
|
+
.withSuccessHandler(function(js) {
|
|
55
|
+
var script = document.createElement('script');
|
|
56
|
+
script.textContent = js;
|
|
57
|
+
document.head.appendChild(script);
|
|
58
|
+
|
|
59
|
+
var exports = window.__gasChunkExports || {};
|
|
60
|
+
resolve(exports);
|
|
61
|
+
delete window.__gasChunkExports;
|
|
62
|
+
})
|
|
63
|
+
.withFailureHandler(function(err) {
|
|
64
|
+
reject(new Error('Failed to load chunk: ' + name));
|
|
65
|
+
})
|
|
66
|
+
.getPage(name);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
chunkCache[name] = promise;
|
|
71
|
+
return promise;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
google.script.run
|
|
75
|
+
.withSuccessHandler(function(js) {
|
|
76
|
+
var s = document.createElement('script');
|
|
77
|
+
s.textContent = js;
|
|
78
|
+
document.head.appendChild(s);
|
|
79
|
+
})
|
|
80
|
+
.withFailureHandler(function(err) {
|
|
81
|
+
document.body.innerHTML = 'Failed to load app: ' + err;
|
|
82
|
+
})
|
|
83
|
+
.getEntryCode();
|
|
84
|
+
})();
|
|
85
|
+
</script>`;
|
|
86
|
+
html = html.replace("</body>", gasScript + "\n</body>");
|
|
87
|
+
return html;
|
|
88
|
+
},
|
|
89
|
+
async generateBundle(_options, bundle) {
|
|
90
|
+
let entryFileName = null;
|
|
91
|
+
let entryCode = null;
|
|
92
|
+
for (const [fileName, item] of Object.entries(bundle)) {
|
|
93
|
+
if (item.type === "chunk" && item.isEntry) {
|
|
94
|
+
entryFileName = fileName;
|
|
95
|
+
entryCode = item.code || "";
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (entryFileName && entryCode) {
|
|
100
|
+
const htmlKey = Object.keys(bundle).find((k) => k.endsWith(".html"));
|
|
101
|
+
if (htmlKey) {
|
|
102
|
+
const htmlItem = bundle[htmlKey];
|
|
103
|
+
let html = typeof htmlItem.source === "string" ? htmlItem.source : new TextDecoder().decode(htmlItem.source);
|
|
104
|
+
html = html.replace(
|
|
105
|
+
/<script\b[^>]*src=["'][^"']*["'][^>]*>\s*<\/script>/g,
|
|
106
|
+
""
|
|
107
|
+
);
|
|
108
|
+
for (const [fileName, item] of Object.entries(bundle)) {
|
|
109
|
+
if (item.type !== "chunk") continue;
|
|
110
|
+
if (fileName === entryFileName) continue;
|
|
111
|
+
const baseName = fileName.split("/").pop();
|
|
112
|
+
const cleanName = baseName.replace(/-.*$/, "").replace(/\.js$/, "");
|
|
113
|
+
const isLazy = lazyPageNames.has(cleanName);
|
|
114
|
+
fileToGasName.set(fileName, toGasName(fileName, isLazy));
|
|
115
|
+
}
|
|
116
|
+
let cleanedEntry = rewriteImportsToGlobals(entryCode, entryFileName, fileToGasName);
|
|
117
|
+
cleanedEntry = rewriteExportsToNamespace(cleanedEntry, "window.__gasEntry__");
|
|
118
|
+
cleanedEntry = `window.__gasEntry__={};
|
|
119
|
+
var __VITE_PRELOAD__=void 0;
|
|
120
|
+
` + cleanedEntry;
|
|
121
|
+
bundle["__gas_entry__.js"] = {
|
|
122
|
+
type: "asset",
|
|
123
|
+
fileName: "__gas_entry__.js",
|
|
124
|
+
source: `var __GAS_ENTRY_CODE__ = ${JSON.stringify(cleanedEntry)};`
|
|
125
|
+
};
|
|
126
|
+
delete bundle[entryFileName];
|
|
127
|
+
const chunkVars = [];
|
|
128
|
+
const depsMap = {};
|
|
129
|
+
for (const [fileName, item] of Object.entries(bundle)) {
|
|
130
|
+
if (item.type !== "chunk") continue;
|
|
131
|
+
if (fileName === entryFileName) continue;
|
|
132
|
+
const gasName = fileToGasName.get(fileName);
|
|
133
|
+
if (!gasName) continue;
|
|
134
|
+
const chunkInfo = item;
|
|
135
|
+
let code = item.code || "";
|
|
136
|
+
code = rewriteImportsToGlobals(code, fileName, fileToGasName);
|
|
137
|
+
const baseName = fileName.split("/").pop();
|
|
138
|
+
const cleanName = baseName.replace(/-.*$/, "").replace(/\.js$/, "");
|
|
139
|
+
const isLazy = lazyPageNames.has(cleanName);
|
|
140
|
+
if (isLazy) {
|
|
141
|
+
code = rewriteExportsToObject(code);
|
|
142
|
+
const wrappedCode = `(function() {
|
|
143
|
+
var __exports = {};
|
|
144
|
+
${code}
|
|
145
|
+
window.__gasChunkExports = __exports;
|
|
146
|
+
})();`;
|
|
147
|
+
chunkVars.push(`var __GAS_CHUNK_${gasName}__ = ${JSON.stringify(wrappedCode)};`);
|
|
148
|
+
} else {
|
|
149
|
+
code = rewriteExportsToObject(code);
|
|
150
|
+
const wrappedCode = `(function() {
|
|
151
|
+
var __exports = {};
|
|
152
|
+
${code}
|
|
153
|
+
window.__gasLib_${gasName}__ = __exports;
|
|
154
|
+
})();`;
|
|
155
|
+
chunkVars.push(`var __GAS_CHUNK_${gasName}__ = ${JSON.stringify(wrappedCode)};`);
|
|
156
|
+
}
|
|
157
|
+
const chunkDeps = [];
|
|
158
|
+
if (chunkInfo.imports) {
|
|
159
|
+
for (const imp of chunkInfo.imports) {
|
|
160
|
+
const depGasName = fileToGasName.get(imp);
|
|
161
|
+
if (depGasName) chunkDeps.push(depGasName);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (chunkDeps.length > 0) {
|
|
165
|
+
depsMap[gasName] = chunkDeps;
|
|
166
|
+
}
|
|
167
|
+
delete bundle[fileName];
|
|
168
|
+
}
|
|
169
|
+
if (chunkVars.length > 0) {
|
|
170
|
+
bundle["__gas_chunks__.js"] = {
|
|
171
|
+
type: "asset",
|
|
172
|
+
fileName: "__gas_chunks__.js",
|
|
173
|
+
source: chunkVars.join("\n")
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const depsJson = JSON.stringify(depsMap);
|
|
177
|
+
html = html.replace("__GAS_DEPS_MAP__", depsJson);
|
|
178
|
+
htmlItem.source = html;
|
|
179
|
+
let codeJs = "";
|
|
180
|
+
if (serverEntry) {
|
|
181
|
+
try {
|
|
182
|
+
const esbuild = await import("esbuild");
|
|
183
|
+
const serverPath = path.resolve(process.cwd(), serverEntry);
|
|
184
|
+
const result = await esbuild.build({
|
|
185
|
+
entryPoints: [serverPath],
|
|
186
|
+
bundle: true,
|
|
187
|
+
format: "esm",
|
|
188
|
+
platform: "neutral",
|
|
189
|
+
target: "es2020",
|
|
190
|
+
write: false,
|
|
191
|
+
external: []
|
|
192
|
+
});
|
|
193
|
+
let serverCode = result.outputFiles[0].text;
|
|
194
|
+
serverCode = serverCode.replace(/^export\s*\{[^}]*\};\s*$/gm, "");
|
|
195
|
+
serverCode = serverCode.replace(/^export\s+/gm, "");
|
|
196
|
+
codeJs += serverCode + "\n";
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error("Failed to bundle server entry:", err);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
codeJs += `function doGet() {
|
|
202
|
+
return HtmlService.createHtmlOutputFromFile('index')
|
|
203
|
+
.setTitle('${appTitle.replace(/'/g, "\\'")}')
|
|
204
|
+
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function getPage(name) {
|
|
208
|
+
var varName = '__GAS_CHUNK_' + name + '__';
|
|
209
|
+
var code = globalThis[varName];
|
|
210
|
+
if (!code) throw new Error('Chunk not found: ' + name);
|
|
211
|
+
return code;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function getEntryCode() {
|
|
215
|
+
return __GAS_ENTRY_CODE__;
|
|
216
|
+
}
|
|
217
|
+
`;
|
|
218
|
+
bundle["Code.js"] = {
|
|
219
|
+
type: "asset",
|
|
220
|
+
fileName: "Code.js",
|
|
221
|
+
source: codeJs
|
|
222
|
+
};
|
|
223
|
+
bundle["appsscript.json"] = {
|
|
224
|
+
type: "asset",
|
|
225
|
+
fileName: "appsscript.json",
|
|
226
|
+
source: JSON.stringify({
|
|
227
|
+
timeZone: "Asia/Kolkata",
|
|
228
|
+
dependencies: {},
|
|
229
|
+
webapp: {
|
|
230
|
+
access: "ANYONE_ANONYMOUS",
|
|
231
|
+
executeAs: "USER_DEPLOYING"
|
|
232
|
+
},
|
|
233
|
+
exceptionLogging: "STACKDRIVER",
|
|
234
|
+
runtimeVersion: "V8"
|
|
235
|
+
}, null, 2)
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function rewriteExportsToNamespace(code, namespace) {
|
|
243
|
+
let result = code;
|
|
244
|
+
result = result.replace(/export\s*\{([^}]*)\}\s*;?/g, (_match, inner) => {
|
|
245
|
+
return inner.split(",").map((binding) => {
|
|
246
|
+
const parts = binding.trim().split(/\s+as\s+/);
|
|
247
|
+
const local = parts[0].trim();
|
|
248
|
+
const exported = (parts[1] || parts[0]).trim();
|
|
249
|
+
return exported ? `${namespace}.${exported}=${local};` : "";
|
|
250
|
+
}).filter(Boolean).join("");
|
|
251
|
+
});
|
|
252
|
+
result = result.replace(/export\s+default\s+(\w+)\s*;/g, `${namespace}.default=$1;`);
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
function rewriteImportsToGlobals(code, chunkFileName, fileToGasName) {
|
|
256
|
+
const lastSlash = chunkFileName.lastIndexOf("/");
|
|
257
|
+
const chunkDir = lastSlash >= 0 ? chunkFileName.substring(0, lastSlash) : "";
|
|
258
|
+
return code.replace(
|
|
259
|
+
/import\s*\{([^}]+)\}\s*from\s*["']([^"']+)["']\s*;?/g,
|
|
260
|
+
(_match, bindings, importPath) => {
|
|
261
|
+
let sourceObj = "window";
|
|
262
|
+
if (importPath.startsWith("./")) {
|
|
263
|
+
const resolved = chunkDir ? `${chunkDir}/${importPath.slice(2)}` : importPath.slice(2);
|
|
264
|
+
const gasName = fileToGasName.get(resolved);
|
|
265
|
+
if (gasName?.startsWith("lib_")) {
|
|
266
|
+
sourceObj = `window.__gasLib_${gasName}__`;
|
|
267
|
+
} else if (!gasName) {
|
|
268
|
+
sourceObj = "window.__gasEntry__";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return bindings.split(",").map((binding) => {
|
|
272
|
+
const parts = binding.trim().split(/\s+as\s+/);
|
|
273
|
+
const original = parts[0].trim();
|
|
274
|
+
const local = (parts[1] || original).trim();
|
|
275
|
+
if (!original) return "";
|
|
276
|
+
return `var ${local} = ${sourceObj}.${original};`;
|
|
277
|
+
}).filter(Boolean).join("\n");
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
function rewriteExportsToObject(code) {
|
|
282
|
+
let result = code;
|
|
283
|
+
result = result.replace(
|
|
284
|
+
/export\s+default\s+(\w+)\s*;/g,
|
|
285
|
+
"__exports.default = $1;"
|
|
286
|
+
);
|
|
287
|
+
result = result.replace(
|
|
288
|
+
/export\s*\{([^}]+)\}\s*;/g,
|
|
289
|
+
(_match, inner) => {
|
|
290
|
+
return inner.split(",").map((binding) => {
|
|
291
|
+
const parts = binding.trim().split(/\s+as\s+/);
|
|
292
|
+
const local = parts[0].trim();
|
|
293
|
+
const exported = (parts[1] || parts[0]).trim();
|
|
294
|
+
return `__exports.${exported} = ${local};`;
|
|
295
|
+
}).join("\n ");
|
|
296
|
+
}
|
|
297
|
+
);
|
|
298
|
+
result = result.replace(
|
|
299
|
+
/export\s+(const|let|var)\s+(\w+)/g,
|
|
300
|
+
"$1 $2"
|
|
301
|
+
);
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export {
|
|
306
|
+
gasPlugin
|
|
307
|
+
};
|
|
308
|
+
//# sourceMappingURL=chunk-DXCNTLXT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin-gas.ts"],"sourcesContent":["import path from 'node:path';\n\ninterface GASPluginOptions {\n pagePrefix?: string;\n appTitle?: string;\n serverEntry?: string;\n}\n\ninterface VitePlugin {\n name: string;\n enforce?: 'pre' | 'post';\n renderChunk?: (code: string, chunk: ChunkInfo) => string | null;\n transformIndexHtml?: (html: string) => string;\n generateBundle?: (options: unknown, bundle: Record<string, BundleItem>) => void | Promise<void>;\n}\n\ninterface ChunkInfo {\n fileName: string;\n isEntry: boolean;\n dynamicImports: string[];\n imports?: string[];\n exports?: string[];\n facadeModuleId?: string | null;\n}\n\ninterface BundleItem {\n type: 'chunk' | 'asset';\n fileName: string;\n code?: string;\n source?: string | Uint8Array;\n}\n\nexport function gasPlugin(options: GASPluginOptions = {}): VitePlugin {\n const { pagePrefix = 'page_', appTitle = 'GAS App', serverEntry } = options;\n const lazyPageNames = new Set<string>();\n const fileToGasName = new Map<string, string>();\n\n function toGasName(fileName: string, isLazyPage: boolean): string {\n const baseName = fileName.split('/').pop()!;\n const cleanName = baseName.replace(/-.*$/, '').replace(/\\.js$/, '');\n return isLazyPage ? `${pagePrefix}${cleanName}` : `lib_${cleanName}`;\n }\n\n return {\n name: 'vite-plugin-gas',\n enforce: 'post',\n\n renderChunk(code: string, chunk: ChunkInfo): string | null {\n if (!chunk.isEntry) return null;\n\n let modified = code;\n let changed = false;\n\n for (const dynamicImport of chunk.dynamicImports) {\n const baseName = dynamicImport.split('/').pop()!;\n const pageName = baseName.replace(/-.*$/, '').replace(/\\.js$/, '');\n const gasPageName = `${pagePrefix}${pageName}`;\n\n lazyPageNames.add(pageName);\n\n const escapedBase = baseName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const importPattern = new RegExp(\n `import\\\\(\\\\s*[\"']\\\\.\\\\/` + escapedBase + `[\"']\\\\s*\\\\)`,\n 'g'\n );\n const replacement = `__gasLoadChunk(\"${gasPageName}\")`;\n\n const newCode = modified.replace(importPattern, replacement);\n if (newCode !== modified) {\n modified = newCode;\n changed = true;\n }\n }\n\n return changed ? modified : null;\n },\n\n transformIndexHtml(html: string): string {\n const gasScript = `\n<script>\n(function() {\n var deps = __GAS_DEPS_MAP__;\n var chunkCache = {};\n\n window.__gasLoadChunk = function(name) {\n if (chunkCache[name]) return chunkCache[name];\n\n var promise = new Promise(function(resolve, reject) {\n var chunkDeps = deps[name] || [];\n var depPromises = chunkDeps.map(function(dep) { return window.__gasLoadChunk(dep); });\n\n Promise.all(depPromises).then(function() {\n google.script.run\n .withSuccessHandler(function(js) {\n var script = document.createElement('script');\n script.textContent = js;\n document.head.appendChild(script);\n\n var exports = window.__gasChunkExports || {};\n resolve(exports);\n delete window.__gasChunkExports;\n })\n .withFailureHandler(function(err) {\n reject(new Error('Failed to load chunk: ' + name));\n })\n .getPage(name);\n });\n });\n\n chunkCache[name] = promise;\n return promise;\n };\n\n google.script.run\n .withSuccessHandler(function(js) {\n var s = document.createElement('script');\n s.textContent = js;\n document.head.appendChild(s);\n })\n .withFailureHandler(function(err) {\n document.body.innerHTML = 'Failed to load app: ' + err;\n })\n .getEntryCode();\n})();\n</script>`;\n\n html = html.replace('</body>', gasScript + '\\n</body>');\n\n return html;\n },\n\n async generateBundle(_options: unknown, bundle: Record<string, BundleItem>): Promise<void> {\n let entryFileName: string | null = null;\n let entryCode: string | null = null;\n\n for (const [fileName, item] of Object.entries(bundle)) {\n if (item.type === 'chunk' && (item as unknown as ChunkInfo).isEntry) {\n entryFileName = fileName;\n entryCode = item.code || '';\n break;\n }\n }\n\n if (entryFileName && entryCode) {\n const htmlKey = Object.keys(bundle).find(k => k.endsWith('.html'));\n if (htmlKey) {\n const htmlItem = bundle[htmlKey];\n let html = typeof htmlItem.source === 'string'\n ? htmlItem.source\n : new TextDecoder().decode(htmlItem.source as Uint8Array);\n\n html = html.replace(\n /<script\\b[^>]*src=[\"'][^\"']*[\"'][^>]*>\\s*<\\/script>/g,\n ''\n );\n\n for (const [fileName, item] of Object.entries(bundle)) {\n if (item.type !== 'chunk') continue;\n if (fileName === entryFileName) continue;\n const baseName = fileName.split('/').pop()!;\n const cleanName = baseName.replace(/-.*$/, '').replace(/\\.js$/, '');\n const isLazy = lazyPageNames.has(cleanName);\n fileToGasName.set(fileName, toGasName(fileName, isLazy));\n }\n\n let cleanedEntry = rewriteImportsToGlobals(entryCode, entryFileName, fileToGasName);\n cleanedEntry = rewriteExportsToNamespace(cleanedEntry, 'window.__gasEntry__');\n cleanedEntry = `window.__gasEntry__={};\\nvar __VITE_PRELOAD__=void 0;\\n` + cleanedEntry;\n\n bundle['__gas_entry__.js'] = {\n type: 'asset',\n fileName: '__gas_entry__.js',\n source: `var __GAS_ENTRY_CODE__ = ${JSON.stringify(cleanedEntry)};`,\n };\n\n delete bundle[entryFileName];\n\n const chunkVars: string[] = [];\n const depsMap: Record<string, string[]> = {};\n\n for (const [fileName, item] of Object.entries(bundle)) {\n if (item.type !== 'chunk') continue;\n if (fileName === entryFileName) continue;\n\n const gasName = fileToGasName.get(fileName);\n if (!gasName) continue;\n\n const chunkInfo = item as unknown as ChunkInfo;\n let code = item.code || '';\n\n code = rewriteImportsToGlobals(code, fileName, fileToGasName);\n\n const baseName = fileName.split('/').pop()!;\n const cleanName = baseName.replace(/-.*$/, '').replace(/\\.js$/, '');\n const isLazy = lazyPageNames.has(cleanName);\n\n if (isLazy) {\n code = rewriteExportsToObject(code);\n const wrappedCode = `(function() {\n var __exports = {};\n ${code}\n window.__gasChunkExports = __exports;\n})();`;\n chunkVars.push(`var __GAS_CHUNK_${gasName}__ = ${JSON.stringify(wrappedCode)};`);\n } else {\n code = rewriteExportsToObject(code);\n const wrappedCode = `(function() {\n var __exports = {};\n ${code}\n window.__gasLib_${gasName}__ = __exports;\n})();`;\n chunkVars.push(`var __GAS_CHUNK_${gasName}__ = ${JSON.stringify(wrappedCode)};`);\n }\n\n const chunkDeps: string[] = [];\n if (chunkInfo.imports) {\n for (const imp of chunkInfo.imports) {\n const depGasName = fileToGasName.get(imp);\n if (depGasName) chunkDeps.push(depGasName);\n }\n }\n if (chunkDeps.length > 0) {\n depsMap[gasName] = chunkDeps;\n }\n\n delete bundle[fileName];\n }\n\n if (chunkVars.length > 0) {\n bundle['__gas_chunks__.js'] = {\n type: 'asset',\n fileName: '__gas_chunks__.js',\n source: chunkVars.join('\\n'),\n };\n }\n\n const depsJson = JSON.stringify(depsMap);\n html = html.replace('__GAS_DEPS_MAP__', depsJson);\n htmlItem.source = html;\n\n let codeJs = '';\n\n if (serverEntry) {\n try {\n const esbuild = await import('esbuild');\n const serverPath = path.resolve(process.cwd(), serverEntry);\n const result = await esbuild.build({\n entryPoints: [serverPath],\n bundle: true,\n format: 'esm',\n platform: 'neutral',\n target: 'es2020',\n write: false,\n external: [],\n });\n let serverCode = result.outputFiles[0].text;\n serverCode = serverCode.replace(/^export\\s*\\{[^}]*\\};\\s*$/gm, '');\n serverCode = serverCode.replace(/^export\\s+/gm, '');\n codeJs += serverCode + '\\n';\n } catch (err) {\n console.error('Failed to bundle server entry:', err);\n }\n }\n\n codeJs += `function doGet() {\n return HtmlService.createHtmlOutputFromFile('index')\n .setTitle('${appTitle.replace(/'/g, \"\\\\'\")}')\n .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);\n}\n\nfunction getPage(name) {\n var varName = '__GAS_CHUNK_' + name + '__';\n var code = globalThis[varName];\n if (!code) throw new Error('Chunk not found: ' + name);\n return code;\n}\n\nfunction getEntryCode() {\n return __GAS_ENTRY_CODE__;\n}\n`;\n bundle['Code.js'] = {\n type: 'asset',\n fileName: 'Code.js',\n source: codeJs,\n };\n\n bundle['appsscript.json'] = {\n type: 'asset',\n fileName: 'appsscript.json',\n source: JSON.stringify({\n timeZone: 'Asia/Kolkata',\n dependencies: {},\n webapp: {\n access: 'ANYONE_ANONYMOUS',\n executeAs: 'USER_DEPLOYING',\n },\n exceptionLogging: 'STACKDRIVER',\n runtimeVersion: 'V8',\n }, null, 2),\n };\n }\n }\n },\n };\n}\n\n/** Rewrite ESM exports to assignments on a namespace object. */\nfunction rewriteExportsToNamespace(code: string, namespace: string): string {\n let result = code;\n result = result.replace(/export\\s*\\{([^}]*)\\}\\s*;?/g, (_match, inner: string) => {\n return inner\n .split(',')\n .map((binding) => {\n const parts = binding.trim().split(/\\s+as\\s+/);\n const local = parts[0].trim();\n const exported = (parts[1] || parts[0]).trim();\n return exported ? `${namespace}.${exported}=${local};` : '';\n })\n .filter(Boolean)\n .join('');\n });\n result = result.replace(/export\\s+default\\s+(\\w+)\\s*;/g, `${namespace}.default=$1;`);\n return result;\n}\n\n/** Rewrite ESM imports to var declarations reading from the correct namespace. */\nfunction rewriteImportsToGlobals(\n code: string,\n chunkFileName: string,\n fileToGasName: Map<string, string>,\n): string {\n const lastSlash = chunkFileName.lastIndexOf('/');\n const chunkDir = lastSlash >= 0 ? chunkFileName.substring(0, lastSlash) : '';\n\n return code.replace(\n /import\\s*\\{([^}]+)\\}\\s*from\\s*[\"']([^\"']+)[\"']\\s*;?/g,\n (_match, bindings: string, importPath: string) => {\n let sourceObj = 'window';\n\n if (importPath.startsWith('./')) {\n const resolved = chunkDir\n ? `${chunkDir}/${importPath.slice(2)}`\n : importPath.slice(2);\n const gasName = fileToGasName.get(resolved);\n if (gasName?.startsWith('lib_')) {\n sourceObj = `window.__gasLib_${gasName}__`;\n } else if (!gasName) {\n sourceObj = 'window.__gasEntry__';\n }\n }\n\n return bindings\n .split(',')\n .map((binding) => {\n const parts = binding.trim().split(/\\s+as\\s+/);\n const original = parts[0].trim();\n const local = (parts[1] || original).trim();\n if (!original) return '';\n return `var ${local} = ${sourceObj}.${original};`;\n })\n .filter(Boolean)\n .join('\\n');\n }\n );\n}\n\n/** Rewrite ESM exports to assign to an __exports object (used for chunks). */\nfunction rewriteExportsToObject(code: string): string {\n let result = code;\n\n result = result.replace(\n /export\\s+default\\s+(\\w+)\\s*;/g,\n '__exports.default = $1;'\n );\n\n result = result.replace(\n /export\\s*\\{([^}]+)\\}\\s*;/g,\n (_match, inner: string) => {\n return inner\n .split(',')\n .map((binding) => {\n const parts = binding.trim().split(/\\s+as\\s+/);\n const local = parts[0].trim();\n const exported = (parts[1] || parts[0]).trim();\n return `__exports.${exported} = ${local};`;\n })\n .join('\\n ');\n }\n );\n\n result = result.replace(\n /export\\s+(const|let|var)\\s+(\\w+)/g,\n '$1 $2'\n );\n\n return result;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare function isLocalDev(): boolean;
|
|
2
|
+
|
|
3
|
+
export interface GASPluginOptions {
|
|
4
|
+
pagePrefix?: string;
|
|
5
|
+
appTitle?: string;
|
|
6
|
+
serverEntry?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export declare function gasPlugin(options?: GASPluginOptions): {
|
|
10
|
+
name: string;
|
|
11
|
+
enforce: 'post';
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface GASViteOptions {
|
|
15
|
+
clientRoot?: string;
|
|
16
|
+
outDir?: string;
|
|
17
|
+
devServerPort?: number;
|
|
18
|
+
devPort?: number;
|
|
19
|
+
aliases?: Record<string, string>;
|
|
20
|
+
plugins?: unknown[];
|
|
21
|
+
appTitle?: string;
|
|
22
|
+
serverEntry?: string;
|
|
23
|
+
vite?: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export declare function createGASViteConfig(
|
|
27
|
+
options?: GASViteOptions
|
|
28
|
+
): Promise<Record<string, unknown>>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
gasPlugin
|
|
3
|
+
} from "./chunk-DXCNTLXT.js";
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { createRequire } from "module";
|
|
8
|
+
import { pathToFileURL } from "url";
|
|
9
|
+
async function importFromProject(specifier) {
|
|
10
|
+
const require2 = createRequire(path.resolve(process.cwd(), "package.json"));
|
|
11
|
+
const resolved = require2.resolve(specifier);
|
|
12
|
+
return import(pathToFileURL(resolved).href);
|
|
13
|
+
}
|
|
14
|
+
function isLocalDev() {
|
|
15
|
+
return process.env.GAS_LOCAL === "true";
|
|
16
|
+
}
|
|
17
|
+
async function createGASViteConfig(options = {}) {
|
|
18
|
+
const {
|
|
19
|
+
clientRoot = "src/client",
|
|
20
|
+
outDir = "dist",
|
|
21
|
+
devServerPort = 3001,
|
|
22
|
+
devPort = 5173,
|
|
23
|
+
aliases = {},
|
|
24
|
+
plugins: extraPlugins = [],
|
|
25
|
+
appTitle,
|
|
26
|
+
serverEntry,
|
|
27
|
+
vite: overrides = {}
|
|
28
|
+
} = options;
|
|
29
|
+
const local = isLocalDev();
|
|
30
|
+
const projectRoot = process.cwd();
|
|
31
|
+
const clientDepth = clientRoot.split("/").filter(Boolean).length;
|
|
32
|
+
const relativeOutDir = "../".repeat(clientDepth) + outDir;
|
|
33
|
+
const plugins = [];
|
|
34
|
+
try {
|
|
35
|
+
const mod = await importFromProject("@vitejs/plugin-react");
|
|
36
|
+
const reactPlugin = mod.default ?? mod;
|
|
37
|
+
plugins.push(typeof reactPlugin === "function" ? reactPlugin() : reactPlugin);
|
|
38
|
+
} catch {
|
|
39
|
+
console.warn(
|
|
40
|
+
"\u26A0\uFE0F @vitejs/plugin-react not found. Install it:\n npm install -D @vitejs/plugin-react\n"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (!local) {
|
|
44
|
+
const { gasPlugin: gasPlugin2 } = await import("./vite-plugin-gas-UCZ4473C.js");
|
|
45
|
+
plugins.push(gasPlugin2({ appTitle, serverEntry }));
|
|
46
|
+
}
|
|
47
|
+
plugins.push(...extraPlugins);
|
|
48
|
+
const resolvedAliases = {
|
|
49
|
+
"@": path.resolve(projectRoot, "src"),
|
|
50
|
+
...Object.fromEntries(
|
|
51
|
+
Object.entries(aliases).map(([key, val]) => [
|
|
52
|
+
key,
|
|
53
|
+
path.resolve(projectRoot, val)
|
|
54
|
+
])
|
|
55
|
+
)
|
|
56
|
+
};
|
|
57
|
+
const config = {
|
|
58
|
+
root: clientRoot,
|
|
59
|
+
plugins,
|
|
60
|
+
resolve: { alias: resolvedAliases },
|
|
61
|
+
build: { outDir: relativeOutDir, emptyOutDir: true }
|
|
62
|
+
};
|
|
63
|
+
if (local) {
|
|
64
|
+
config.define = {
|
|
65
|
+
"window.__GAS_DEV_MODE__": "true",
|
|
66
|
+
"window.__GAS_DEV_SERVER__": JSON.stringify(`http://localhost:${devServerPort}`)
|
|
67
|
+
};
|
|
68
|
+
config.server = { port: devPort, open: true };
|
|
69
|
+
}
|
|
70
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
71
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && typeof config[key] === "object" && config[key] !== null) {
|
|
72
|
+
config[key] = {
|
|
73
|
+
...config[key],
|
|
74
|
+
...value
|
|
75
|
+
};
|
|
76
|
+
} else {
|
|
77
|
+
config[key] = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return config;
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
createGASViteConfig,
|
|
84
|
+
gasPlugin,
|
|
85
|
+
isLocalDev
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import path from 'node:path';\nimport { createRequire } from 'node:module';\nimport { pathToFileURL } from 'node:url';\n\nexport { gasPlugin } from './vite-plugin-gas';\n\nasync function importFromProject(specifier: string): Promise<unknown> {\n const require = createRequire(path.resolve(process.cwd(), 'package.json'));\n const resolved = require.resolve(specifier);\n return import(pathToFileURL(resolved).href);\n}\n\nexport function isLocalDev(): boolean {\n return process.env.GAS_LOCAL === 'true';\n}\n\nexport interface GASViteOptions {\n clientRoot?: string;\n outDir?: string;\n devServerPort?: number;\n devPort?: number;\n aliases?: Record<string, string>;\n plugins?: unknown[];\n appTitle?: string;\n serverEntry?: string;\n vite?: Record<string, unknown>;\n}\n\nexport async function createGASViteConfig(\n options: GASViteOptions = {}\n): Promise<Record<string, unknown>> {\n const {\n clientRoot = 'src/client',\n outDir = 'dist',\n devServerPort = 3001,\n devPort = 5173,\n aliases = {},\n plugins: extraPlugins = [],\n appTitle,\n serverEntry,\n vite: overrides = {},\n } = options;\n\n const local = isLocalDev();\n const projectRoot = process.cwd();\n const clientDepth = clientRoot.split('/').filter(Boolean).length;\n const relativeOutDir = '../'.repeat(clientDepth) + outDir;\n\n const plugins: unknown[] = [];\n\n try {\n const mod = await importFromProject('@vitejs/plugin-react') as Record<string, unknown>;\n const reactPlugin = mod.default ?? mod;\n plugins.push(typeof reactPlugin === 'function' ? (reactPlugin as () => unknown)() : reactPlugin);\n } catch {\n console.warn(\n '⚠️ @vitejs/plugin-react not found. Install it:\\n' +\n ' npm install -D @vitejs/plugin-react\\n'\n );\n }\n\n if (!local) {\n const { gasPlugin } = await import('./vite-plugin-gas');\n plugins.push(gasPlugin({ appTitle, serverEntry }));\n }\n\n plugins.push(...extraPlugins);\n\n const resolvedAliases: Record<string, string> = {\n '@': path.resolve(projectRoot, 'src'),\n ...Object.fromEntries(\n Object.entries(aliases).map(([key, val]) => [\n key,\n path.resolve(projectRoot, val),\n ])\n ),\n };\n\n const config: Record<string, unknown> = {\n root: clientRoot,\n plugins,\n resolve: { alias: resolvedAliases },\n build: { outDir: relativeOutDir, emptyOutDir: true },\n };\n\n if (local) {\n config.define = {\n 'window.__GAS_DEV_MODE__': 'true',\n 'window.__GAS_DEV_SERVER__': JSON.stringify(`http://localhost:${devServerPort}`),\n };\n config.server = { port: devPort, open: true };\n }\n\n for (const [key, value] of Object.entries(overrides)) {\n if (\n typeof value === 'object' && value !== null && !Array.isArray(value) &&\n typeof config[key] === 'object' && config[key] !== null\n ) {\n config[key] = {\n ...(config[key] as Record<string, unknown>),\n ...(value as Record<string, unknown>),\n };\n } else {\n config[key] = value;\n }\n }\n\n return config;\n}\n"],"mappings":";;;;;AAAA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAI9B,eAAe,kBAAkB,WAAqC;AACpE,QAAMA,WAAU,cAAc,KAAK,QAAQ,QAAQ,IAAI,GAAG,cAAc,CAAC;AACzE,QAAM,WAAWA,SAAQ,QAAQ,SAAS;AAC1C,SAAO,OAAO,cAAc,QAAQ,EAAE;AACxC;AAEO,SAAS,aAAsB;AACpC,SAAO,QAAQ,IAAI,cAAc;AACnC;AAcA,eAAsB,oBACpB,UAA0B,CAAC,GACO;AAClC,QAAM;AAAA,IACJ,aAAa;AAAA,IACb,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,UAAU,CAAC;AAAA,IACX,SAAS,eAAe,CAAC;AAAA,IACzB;AAAA,IACA;AAAA,IACA,MAAM,YAAY,CAAC;AAAA,EACrB,IAAI;AAEJ,QAAM,QAAQ,WAAW;AACzB,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,cAAc,WAAW,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE;AAC1D,QAAM,iBAAiB,MAAM,OAAO,WAAW,IAAI;AAEnD,QAAM,UAAqB,CAAC;AAE5B,MAAI;AACF,UAAM,MAAM,MAAM,kBAAkB,sBAAsB;AAC1D,UAAM,cAAc,IAAI,WAAW;AACnC,YAAQ,KAAK,OAAO,gBAAgB,aAAc,YAA8B,IAAI,WAAW;AAAA,EACjG,QAAQ;AACN,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACV,UAAM,EAAE,WAAAC,WAAU,IAAI,MAAM,OAAO,+BAAmB;AACtD,YAAQ,KAAKA,WAAU,EAAE,UAAU,YAAY,CAAC,CAAC;AAAA,EACnD;AAEA,UAAQ,KAAK,GAAG,YAAY;AAE5B,QAAM,kBAA0C;AAAA,IAC9C,KAAK,KAAK,QAAQ,aAAa,KAAK;AAAA,IACpC,GAAG,OAAO;AAAA,MACR,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM;AAAA,QAC1C;AAAA,QACA,KAAK,QAAQ,aAAa,GAAG;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,SAAkC;AAAA,IACtC,MAAM;AAAA,IACN;AAAA,IACA,SAAS,EAAE,OAAO,gBAAgB;AAAA,IAClC,OAAO,EAAE,QAAQ,gBAAgB,aAAa,KAAK;AAAA,EACrD;AAEA,MAAI,OAAO;AACT,WAAO,SAAS;AAAA,MACd,2BAA2B;AAAA,MAC3B,6BAA6B,KAAK,UAAU,oBAAoB,aAAa,EAAE;AAAA,IACjF;AACA,WAAO,SAAS,EAAE,MAAM,SAAS,MAAM,KAAK;AAAA,EAC9C;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AACpD,QACE,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,KACnE,OAAO,OAAO,GAAG,MAAM,YAAY,OAAO,GAAG,MAAM,MACnD;AACA,aAAO,GAAG,IAAI;AAAA,QACZ,GAAI,OAAO,GAAG;AAAA,QACd,GAAI;AAAA,MACN;AAAA,IACF,OAAO;AACL,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;","names":["require","gasPlugin"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-gas-react",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Vite plugin that deploys React apps to Google Apps Script with automatic code splitting",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Sarfraj Akhtar",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/sarfrajakhtar/gas-react.git",
|
|
11
|
+
"directory": "packages/vite-plugin"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/sarfrajakhtar/gas-react/tree/main/packages/vite-plugin",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/sarfrajakhtar/gas-react/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"vite",
|
|
19
|
+
"vite-plugin",
|
|
20
|
+
"google-apps-script",
|
|
21
|
+
"gas",
|
|
22
|
+
"react",
|
|
23
|
+
"code-splitting",
|
|
24
|
+
"clasp"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"main": "./dist/index.js",
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"typesVersions": {
|
|
40
|
+
"*": {
|
|
41
|
+
".": [
|
|
42
|
+
"dist/index.d.ts"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"README.md",
|
|
49
|
+
"LICENSE"
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"dev": "tsup --watch",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@vitejs/plugin-react": ">=4.0.0",
|
|
59
|
+
"vite": ">=5.0.0"
|
|
60
|
+
},
|
|
61
|
+
"peerDependenciesMeta": {
|
|
62
|
+
"@vitejs/plugin-react": {
|
|
63
|
+
"optional": true
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@types/node": "^25.3.3",
|
|
68
|
+
"tsup": "^8.0.0",
|
|
69
|
+
"typescript": "^5.7.0",
|
|
70
|
+
"vitest": "^3.2.4"
|
|
71
|
+
}
|
|
72
|
+
}
|