void 0.7.5 → 0.7.6
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/dist/cli/cli.mjs +8 -8
- package/dist/{db-RqxhOT2E.mjs → db-DvBg7Oc5.mjs} +1 -1
- package/dist/{deploy-CRIz77mw.mjs → deploy-B5rTuzXw.mjs} +39 -12
- package/dist/index.mjs +463 -34
- package/dist/{init-FMZ29UZW.mjs → init-BiXWyKEf.mjs} +2 -2
- package/dist/pages/client.d.mts +1 -1
- package/dist/pages/index.d.mts +1 -1
- package/dist/pages/islands-plugin.d.mts +2 -11
- package/dist/pages/islands-plugin.mjs +1 -63
- package/dist/pages/protocol.d.mts +1 -1
- package/dist/pages/protocol.mjs +96 -12
- package/dist/plugin-inference-CXUuq0aC.mjs +577 -0
- package/dist/{prepare-PqAbhLte.mjs → prepare-BrfiJBju.mjs} +2 -2
- package/dist/{preset-jKn1hQwP.mjs → preset-BhnZpb7t.mjs} +1 -1
- package/dist/{protocol-DxGzKEz2.d.mts → protocol-COz1NHxc.d.mts} +3 -0
- package/dist/{scan-QtRTdh94.mjs → scan-vv5u7rHu.mjs} +1 -1
- package/dist/{types-AdKzPp2C.d.mts → types-D6AuY5Bf.d.mts} +9 -1
- package/package.json +3 -3
- package/dist/plugin-inference-CCtRkt9O.mjs +0 -2476
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import { a as join } from "./pathe.M-eThtNZ-D-kmWkCS.mjs";
|
|
2
|
+
import { n as globSync } from "./dist-DUyXJLkq.mjs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { parseSync } from "vite";
|
|
5
|
+
import { parse } from "es-module-lexer/js";
|
|
6
|
+
//#region ../../node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/walker.js
|
|
7
|
+
/**
|
|
8
|
+
* @typedef { import('estree').Node} Node
|
|
9
|
+
* @typedef {{
|
|
10
|
+
* skip: () => void;
|
|
11
|
+
* remove: () => void;
|
|
12
|
+
* replace: (node: Node) => void;
|
|
13
|
+
* }} WalkerContext
|
|
14
|
+
*/
|
|
15
|
+
var WalkerBase = class {
|
|
16
|
+
constructor() {
|
|
17
|
+
/** @type {boolean} */
|
|
18
|
+
this.should_skip = false;
|
|
19
|
+
/** @type {boolean} */
|
|
20
|
+
this.should_remove = false;
|
|
21
|
+
/** @type {Node | null} */
|
|
22
|
+
this.replacement = null;
|
|
23
|
+
/** @type {WalkerContext} */
|
|
24
|
+
this.context = {
|
|
25
|
+
skip: () => this.should_skip = true,
|
|
26
|
+
remove: () => this.should_remove = true,
|
|
27
|
+
replace: (node) => this.replacement = node
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @template {Node} Parent
|
|
32
|
+
* @param {Parent | null | undefined} parent
|
|
33
|
+
* @param {keyof Parent | null | undefined} prop
|
|
34
|
+
* @param {number | null | undefined} index
|
|
35
|
+
* @param {Node} node
|
|
36
|
+
*/
|
|
37
|
+
replace(parent, prop, index, node) {
|
|
38
|
+
if (parent && prop) if (index != null)
|
|
39
|
+
/** @type {Array<Node>} */ parent[prop][index] = node;
|
|
40
|
+
else
|
|
41
|
+
/** @type {Node} */ parent[prop] = node;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* @template {Node} Parent
|
|
45
|
+
* @param {Parent | null | undefined} parent
|
|
46
|
+
* @param {keyof Parent | null | undefined} prop
|
|
47
|
+
* @param {number | null | undefined} index
|
|
48
|
+
*/
|
|
49
|
+
remove(parent, prop, index) {
|
|
50
|
+
if (parent && prop) if (index !== null && index !== void 0)
|
|
51
|
+
/** @type {Array<Node>} */ parent[prop].splice(index, 1);
|
|
52
|
+
else delete parent[prop];
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region ../../node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/sync.js
|
|
57
|
+
/**
|
|
58
|
+
* @typedef { import('estree').Node} Node
|
|
59
|
+
* @typedef { import('./walker.js').WalkerContext} WalkerContext
|
|
60
|
+
* @typedef {(
|
|
61
|
+
* this: WalkerContext,
|
|
62
|
+
* node: Node,
|
|
63
|
+
* parent: Node | null,
|
|
64
|
+
* key: string | number | symbol | null | undefined,
|
|
65
|
+
* index: number | null | undefined
|
|
66
|
+
* ) => void} SyncHandler
|
|
67
|
+
*/
|
|
68
|
+
var SyncWalker = class extends WalkerBase {
|
|
69
|
+
/**
|
|
70
|
+
*
|
|
71
|
+
* @param {SyncHandler} [enter]
|
|
72
|
+
* @param {SyncHandler} [leave]
|
|
73
|
+
*/
|
|
74
|
+
constructor(enter, leave) {
|
|
75
|
+
super();
|
|
76
|
+
/** @type {boolean} */
|
|
77
|
+
this.should_skip = false;
|
|
78
|
+
/** @type {boolean} */
|
|
79
|
+
this.should_remove = false;
|
|
80
|
+
/** @type {Node | null} */
|
|
81
|
+
this.replacement = null;
|
|
82
|
+
/** @type {WalkerContext} */
|
|
83
|
+
this.context = {
|
|
84
|
+
skip: () => this.should_skip = true,
|
|
85
|
+
remove: () => this.should_remove = true,
|
|
86
|
+
replace: (node) => this.replacement = node
|
|
87
|
+
};
|
|
88
|
+
/** @type {SyncHandler | undefined} */
|
|
89
|
+
this.enter = enter;
|
|
90
|
+
/** @type {SyncHandler | undefined} */
|
|
91
|
+
this.leave = leave;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* @template {Node} Parent
|
|
95
|
+
* @param {Node} node
|
|
96
|
+
* @param {Parent | null} parent
|
|
97
|
+
* @param {keyof Parent} [prop]
|
|
98
|
+
* @param {number | null} [index]
|
|
99
|
+
* @returns {Node | null}
|
|
100
|
+
*/
|
|
101
|
+
visit(node, parent, prop, index) {
|
|
102
|
+
if (node) {
|
|
103
|
+
if (this.enter) {
|
|
104
|
+
const _should_skip = this.should_skip;
|
|
105
|
+
const _should_remove = this.should_remove;
|
|
106
|
+
const _replacement = this.replacement;
|
|
107
|
+
this.should_skip = false;
|
|
108
|
+
this.should_remove = false;
|
|
109
|
+
this.replacement = null;
|
|
110
|
+
this.enter.call(this.context, node, parent, prop, index);
|
|
111
|
+
if (this.replacement) {
|
|
112
|
+
node = this.replacement;
|
|
113
|
+
this.replace(parent, prop, index, node);
|
|
114
|
+
}
|
|
115
|
+
if (this.should_remove) this.remove(parent, prop, index);
|
|
116
|
+
const skipped = this.should_skip;
|
|
117
|
+
const removed = this.should_remove;
|
|
118
|
+
this.should_skip = _should_skip;
|
|
119
|
+
this.should_remove = _should_remove;
|
|
120
|
+
this.replacement = _replacement;
|
|
121
|
+
if (skipped) return node;
|
|
122
|
+
if (removed) return null;
|
|
123
|
+
}
|
|
124
|
+
/** @type {keyof Node} */
|
|
125
|
+
let key;
|
|
126
|
+
for (key in node) {
|
|
127
|
+
/** @type {unknown} */
|
|
128
|
+
const value = node[key];
|
|
129
|
+
if (value && typeof value === "object") {
|
|
130
|
+
if (Array.isArray(value)) {
|
|
131
|
+
const nodes = value;
|
|
132
|
+
for (let i = 0; i < nodes.length; i += 1) {
|
|
133
|
+
const item = nodes[i];
|
|
134
|
+
if (isNode(item)) {
|
|
135
|
+
if (!this.visit(item, node, key, i)) i--;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} else if (isNode(value)) this.visit(value, node, key, null);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (this.leave) {
|
|
142
|
+
const _replacement = this.replacement;
|
|
143
|
+
const _should_remove = this.should_remove;
|
|
144
|
+
this.replacement = null;
|
|
145
|
+
this.should_remove = false;
|
|
146
|
+
this.leave.call(this.context, node, parent, prop, index);
|
|
147
|
+
if (this.replacement) {
|
|
148
|
+
node = this.replacement;
|
|
149
|
+
this.replace(parent, prop, index, node);
|
|
150
|
+
}
|
|
151
|
+
if (this.should_remove) this.remove(parent, prop, index);
|
|
152
|
+
const removed = this.should_remove;
|
|
153
|
+
this.replacement = _replacement;
|
|
154
|
+
this.should_remove = _should_remove;
|
|
155
|
+
if (removed) return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return node;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Ducktype a node.
|
|
163
|
+
*
|
|
164
|
+
* @param {unknown} value
|
|
165
|
+
* @returns {value is Node}
|
|
166
|
+
*/
|
|
167
|
+
function isNode(value) {
|
|
168
|
+
return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string";
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region ../../node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/index.js
|
|
172
|
+
/**
|
|
173
|
+
* @typedef {import('estree').Node} Node
|
|
174
|
+
* @typedef {import('./sync.js').SyncHandler} SyncHandler
|
|
175
|
+
* @typedef {import('./async.js').AsyncHandler} AsyncHandler
|
|
176
|
+
*/
|
|
177
|
+
/**
|
|
178
|
+
* @param {Node} ast
|
|
179
|
+
* @param {{
|
|
180
|
+
* enter?: SyncHandler
|
|
181
|
+
* leave?: SyncHandler
|
|
182
|
+
* }} walker
|
|
183
|
+
* @returns {Node | null}
|
|
184
|
+
*/
|
|
185
|
+
function walk(ast, { enter, leave }) {
|
|
186
|
+
return new SyncWalker(enter, leave).visit(ast, null);
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/shared/ast.ts
|
|
190
|
+
function isIdentifier(node) {
|
|
191
|
+
return node?.type === "Identifier" && typeof node.name === "string";
|
|
192
|
+
}
|
|
193
|
+
function isLiteral(node) {
|
|
194
|
+
return node?.type === "Literal";
|
|
195
|
+
}
|
|
196
|
+
function isMemberExpression(node) {
|
|
197
|
+
return node?.type === "MemberExpression";
|
|
198
|
+
}
|
|
199
|
+
function isCallExpression(node) {
|
|
200
|
+
return node?.type === "CallExpression";
|
|
201
|
+
}
|
|
202
|
+
function isObjectExpression(node) {
|
|
203
|
+
return node?.type === "ObjectExpression";
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/inference/walk.ts
|
|
207
|
+
const ENV_BINDING_RE = /\benv\s*\.\s*(DB|KV|STORAGE|QUEUE_\w+|AI|SANDBOX)\b/;
|
|
208
|
+
/**
|
|
209
|
+
* Infer which Cloudflare bindings a source file uses.
|
|
210
|
+
*
|
|
211
|
+
* Uses a tiered approach for performance:
|
|
212
|
+
* Tier 1: es-module-lexer to extract import sources (fast, asm.js, no init)
|
|
213
|
+
* Tier 2: Regex check for env.DB/KV/STORAGE patterns
|
|
214
|
+
* Tier 3: Full AST walk only when regex matches (to confirm member expressions)
|
|
215
|
+
*/
|
|
216
|
+
function inferBindingsFromSource(code, filename) {
|
|
217
|
+
const result = {
|
|
218
|
+
needsD1: false,
|
|
219
|
+
needsKV: false,
|
|
220
|
+
needsR2: false,
|
|
221
|
+
needsAuth: false,
|
|
222
|
+
needsQueues: false,
|
|
223
|
+
needsAI: false,
|
|
224
|
+
needsSandbox: false,
|
|
225
|
+
needsLive: false
|
|
226
|
+
};
|
|
227
|
+
try {
|
|
228
|
+
const [imports] = parse(code);
|
|
229
|
+
for (const imp of imports) {
|
|
230
|
+
const source = imp.n;
|
|
231
|
+
if (!source) continue;
|
|
232
|
+
switch (source) {
|
|
233
|
+
case "void/db":
|
|
234
|
+
result.needsD1 = true;
|
|
235
|
+
break;
|
|
236
|
+
case "void/kv":
|
|
237
|
+
result.needsKV = true;
|
|
238
|
+
break;
|
|
239
|
+
case "void/storage":
|
|
240
|
+
result.needsR2 = true;
|
|
241
|
+
break;
|
|
242
|
+
case "void/auth":
|
|
243
|
+
result.needsAuth = true;
|
|
244
|
+
break;
|
|
245
|
+
case "void/queue":
|
|
246
|
+
result.needsQueues = true;
|
|
247
|
+
break;
|
|
248
|
+
case "void/ai":
|
|
249
|
+
result.needsAI = true;
|
|
250
|
+
break;
|
|
251
|
+
case "void/sandbox":
|
|
252
|
+
result.needsSandbox = true;
|
|
253
|
+
break;
|
|
254
|
+
case "void/live":
|
|
255
|
+
if (!isTypeOnlyImportStatement(code.slice(imp.ss, imp.se + 1))) result.needsLive = true;
|
|
256
|
+
break;
|
|
257
|
+
case "void/client": {
|
|
258
|
+
const stmt = code.slice(imp.ss, imp.se + 1);
|
|
259
|
+
if (/\bauth\b/.test(stmt)) result.needsAuth = true;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} catch {
|
|
265
|
+
if (/\bfrom\s+["']void\/db["']/.test(code)) result.needsD1 = true;
|
|
266
|
+
if (/\bfrom\s+["']void\/kv["']/.test(code)) result.needsKV = true;
|
|
267
|
+
if (/\bfrom\s+["']void\/storage["']/.test(code)) result.needsR2 = true;
|
|
268
|
+
if (/\bfrom\s+["']void\/auth["']/.test(code)) result.needsAuth = true;
|
|
269
|
+
if (/\bfrom\s+["']void\/queue["']/.test(code)) result.needsQueues = true;
|
|
270
|
+
if (/\bfrom\s+["']void\/ai["']/.test(code)) result.needsAI = true;
|
|
271
|
+
if (/\bfrom\s+["']void\/sandbox["']/.test(code)) result.needsSandbox = true;
|
|
272
|
+
if (hasRuntimeImportFrom(code, "void/live")) result.needsLive = true;
|
|
273
|
+
if (/\bfrom\s+["']void\/client["']/.test(code) && /\bauth\b/.test(code)) result.needsAuth = true;
|
|
274
|
+
}
|
|
275
|
+
if (result.needsD1 && result.needsKV && result.needsR2 && result.needsAI && result.needsSandbox && result.needsLive) return result;
|
|
276
|
+
if (!ENV_BINDING_RE.test(code)) return result;
|
|
277
|
+
let parsed;
|
|
278
|
+
try {
|
|
279
|
+
parsed = parseSync(filename, code);
|
|
280
|
+
} catch {
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
const program = parsed.program;
|
|
284
|
+
walk(program, { enter(node) {
|
|
285
|
+
const current = node;
|
|
286
|
+
if (!isMemberExpression(current)) return;
|
|
287
|
+
if (current.computed) return;
|
|
288
|
+
const property = current.property;
|
|
289
|
+
if (!isIdentifier(property)) return;
|
|
290
|
+
const obj = current.object;
|
|
291
|
+
const isViaContextEnv = isMemberExpression(obj) && isIdentifier(obj.property) && obj.property.name === "env";
|
|
292
|
+
const isDirectEnv = isIdentifier(obj) && obj.name === "env";
|
|
293
|
+
if (!isViaContextEnv && !isDirectEnv) return;
|
|
294
|
+
switch (property.name) {
|
|
295
|
+
case "DB":
|
|
296
|
+
result.needsD1 = true;
|
|
297
|
+
break;
|
|
298
|
+
case "KV":
|
|
299
|
+
result.needsKV = true;
|
|
300
|
+
break;
|
|
301
|
+
case "STORAGE":
|
|
302
|
+
result.needsR2 = true;
|
|
303
|
+
break;
|
|
304
|
+
case "AI":
|
|
305
|
+
result.needsAI = true;
|
|
306
|
+
break;
|
|
307
|
+
case "SANDBOX":
|
|
308
|
+
result.needsSandbox = true;
|
|
309
|
+
break;
|
|
310
|
+
default:
|
|
311
|
+
if (property.name.startsWith("QUEUE_")) result.needsQueues = true;
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
} });
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
function isTypeOnlyImportStatement(statement) {
|
|
318
|
+
if (/^\s*import\s+type\b/.test(statement)) return true;
|
|
319
|
+
const specifierStart = statement.indexOf("{");
|
|
320
|
+
const specifierEnd = statement.lastIndexOf("}");
|
|
321
|
+
if (specifierStart === -1 || specifierEnd === -1 || specifierEnd < specifierStart) return false;
|
|
322
|
+
const beforeSpecifiers = statement.slice(0, specifierStart);
|
|
323
|
+
if (!/^\s*import\s*$/.test(beforeSpecifiers)) return false;
|
|
324
|
+
const specifiers = statement.slice(specifierStart + 1, specifierEnd).split(",").map((specifier) => specifier.trim()).filter(Boolean);
|
|
325
|
+
return specifiers.length > 0 && specifiers.every((specifier) => specifier.startsWith("type "));
|
|
326
|
+
}
|
|
327
|
+
function hasRuntimeImportFrom(code, source) {
|
|
328
|
+
const escapedSource = source.replaceAll("/", "\\/");
|
|
329
|
+
const importRe = new RegExp(`\\bimport\\s+(?:[\\s\\S]*?\\bfrom\\s+)?["']${escapedSource}["']\\s*;?`, "g");
|
|
330
|
+
for (const match of code.matchAll(importRe)) {
|
|
331
|
+
const statement = match[0];
|
|
332
|
+
if (!isTypeOnlyImportStatement(statement)) return true;
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Merge multiple InferredBindings into a single result (union).
|
|
338
|
+
*/
|
|
339
|
+
function mergeBindings(bindings) {
|
|
340
|
+
return {
|
|
341
|
+
needsD1: bindings.some((b) => b.needsD1),
|
|
342
|
+
needsKV: bindings.some((b) => b.needsKV),
|
|
343
|
+
needsR2: bindings.some((b) => b.needsR2),
|
|
344
|
+
needsAuth: bindings.some((b) => b.needsAuth),
|
|
345
|
+
needsQueues: bindings.some((b) => b.needsQueues),
|
|
346
|
+
needsAI: bindings.some((b) => b.needsAI),
|
|
347
|
+
needsSandbox: bindings.some((b) => b.needsSandbox),
|
|
348
|
+
needsLive: bindings.some((b) => b.needsLive)
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region src/ssr/entry.ts
|
|
353
|
+
const SSR_SERVER_ENTRY_RELATIVE_PATHS = ["src/main.ssr.ts", "src/main.ssr.tsx"];
|
|
354
|
+
const SSR_CLIENT_ENTRY_RELATIVE_PATHS = ["src/main.client.ts", "src/main.client.tsx"];
|
|
355
|
+
function resolveSingleEntry(root, entries, label) {
|
|
356
|
+
const matches = entries.map((relativePath) => ({
|
|
357
|
+
relativePath,
|
|
358
|
+
absolutePath: join(root, relativePath)
|
|
359
|
+
})).filter((entry) => existsSync(entry.absolutePath));
|
|
360
|
+
if (matches.length === 0) return null;
|
|
361
|
+
if (matches.length > 1) {
|
|
362
|
+
const names = matches.map((entry) => entry.relativePath).join(", ");
|
|
363
|
+
throw new Error(`ssr: Multiple ${label} entry files found: ${names}. Keep only one.`);
|
|
364
|
+
}
|
|
365
|
+
return matches[0];
|
|
366
|
+
}
|
|
367
|
+
function getSsrServerEntryPath(root) {
|
|
368
|
+
return resolveSingleEntry(root, SSR_SERVER_ENTRY_RELATIVE_PATHS, "SSR")?.absolutePath ?? null;
|
|
369
|
+
}
|
|
370
|
+
function hasSsrEntry(root) {
|
|
371
|
+
return getSsrServerEntryPath(root) !== null;
|
|
372
|
+
}
|
|
373
|
+
function validateServerRenderExport(entry) {
|
|
374
|
+
const { absolutePath, relativePath } = entry;
|
|
375
|
+
const source = readFileSync(absolutePath, "utf-8");
|
|
376
|
+
if (/\bexport\s+(?:async\s+)?function\s+render\b/.test(source) || /\bexport\s+const\s+render\b/.test(source) || /\bexport\s+default\s+(?:defineRender\b|(?:async\s+)?function\b)/.test(source)) return;
|
|
377
|
+
let parsed;
|
|
378
|
+
try {
|
|
379
|
+
parsed = parseSync(relativePath, source);
|
|
380
|
+
} catch (err) {
|
|
381
|
+
throw new Error(`ssr: Invalid entry '${relativePath}'. Could not parse source: '${err instanceof Error ? err.message : String(err)}'.`);
|
|
382
|
+
}
|
|
383
|
+
if (!(parsed.program.body ?? []).some((node) => {
|
|
384
|
+
if (node.type === "ExportDefaultDeclaration") {
|
|
385
|
+
const declaration = node.declaration;
|
|
386
|
+
if (declaration?.type === "FunctionDeclaration" || declaration?.type === "FunctionExpression" || declaration?.type === "ArrowFunctionExpression") return true;
|
|
387
|
+
if (declaration?.type === "CallExpression") {
|
|
388
|
+
const call = declaration;
|
|
389
|
+
return isIdentifier(call.callee) && call.callee.name === "defineRender";
|
|
390
|
+
}
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
if (node.type !== "ExportNamedDeclaration") return false;
|
|
394
|
+
const declaration = node.declaration;
|
|
395
|
+
if (declaration?.type === "FunctionDeclaration") {
|
|
396
|
+
const fn = declaration;
|
|
397
|
+
return isIdentifier(fn.id) && fn.id.name === "render";
|
|
398
|
+
}
|
|
399
|
+
if (declaration?.type === "VariableDeclaration") return (declaration.declarations ?? []).some((decl) => isIdentifier(decl.id) && decl.id.name === "render");
|
|
400
|
+
return (node.specifiers ?? []).some((specifier) => {
|
|
401
|
+
const exported = specifier.exported;
|
|
402
|
+
return isIdentifier(exported) && exported.name === "render";
|
|
403
|
+
});
|
|
404
|
+
})) throw new Error(`ssr: Invalid entry '${relativePath}'. Export \`render(c, assetTags)\` or \`export default defineRender(...)\`.`);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Validate SSR mode entry conventions.
|
|
408
|
+
* Returns null when SSR mode is not used.
|
|
409
|
+
*/
|
|
410
|
+
function validateSsrEntries(root) {
|
|
411
|
+
const server = resolveSingleEntry(root, SSR_SERVER_ENTRY_RELATIVE_PATHS, "SSR");
|
|
412
|
+
const client = resolveSingleEntry(root, SSR_CLIENT_ENTRY_RELATIVE_PATHS, "SSR client");
|
|
413
|
+
if (!server && !client) return null;
|
|
414
|
+
if (server && !client) throw new Error(`ssr: Missing client entry. Add one of: ${SSR_CLIENT_ENTRY_RELATIVE_PATHS.join(", ")}.`);
|
|
415
|
+
if (!server && client) throw new Error(`ssr: Missing server entry. Add one of: ${SSR_SERVER_ENTRY_RELATIVE_PATHS.join(", ")}.`);
|
|
416
|
+
validateServerRenderExport(server);
|
|
417
|
+
return {
|
|
418
|
+
server,
|
|
419
|
+
client
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function validateSsrEntry(root) {
|
|
423
|
+
return validateSsrEntries(root)?.server.absolutePath ?? null;
|
|
424
|
+
}
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region src/plugin-inference.ts
|
|
427
|
+
const SOURCE_GLOB = "**/*.{ts,tsx,mts,js,jsx,mjs,astro}";
|
|
428
|
+
/** Directories to scan for binding inference in framework mode. */
|
|
429
|
+
const FRAMEWORK_SCAN_DIRS = [
|
|
430
|
+
"src",
|
|
431
|
+
"app",
|
|
432
|
+
"pages",
|
|
433
|
+
"routes",
|
|
434
|
+
"server",
|
|
435
|
+
"crons",
|
|
436
|
+
"queues"
|
|
437
|
+
];
|
|
438
|
+
/** Directories to scan for binding inference in Void app mode. */
|
|
439
|
+
const DEFAULT_SCAN_DIRS = [
|
|
440
|
+
"routes",
|
|
441
|
+
"middleware",
|
|
442
|
+
"queues",
|
|
443
|
+
"pages",
|
|
444
|
+
"crons",
|
|
445
|
+
"src"
|
|
446
|
+
];
|
|
447
|
+
/**
|
|
448
|
+
* Map from npm package name to framework identifier.
|
|
449
|
+
* Only packages that unambiguously indicate framework mode are listed.
|
|
450
|
+
* For example, `react-router` alone is a client-side router — only
|
|
451
|
+
* `@react-router/dev` signals the framework (Vite plugin) mode.
|
|
452
|
+
*/
|
|
453
|
+
const FRAMEWORK_PACKAGES = {
|
|
454
|
+
"@tanstack/react-start": {
|
|
455
|
+
name: "tanstack-start",
|
|
456
|
+
class: "a"
|
|
457
|
+
},
|
|
458
|
+
"@react-router/dev": {
|
|
459
|
+
name: "react-router",
|
|
460
|
+
class: "a"
|
|
461
|
+
},
|
|
462
|
+
vinext: {
|
|
463
|
+
name: "vinext-pages",
|
|
464
|
+
class: "a"
|
|
465
|
+
},
|
|
466
|
+
"@sveltejs/kit": {
|
|
467
|
+
name: "sveltekit",
|
|
468
|
+
class: "b"
|
|
469
|
+
},
|
|
470
|
+
nuxt: {
|
|
471
|
+
name: "nuxt",
|
|
472
|
+
class: "c"
|
|
473
|
+
},
|
|
474
|
+
"@analogjs/platform": {
|
|
475
|
+
name: "analog",
|
|
476
|
+
class: "c"
|
|
477
|
+
},
|
|
478
|
+
astro: {
|
|
479
|
+
name: "astro",
|
|
480
|
+
class: "c"
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
/**
|
|
484
|
+
* Detect a meta-framework by checking package.json dependencies.
|
|
485
|
+
* Returns the detected framework with class info, or `undefined`.
|
|
486
|
+
*/
|
|
487
|
+
function detectFramework(root) {
|
|
488
|
+
let pkg;
|
|
489
|
+
try {
|
|
490
|
+
pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf-8"));
|
|
491
|
+
} catch {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const deps = {
|
|
495
|
+
...pkg.dependencies,
|
|
496
|
+
...pkg.devDependencies
|
|
497
|
+
};
|
|
498
|
+
for (const [pkgName, framework] of Object.entries(FRAMEWORK_PACKAGES)) if (pkgName in deps) {
|
|
499
|
+
if (framework.name === "vinext-pages" && existsSync(join(root, "app"))) return {
|
|
500
|
+
name: "vinext-app",
|
|
501
|
+
class: "a"
|
|
502
|
+
};
|
|
503
|
+
return framework;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function extractAstroFrontmatter(code) {
|
|
507
|
+
return code.match(/^\uFEFF?---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1] ?? "";
|
|
508
|
+
}
|
|
509
|
+
function inferBindingsFromFile(file, code) {
|
|
510
|
+
const filename = file.split("/").pop() ?? "unknown.ts";
|
|
511
|
+
if (!filename.endsWith(".astro")) return inferBindingsFromSource(code, filename);
|
|
512
|
+
return inferBindingsFromSource(extractAstroFrontmatter(code), filename.replace(/\.astro$/, ".ts"));
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Synchronously infer which Cloudflare bindings the project needs
|
|
516
|
+
* by scanning route/middleware files and optional src/main.ssr.ts with oxc-parser.
|
|
517
|
+
*
|
|
518
|
+
* This runs once when voidPlugin() is called (before plugins initialize),
|
|
519
|
+
* so results are available to configure the Cloudflare plugin.
|
|
520
|
+
*
|
|
521
|
+
* @param scanDirs - Override the default scan directories (e.g., for framework mode)
|
|
522
|
+
*/
|
|
523
|
+
function inferProjectBindings(root, options, scanDirs) {
|
|
524
|
+
if (options?.bindings) return {
|
|
525
|
+
needsD1: Boolean(options.bindings.db),
|
|
526
|
+
needsKV: Boolean(options.bindings.kv),
|
|
527
|
+
needsR2: Boolean(options.bindings.storage),
|
|
528
|
+
needsAuth: false,
|
|
529
|
+
needsQueues: false,
|
|
530
|
+
needsAI: Boolean(options.bindings.ai),
|
|
531
|
+
needsSandbox: false,
|
|
532
|
+
needsLive: false
|
|
533
|
+
};
|
|
534
|
+
const dirs = scanDirs ?? DEFAULT_SCAN_DIRS;
|
|
535
|
+
const allBindings = [];
|
|
536
|
+
for (const dir of dirs) {
|
|
537
|
+
const dirPath = join(root, dir);
|
|
538
|
+
let files;
|
|
539
|
+
try {
|
|
540
|
+
files = globSync(SOURCE_GLOB, {
|
|
541
|
+
cwd: dirPath,
|
|
542
|
+
absolute: true
|
|
543
|
+
});
|
|
544
|
+
} catch {
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
for (const file of files) {
|
|
548
|
+
const code = readFileSync(file, "utf-8");
|
|
549
|
+
allBindings.push(inferBindingsFromFile(file, code));
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const ssrEntry = getSsrServerEntryPath(root);
|
|
553
|
+
if (ssrEntry) {
|
|
554
|
+
const code = readFileSync(ssrEntry, "utf-8");
|
|
555
|
+
const filename = ssrEntry.split("/").pop() ?? "main.ssr.ts";
|
|
556
|
+
allBindings.push(inferBindingsFromSource(code, filename));
|
|
557
|
+
}
|
|
558
|
+
if (!new Set(dirs).has("src")) {
|
|
559
|
+
const srcPath = join(root, "src");
|
|
560
|
+
let srcFiles;
|
|
561
|
+
try {
|
|
562
|
+
srcFiles = globSync(SOURCE_GLOB, {
|
|
563
|
+
cwd: srcPath,
|
|
564
|
+
absolute: true
|
|
565
|
+
});
|
|
566
|
+
} catch {
|
|
567
|
+
srcFiles = [];
|
|
568
|
+
}
|
|
569
|
+
for (const file of srcFiles) {
|
|
570
|
+
const code = readFileSync(file, "utf-8");
|
|
571
|
+
allBindings.push(inferBindingsFromFile(file, code));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return mergeBindings(allBindings);
|
|
575
|
+
}
|
|
576
|
+
//#endregion
|
|
577
|
+
export { SSR_CLIENT_ENTRY_RELATIVE_PATHS as a, validateSsrEntries as c, isCallExpression as d, isIdentifier as f, walk as g, isObjectExpression as h, inferProjectBindings as i, validateSsrEntry as l, isMemberExpression as m, FRAMEWORK_SCAN_DIRS as n, SSR_SERVER_ENTRY_RELATIVE_PATHS as o, isLiteral as p, detectFramework as r, hasSsrEntry as s, DEFAULT_SCAN_DIRS as t, inferBindingsFromSource as u };
|
|
@@ -3,8 +3,8 @@ import { n as cliTitle } from "./output-BwlcIYSR.mjs";
|
|
|
3
3
|
import { n as loadVoidAuthConfig, t as findVoidAuthConfig } from "./config-CvHtTM0q.mjs";
|
|
4
4
|
import { c as R, g as ge, u as Se, y as ye } from "./dist-Dayj3gCK.mjs";
|
|
5
5
|
import { c as getDatabaseDialect, f as readConfig } from "./config-BIa9HwVX.mjs";
|
|
6
|
-
import { t as scanRoutes } from "./scan-
|
|
7
|
-
import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework } from "./plugin-inference-
|
|
6
|
+
import { t as scanRoutes } from "./scan-vv5u7rHu.mjs";
|
|
7
|
+
import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework } from "./plugin-inference-CXUuq0aC.mjs";
|
|
8
8
|
import { i as generateDbDts, n as generateQueueTypes, t as generateRouteTypes } from "./route-types-DReF1gUY.mjs";
|
|
9
9
|
import { n as ensureProjectTsconfig } from "./project-tsconfig-DfkESbDL.mjs";
|
|
10
10
|
import { t as discoverSchema } from "./discover-B7FkXBLB.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as join, s as resolve } from "./pathe.M-eThtNZ-D-kmWkCS.mjs";
|
|
2
|
-
import { s as hasSsrEntry } from "./plugin-inference-
|
|
2
|
+
import { s as hasSsrEntry } from "./plugin-inference-CXUuq0aC.mjs";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
//#region src/cli/preset.ts
|
|
5
5
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { n as HeadDescriptor, t as HeadConfig } from "./head-CZGAunBV.mjs";
|
|
2
2
|
import { n as CloudContext, t as CloudBindings } from "./handler-dKQWyF-G.mjs";
|
|
3
|
+
import { t as DevCssAsset } from "./types-D6AuY5Bf.mjs";
|
|
3
4
|
|
|
4
5
|
//#region src/pages/protocol.d.ts
|
|
5
6
|
/** Escape characters that are unsafe inside inline <script> tags to prevent XSS. */
|
|
@@ -22,6 +23,8 @@ type AssetTags = {
|
|
|
22
23
|
css: string;
|
|
23
24
|
preloads: string;
|
|
24
25
|
body: string;
|
|
26
|
+
devCss?: Array<DevCssAsset>;
|
|
27
|
+
devCssNonce?: string;
|
|
25
28
|
};
|
|
26
29
|
type RenderPage = (pageObj: PageObject, assetTags: AssetTags) => Promise<PageRenderResult>;
|
|
27
30
|
type GetAssetTags = (req: Request, env: CloudBindings, componentId: string) => Promise<AssetTags>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as join, t as basename } from "./pathe.M-eThtNZ-D-kmWkCS.mjs";
|
|
2
2
|
import { n as globSync, t as glob } from "./dist-DUyXJLkq.mjs";
|
|
3
|
-
import { d as isCallExpression, f as isIdentifier, h as isObjectExpression, p as isLiteral } from "./plugin-inference-
|
|
3
|
+
import { d as isCallExpression, f as isIdentifier, h as isObjectExpression, p as isLiteral } from "./plugin-inference-CXUuq0aC.mjs";
|
|
4
4
|
import { o as parseRouteFilename, s as parseWebSocketFilename, t as scanPages } from "./scan-Ba4hFwlH.mjs";
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
import { parseSync } from "vite";
|
|
@@ -53,5 +53,13 @@ interface PageScanResult {
|
|
|
53
53
|
layouts: Array<LayoutDefinition>;
|
|
54
54
|
namedLayouts: Array<NamedLayoutDefinition>;
|
|
55
55
|
}
|
|
56
|
+
interface DevCssAsset {
|
|
57
|
+
/** Vite client URL used to request the CSS module. */
|
|
58
|
+
url: string;
|
|
59
|
+
/** Vite dev style id used by the client to adopt/update SSR-inlined styles. */
|
|
60
|
+
id?: string;
|
|
61
|
+
/** CSS bytes returned by Vite's client environment transform with ?inline. */
|
|
62
|
+
css: string;
|
|
63
|
+
}
|
|
56
64
|
//#endregion
|
|
57
|
-
export { PageScanResult as i,
|
|
65
|
+
export { PageScanResult as a, PageDefinition as i, LayoutDefinition as n, NamedLayoutDefinition as r, DevCssAsset as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "void",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/voidzero-dev/void.git",
|
|
@@ -300,6 +300,7 @@
|
|
|
300
300
|
"drizzle-orm": "^0.45.2",
|
|
301
301
|
"drizzle-valibot": "^0.4.2",
|
|
302
302
|
"drizzle-zod": "^0.8.3",
|
|
303
|
+
"es-module-lexer": "^2.1.0",
|
|
303
304
|
"hono": "^4.12.18",
|
|
304
305
|
"ignore": "^7.0.5",
|
|
305
306
|
"jsonc-parser": "3.3.1",
|
|
@@ -314,7 +315,6 @@
|
|
|
314
315
|
"@types/pg": "^8.20.0",
|
|
315
316
|
"@typescript/native-preview": "7.0.0-dev.20260507.1",
|
|
316
317
|
"arktype": "^2.2.0",
|
|
317
|
-
"es-module-lexer": "^2.1.0",
|
|
318
318
|
"estree-walker": "^3.0.3",
|
|
319
319
|
"magic-string": "^0.30.21",
|
|
320
320
|
"ofetch": "2.0.0-alpha.3",
|
|
@@ -332,7 +332,7 @@
|
|
|
332
332
|
"valibot": ">=1.0.0-beta.7",
|
|
333
333
|
"vite": "^8.0.0",
|
|
334
334
|
"zod": "^3.25.0 || ^4.0.0",
|
|
335
|
-
"@void/md": "0.7.
|
|
335
|
+
"@void/md": "0.7.6"
|
|
336
336
|
},
|
|
337
337
|
"peerDependenciesMeta": {
|
|
338
338
|
"@void/md": {
|