@netrojs/fnetro 0.1.6 → 0.2.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.md +686 -871
- package/client.ts +220 -200
- package/core.ts +146 -644
- package/dist/client.d.ts +99 -155
- package/dist/client.js +177 -570
- package/dist/core.d.ts +69 -156
- package/dist/core.js +31 -452
- package/dist/server.d.ts +120 -179
- package/dist/server.js +278 -553
- package/package.json +17 -8
- package/server.ts +455 -247
package/dist/server.js
CHANGED
|
@@ -1,413 +1,9 @@
|
|
|
1
1
|
// server.ts
|
|
2
2
|
import { Hono } from "hono";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { createComponent } from "solid-js";
|
|
4
|
+
import { renderToStringAsync, generateHydrationScript } from "solid-js/web";
|
|
5
5
|
|
|
6
6
|
// core.ts
|
|
7
|
-
var RAW = /* @__PURE__ */ Symbol("raw");
|
|
8
|
-
var IS_REACTIVE = /* @__PURE__ */ Symbol("isReactive");
|
|
9
|
-
var IS_READONLY = /* @__PURE__ */ Symbol("isReadonly");
|
|
10
|
-
var IS_REF = /* @__PURE__ */ Symbol("isRef");
|
|
11
|
-
var MARK_RAW = /* @__PURE__ */ Symbol("markRaw");
|
|
12
|
-
var targetMap = /* @__PURE__ */ new WeakMap();
|
|
13
|
-
var activeEffect = null;
|
|
14
|
-
var shouldTrack = true;
|
|
15
|
-
var trackStack = [];
|
|
16
|
-
function pauseTracking() {
|
|
17
|
-
trackStack.push(shouldTrack);
|
|
18
|
-
shouldTrack = false;
|
|
19
|
-
}
|
|
20
|
-
function resetTracking() {
|
|
21
|
-
shouldTrack = trackStack.pop() ?? true;
|
|
22
|
-
}
|
|
23
|
-
function track(target, key) {
|
|
24
|
-
if (!shouldTrack || !activeEffect) return;
|
|
25
|
-
let depsMap = targetMap.get(target);
|
|
26
|
-
if (!depsMap) targetMap.set(target, depsMap = /* @__PURE__ */ new Map());
|
|
27
|
-
let dep = depsMap.get(key);
|
|
28
|
-
if (!dep) depsMap.set(key, dep = /* @__PURE__ */ new Set());
|
|
29
|
-
trackEffect(activeEffect, dep);
|
|
30
|
-
}
|
|
31
|
-
function trackEffect(effect2, dep) {
|
|
32
|
-
if (!dep.has(effect2)) {
|
|
33
|
-
dep.add(effect2);
|
|
34
|
-
effect2.deps.push(dep);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function trigger(target, key, newVal, oldVal) {
|
|
38
|
-
const depsMap = targetMap.get(target);
|
|
39
|
-
if (!depsMap) return;
|
|
40
|
-
const effects = [];
|
|
41
|
-
const computedEffects = [];
|
|
42
|
-
depsMap.get(key)?.forEach((e) => {
|
|
43
|
-
if (e !== activeEffect) {
|
|
44
|
-
e.computed ? computedEffects.push(e) : effects.push(e);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
[...computedEffects, ...effects].forEach((e) => {
|
|
48
|
-
if (e.active) e.scheduler ? e.scheduler() : e.run();
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
var ReactiveEffect = class {
|
|
52
|
-
constructor(fn, scheduler, scope) {
|
|
53
|
-
this.fn = fn;
|
|
54
|
-
this.scheduler = scheduler;
|
|
55
|
-
this.scope = scope;
|
|
56
|
-
scope?.effects.push(this);
|
|
57
|
-
}
|
|
58
|
-
deps = [];
|
|
59
|
-
active = true;
|
|
60
|
-
cleanup;
|
|
61
|
-
computed = false;
|
|
62
|
-
run() {
|
|
63
|
-
if (!this.active) return this.fn();
|
|
64
|
-
const prevEffect = activeEffect;
|
|
65
|
-
const prevShouldTrack = shouldTrack;
|
|
66
|
-
shouldTrack = true;
|
|
67
|
-
activeEffect = this;
|
|
68
|
-
this.cleanup?.();
|
|
69
|
-
this.cleanup = void 0;
|
|
70
|
-
this.deps.length = 0;
|
|
71
|
-
try {
|
|
72
|
-
const result = this.fn();
|
|
73
|
-
if (typeof result === "function") this.cleanup = result;
|
|
74
|
-
return result;
|
|
75
|
-
} finally {
|
|
76
|
-
activeEffect = prevEffect;
|
|
77
|
-
shouldTrack = prevShouldTrack;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
stop() {
|
|
81
|
-
if (this.active) {
|
|
82
|
-
cleanupEffect(this);
|
|
83
|
-
this.active = false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
function cleanupEffect(e) {
|
|
88
|
-
e.deps.forEach((dep) => dep.delete(e));
|
|
89
|
-
e.deps.length = 0;
|
|
90
|
-
}
|
|
91
|
-
var activeScope;
|
|
92
|
-
var EffectScope = class {
|
|
93
|
-
effects = [];
|
|
94
|
-
cleanups = [];
|
|
95
|
-
active = true;
|
|
96
|
-
run(fn) {
|
|
97
|
-
const prev = activeScope;
|
|
98
|
-
activeScope = this;
|
|
99
|
-
try {
|
|
100
|
-
return fn();
|
|
101
|
-
} finally {
|
|
102
|
-
activeScope = prev;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
stop() {
|
|
106
|
-
if (this.active) {
|
|
107
|
-
this.effects.forEach((e) => e.stop());
|
|
108
|
-
this.cleanups.forEach((fn) => fn());
|
|
109
|
-
this.active = false;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
onCleanup(fn) {
|
|
113
|
-
this.cleanups.push(fn);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
function effectScope() {
|
|
117
|
-
return new EffectScope();
|
|
118
|
-
}
|
|
119
|
-
function effect(fn) {
|
|
120
|
-
const e = new ReactiveEffect(fn, void 0, activeScope);
|
|
121
|
-
e.run();
|
|
122
|
-
return () => e.stop();
|
|
123
|
-
}
|
|
124
|
-
function watchEffect(fn, opts) {
|
|
125
|
-
const e = new ReactiveEffect(fn, void 0, activeScope);
|
|
126
|
-
e.run();
|
|
127
|
-
return () => e.stop();
|
|
128
|
-
}
|
|
129
|
-
var refTarget = /* @__PURE__ */ Symbol("refTarget");
|
|
130
|
-
var RefImpl = class {
|
|
131
|
-
constructor(value, shallow = false) {
|
|
132
|
-
this.shallow = shallow;
|
|
133
|
-
this._value = shallow ? value : toReactive(value);
|
|
134
|
-
}
|
|
135
|
-
[IS_REF] = true;
|
|
136
|
-
_value;
|
|
137
|
-
_subscribers = /* @__PURE__ */ new Set();
|
|
138
|
-
get value() {
|
|
139
|
-
track(this, refTarget);
|
|
140
|
-
this._subscribers.forEach((fn) => {
|
|
141
|
-
});
|
|
142
|
-
return this._value;
|
|
143
|
-
}
|
|
144
|
-
set value(next) {
|
|
145
|
-
const newVal = this.shallow ? next : toReactive(next);
|
|
146
|
-
if (!hasChanged(newVal, this._value)) return;
|
|
147
|
-
this._value = newVal;
|
|
148
|
-
trigger(this, refTarget, newVal, this._value);
|
|
149
|
-
this._subscribers.forEach((fn) => fn());
|
|
150
|
-
}
|
|
151
|
-
/** Subscribe for useSyncExternalStore */
|
|
152
|
-
subscribe(fn) {
|
|
153
|
-
this._subscribers.add(fn);
|
|
154
|
-
return () => this._subscribers.delete(fn);
|
|
155
|
-
}
|
|
156
|
-
peek() {
|
|
157
|
-
return this._value;
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
function ref(value) {
|
|
161
|
-
return isRef(value) ? value : new RefImpl(value);
|
|
162
|
-
}
|
|
163
|
-
function shallowRef(value) {
|
|
164
|
-
return new RefImpl(value, true);
|
|
165
|
-
}
|
|
166
|
-
function triggerRef(r) {
|
|
167
|
-
if (r instanceof RefImpl) {
|
|
168
|
-
trigger(r, refTarget);
|
|
169
|
-
r._subscribers.forEach((fn) => fn());
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
function isRef(r) {
|
|
173
|
-
return !!r && typeof r === "object" && r[IS_REF] === true;
|
|
174
|
-
}
|
|
175
|
-
function unref(r) {
|
|
176
|
-
return isRef(r) ? r.value : r;
|
|
177
|
-
}
|
|
178
|
-
function toRef(obj, key) {
|
|
179
|
-
const r = new RefImpl(void 0, false);
|
|
180
|
-
Object.defineProperty(r, "value", {
|
|
181
|
-
get() {
|
|
182
|
-
track(r, refTarget);
|
|
183
|
-
return obj[key];
|
|
184
|
-
},
|
|
185
|
-
set(v) {
|
|
186
|
-
obj[key] = v;
|
|
187
|
-
trigger(r, refTarget, v, obj[key]);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
return r;
|
|
191
|
-
}
|
|
192
|
-
function toRefs(obj) {
|
|
193
|
-
const result = {};
|
|
194
|
-
for (const key in obj) result[key] = toRef(obj, key);
|
|
195
|
-
return result;
|
|
196
|
-
}
|
|
197
|
-
var ComputedRefImpl = class {
|
|
198
|
-
constructor(getter, setter) {
|
|
199
|
-
this.setter = setter;
|
|
200
|
-
this.effect = new ReactiveEffect(getter, () => {
|
|
201
|
-
if (!this._dirty) {
|
|
202
|
-
this._dirty = true;
|
|
203
|
-
trigger(this, refTarget);
|
|
204
|
-
this._subscribers.forEach((fn) => fn());
|
|
205
|
-
}
|
|
206
|
-
}, activeScope);
|
|
207
|
-
this.effect.computed = true;
|
|
208
|
-
}
|
|
209
|
-
[IS_REF] = true;
|
|
210
|
-
effect;
|
|
211
|
-
_value;
|
|
212
|
-
_dirty = true;
|
|
213
|
-
_subscribers = /* @__PURE__ */ new Set();
|
|
214
|
-
get value() {
|
|
215
|
-
track(this, refTarget);
|
|
216
|
-
if (this._dirty) {
|
|
217
|
-
this._dirty = false;
|
|
218
|
-
this._value = this.effect.run();
|
|
219
|
-
}
|
|
220
|
-
return this._value;
|
|
221
|
-
}
|
|
222
|
-
set value(v) {
|
|
223
|
-
this.setter?.(v);
|
|
224
|
-
}
|
|
225
|
-
subscribe(fn) {
|
|
226
|
-
this._subscribers.add(fn);
|
|
227
|
-
return () => this._subscribers.delete(fn);
|
|
228
|
-
}
|
|
229
|
-
peek() {
|
|
230
|
-
return this._value;
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
function computed(arg) {
|
|
234
|
-
if (typeof arg === "function") {
|
|
235
|
-
return new ComputedRefImpl(arg);
|
|
236
|
-
}
|
|
237
|
-
return new ComputedRefImpl(arg.get, arg.set);
|
|
238
|
-
}
|
|
239
|
-
var reactiveMap = /* @__PURE__ */ new WeakMap();
|
|
240
|
-
var readonlyMap = /* @__PURE__ */ new WeakMap();
|
|
241
|
-
var shallowReactiveMap = /* @__PURE__ */ new WeakMap();
|
|
242
|
-
function toReactive(value) {
|
|
243
|
-
return value !== null && typeof value === "object" ? reactive(value) : value;
|
|
244
|
-
}
|
|
245
|
-
var arrayInstrumentations = {};
|
|
246
|
-
["includes", "indexOf", "lastIndexOf"].forEach((method) => {
|
|
247
|
-
arrayInstrumentations[method] = function(...args) {
|
|
248
|
-
const arr = toRaw(this);
|
|
249
|
-
for (let i = 0; i < this.length; i++) track(arr, i);
|
|
250
|
-
let res = arr[method](...args);
|
|
251
|
-
if (res === -1 || res === false) res = arr[method](...args.map(toRaw));
|
|
252
|
-
return res;
|
|
253
|
-
};
|
|
254
|
-
});
|
|
255
|
-
["push", "pop", "shift", "unshift", "splice"].forEach((method) => {
|
|
256
|
-
arrayInstrumentations[method] = function(...args) {
|
|
257
|
-
pauseTracking();
|
|
258
|
-
const res = toRaw(this)[method].apply(this, args);
|
|
259
|
-
resetTracking();
|
|
260
|
-
return res;
|
|
261
|
-
};
|
|
262
|
-
});
|
|
263
|
-
function createHandler(shallow = false, readonly2 = false) {
|
|
264
|
-
return {
|
|
265
|
-
get(target, key, receiver) {
|
|
266
|
-
if (key === RAW) return target;
|
|
267
|
-
if (key === IS_REACTIVE) return !readonly2;
|
|
268
|
-
if (key === IS_READONLY) return readonly2;
|
|
269
|
-
if (key === MARK_RAW) return target[MARK_RAW];
|
|
270
|
-
const isArray = Array.isArray(target);
|
|
271
|
-
if (!readonly2 && isArray && hasOwn(arrayInstrumentations, key)) {
|
|
272
|
-
return Reflect.get(arrayInstrumentations, key, receiver);
|
|
273
|
-
}
|
|
274
|
-
const res = Reflect.get(target, key, receiver);
|
|
275
|
-
if (typeof key === "symbol" || key === "__proto__") return res;
|
|
276
|
-
if (!readonly2) track(target, key);
|
|
277
|
-
if (shallow) return res;
|
|
278
|
-
if (isRef(res)) return isArray ? res : res.value;
|
|
279
|
-
return res !== null && typeof res === "object" && !res[MARK_RAW] ? readonly2 ? readonlyProxy(res) : reactive(res) : res;
|
|
280
|
-
},
|
|
281
|
-
set(target, key, value, receiver) {
|
|
282
|
-
if (readonly2) {
|
|
283
|
-
console.warn(`[fnetro] Cannot set "${String(key)}" on readonly object`);
|
|
284
|
-
return true;
|
|
285
|
-
}
|
|
286
|
-
const oldVal = target[key];
|
|
287
|
-
const result = Reflect.set(target, key, value, receiver);
|
|
288
|
-
if (hasChanged(value, oldVal)) trigger(target, key, value, oldVal);
|
|
289
|
-
return result;
|
|
290
|
-
},
|
|
291
|
-
deleteProperty(target, key) {
|
|
292
|
-
if (readonly2) return true;
|
|
293
|
-
const hadKey = hasOwn(target, key);
|
|
294
|
-
const result = Reflect.deleteProperty(target, key);
|
|
295
|
-
if (hadKey && result) trigger(target, key);
|
|
296
|
-
return result;
|
|
297
|
-
},
|
|
298
|
-
has(target, key) {
|
|
299
|
-
const res = Reflect.has(target, key);
|
|
300
|
-
track(target, key);
|
|
301
|
-
return res;
|
|
302
|
-
},
|
|
303
|
-
ownKeys(target) {
|
|
304
|
-
track(target, Array.isArray(target) ? "length" : "__iterate__");
|
|
305
|
-
return Reflect.ownKeys(target);
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
function reactive(target) {
|
|
310
|
-
if (isReadonly(target)) return target;
|
|
311
|
-
if (target[MARK_RAW]) return target;
|
|
312
|
-
if (reactiveMap.has(target)) return reactiveMap.get(target);
|
|
313
|
-
const proxy = new Proxy(target, createHandler());
|
|
314
|
-
reactiveMap.set(target, proxy);
|
|
315
|
-
return proxy;
|
|
316
|
-
}
|
|
317
|
-
function shallowReactive(target) {
|
|
318
|
-
if (shallowReactiveMap.has(target)) return shallowReactiveMap.get(target);
|
|
319
|
-
const proxy = new Proxy(target, createHandler(true));
|
|
320
|
-
shallowReactiveMap.set(target, proxy);
|
|
321
|
-
return proxy;
|
|
322
|
-
}
|
|
323
|
-
function readonlyProxy(target) {
|
|
324
|
-
if (readonlyMap.has(target)) return readonlyMap.get(target);
|
|
325
|
-
const proxy = new Proxy(target, createHandler(false, true));
|
|
326
|
-
readonlyMap.set(target, proxy);
|
|
327
|
-
return proxy;
|
|
328
|
-
}
|
|
329
|
-
function readonly(target) {
|
|
330
|
-
return readonlyProxy(target);
|
|
331
|
-
}
|
|
332
|
-
function markRaw(value) {
|
|
333
|
-
;
|
|
334
|
-
value[MARK_RAW] = true;
|
|
335
|
-
return value;
|
|
336
|
-
}
|
|
337
|
-
function toRaw(observed) {
|
|
338
|
-
const raw = observed?.[RAW];
|
|
339
|
-
return raw ? toRaw(raw) : observed;
|
|
340
|
-
}
|
|
341
|
-
function isReactive(value) {
|
|
342
|
-
if (isReadonly(value)) return isReactive(value[RAW]);
|
|
343
|
-
return !!(value && value[IS_REACTIVE]);
|
|
344
|
-
}
|
|
345
|
-
function isReadonly(value) {
|
|
346
|
-
return !!(value && value[IS_READONLY]);
|
|
347
|
-
}
|
|
348
|
-
function traverse(value, seen = /* @__PURE__ */ new Set()) {
|
|
349
|
-
if (!value || typeof value !== "object" || seen.has(value)) return value;
|
|
350
|
-
seen.add(value);
|
|
351
|
-
if (isRef(value)) {
|
|
352
|
-
traverse(value.value, seen);
|
|
353
|
-
return value;
|
|
354
|
-
}
|
|
355
|
-
if (Array.isArray(value)) {
|
|
356
|
-
value.forEach((v) => traverse(v, seen));
|
|
357
|
-
return value;
|
|
358
|
-
}
|
|
359
|
-
for (const key in value) traverse(value[key], seen);
|
|
360
|
-
return value;
|
|
361
|
-
}
|
|
362
|
-
function normalizeSource(src) {
|
|
363
|
-
if (Array.isArray(src)) return () => src.map((s) => isRef(s) ? s.value : s());
|
|
364
|
-
if (isRef(src)) return () => src.value;
|
|
365
|
-
return src;
|
|
366
|
-
}
|
|
367
|
-
function watch(source, cb, opts = {}) {
|
|
368
|
-
const getter = opts.deep ? () => traverse(normalizeSource(source)()) : normalizeSource(source);
|
|
369
|
-
let oldVal = void 0;
|
|
370
|
-
let cleanupFn;
|
|
371
|
-
const cleanup = (fn) => {
|
|
372
|
-
cleanupFn = fn;
|
|
373
|
-
};
|
|
374
|
-
const job = () => {
|
|
375
|
-
if (!effect2.active) return;
|
|
376
|
-
cleanupFn?.();
|
|
377
|
-
cleanupFn = void 0;
|
|
378
|
-
const newVal = effect2.run();
|
|
379
|
-
if (opts.deep || hasChanged(newVal, oldVal)) {
|
|
380
|
-
cb(newVal, oldVal, cleanup);
|
|
381
|
-
oldVal = newVal;
|
|
382
|
-
}
|
|
383
|
-
if (opts.once) effect2.stop();
|
|
384
|
-
};
|
|
385
|
-
const effect2 = new ReactiveEffect(getter, job, activeScope);
|
|
386
|
-
if (opts.immediate) {
|
|
387
|
-
cleanupFn?.();
|
|
388
|
-
cleanupFn = void 0;
|
|
389
|
-
const val = effect2.run();
|
|
390
|
-
cb(val, oldVal, cleanup);
|
|
391
|
-
oldVal = val;
|
|
392
|
-
} else {
|
|
393
|
-
oldVal = effect2.run();
|
|
394
|
-
}
|
|
395
|
-
return () => effect2.stop();
|
|
396
|
-
}
|
|
397
|
-
var __hooks = {
|
|
398
|
-
useValue: (r) => isRef(r) ? r.value : r(),
|
|
399
|
-
useLocalRef: (init) => ref(init),
|
|
400
|
-
useLocalReactive: (init) => reactive(init)
|
|
401
|
-
};
|
|
402
|
-
function use(source) {
|
|
403
|
-
return __hooks.useValue(source);
|
|
404
|
-
}
|
|
405
|
-
function useLocalRef(init) {
|
|
406
|
-
return __hooks.useLocalRef(init);
|
|
407
|
-
}
|
|
408
|
-
function useLocalReactive(init) {
|
|
409
|
-
return __hooks.useLocalReactive(init);
|
|
410
|
-
}
|
|
411
7
|
function definePage(def) {
|
|
412
8
|
return { __type: "page", ...def };
|
|
413
9
|
}
|
|
@@ -417,9 +13,6 @@ function defineGroup(def) {
|
|
|
417
13
|
function defineLayout(Component) {
|
|
418
14
|
return { __type: "layout", Component };
|
|
419
15
|
}
|
|
420
|
-
function defineMiddleware(handler) {
|
|
421
|
-
return { __type: "middleware", handler };
|
|
422
|
-
}
|
|
423
16
|
function defineApiRoute(path, register) {
|
|
424
17
|
return { __type: "api", path, register };
|
|
425
18
|
}
|
|
@@ -437,25 +30,16 @@ function resolveRoutes(routes, options = {}) {
|
|
|
437
30
|
pages.push(...sub.pages);
|
|
438
31
|
apis.push(...sub.apis);
|
|
439
32
|
} else {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
33
|
+
pages.push({
|
|
34
|
+
fullPath: (options.prefix ?? "") + route.path,
|
|
35
|
+
page: route,
|
|
36
|
+
layout: route.layout !== void 0 ? route.layout : options.layout,
|
|
37
|
+
middleware: [...options.middleware ?? [], ...route.middleware ?? []]
|
|
38
|
+
});
|
|
444
39
|
}
|
|
445
40
|
}
|
|
446
41
|
return { pages, apis };
|
|
447
42
|
}
|
|
448
|
-
var SPA_HEADER = "x-fnetro-spa";
|
|
449
|
-
var STATE_KEY = "__FNETRO_STATE__";
|
|
450
|
-
var PARAMS_KEY = "__FNETRO_PARAMS__";
|
|
451
|
-
function hasChanged(a, b) {
|
|
452
|
-
return !Object.is(a, b);
|
|
453
|
-
}
|
|
454
|
-
function hasOwn(obj, key) {
|
|
455
|
-
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// server.ts
|
|
459
43
|
function compilePath(path) {
|
|
460
44
|
const keys = [];
|
|
461
45
|
const src = path.replace(/\[\.\.\.([^\]]+)\]/g, (_, k) => {
|
|
@@ -465,76 +49,183 @@ function compilePath(path) {
|
|
|
465
49
|
keys.push(k);
|
|
466
50
|
return "([^/]+)";
|
|
467
51
|
}).replace(/\*/g, "(.*)");
|
|
468
|
-
return { re: new RegExp(`^${src}$`), keys
|
|
52
|
+
return { re: new RegExp(`^${src}$`), keys };
|
|
469
53
|
}
|
|
470
54
|
function matchPath(compiled, pathname) {
|
|
471
55
|
const m = pathname.match(compiled.re);
|
|
472
56
|
if (!m) return null;
|
|
473
57
|
const params = {};
|
|
474
58
|
compiled.keys.forEach((k, i) => {
|
|
475
|
-
params[k] = decodeURIComponent(m[i + 1]);
|
|
59
|
+
params[k] = decodeURIComponent(m[i + 1] ?? "");
|
|
476
60
|
});
|
|
477
61
|
return params;
|
|
478
62
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
<link rel="stylesheet" href="/assets/style.css">
|
|
487
|
-
</head>
|
|
488
|
-
<body>
|
|
489
|
-
<div id="fnetro-app">${opts.pageHtml}</div>
|
|
490
|
-
<script>window.${STATE_KEY}=${opts.stateJson};window.${PARAMS_KEY}=${opts.paramsJson};</script>
|
|
491
|
-
<script type="module" src="/client.ts"></script>
|
|
492
|
-
</body>
|
|
493
|
-
</html>`;
|
|
494
|
-
}
|
|
495
|
-
function escHtml(s) {
|
|
63
|
+
var SPA_HEADER = "x-fnetro-spa";
|
|
64
|
+
var STATE_KEY = "__FNETRO_STATE__";
|
|
65
|
+
var PARAMS_KEY = "__FNETRO_PARAMS__";
|
|
66
|
+
var SEO_KEY = "__FNETRO_SEO__";
|
|
67
|
+
|
|
68
|
+
// server.ts
|
|
69
|
+
function esc(s) {
|
|
496
70
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
497
71
|
}
|
|
498
|
-
|
|
499
|
-
const
|
|
72
|
+
function buildHeadMeta(seo, extraHead = "") {
|
|
73
|
+
const m = (n, v) => v ? `<meta name="${n}" content="${esc(v)}">` : "";
|
|
74
|
+
const p = (pr, v) => v ? `<meta property="${pr}" content="${esc(v)}">` : "";
|
|
75
|
+
const lk = (rel, href) => `<link rel="${rel}" href="${esc(href)}">`;
|
|
76
|
+
const parts = [];
|
|
77
|
+
if (seo.description) parts.push(m("description", seo.description));
|
|
78
|
+
if (seo.keywords) parts.push(m("keywords", seo.keywords));
|
|
79
|
+
if (seo.author) parts.push(m("author", seo.author));
|
|
80
|
+
if (seo.robots) parts.push(m("robots", seo.robots));
|
|
81
|
+
if (seo.themeColor) parts.push(m("theme-color", seo.themeColor));
|
|
82
|
+
if (seo.canonical) parts.push(lk("canonical", seo.canonical));
|
|
83
|
+
if (seo.ogTitle) parts.push(p("og:title", seo.ogTitle));
|
|
84
|
+
if (seo.ogDescription) parts.push(p("og:description", seo.ogDescription));
|
|
85
|
+
if (seo.ogImage) parts.push(p("og:image", seo.ogImage));
|
|
86
|
+
if (seo.ogImageAlt) parts.push(p("og:image:alt", seo.ogImageAlt));
|
|
87
|
+
if (seo.ogImageWidth) parts.push(p("og:image:width", seo.ogImageWidth));
|
|
88
|
+
if (seo.ogImageHeight) parts.push(p("og:image:height", seo.ogImageHeight));
|
|
89
|
+
if (seo.ogUrl) parts.push(p("og:url", seo.ogUrl));
|
|
90
|
+
if (seo.ogType) parts.push(p("og:type", seo.ogType));
|
|
91
|
+
if (seo.ogSiteName) parts.push(p("og:site_name", seo.ogSiteName));
|
|
92
|
+
if (seo.ogLocale) parts.push(p("og:locale", seo.ogLocale));
|
|
93
|
+
if (seo.twitterCard) parts.push(m("twitter:card", seo.twitterCard));
|
|
94
|
+
if (seo.twitterSite) parts.push(m("twitter:site", seo.twitterSite));
|
|
95
|
+
if (seo.twitterCreator) parts.push(m("twitter:creator", seo.twitterCreator));
|
|
96
|
+
if (seo.twitterTitle) parts.push(m("twitter:title", seo.twitterTitle));
|
|
97
|
+
if (seo.twitterDescription) parts.push(m("twitter:description", seo.twitterDescription));
|
|
98
|
+
if (seo.twitterImage) parts.push(m("twitter:image", seo.twitterImage));
|
|
99
|
+
if (seo.twitterImageAlt) parts.push(m("twitter:image:alt", seo.twitterImageAlt));
|
|
100
|
+
for (const tag of seo.extra ?? []) {
|
|
101
|
+
const attrs = [
|
|
102
|
+
tag.name ? `name="${esc(tag.name)}"` : "",
|
|
103
|
+
tag.property ? `property="${esc(tag.property)}"` : "",
|
|
104
|
+
tag.httpEquiv ? `http-equiv="${esc(tag.httpEquiv)}"` : "",
|
|
105
|
+
`content="${esc(tag.content)}"`
|
|
106
|
+
].filter(Boolean).join(" ");
|
|
107
|
+
parts.push(`<meta ${attrs}>`);
|
|
108
|
+
}
|
|
109
|
+
const ld = seo.jsonLd;
|
|
110
|
+
if (ld) {
|
|
111
|
+
const schemas = Array.isArray(ld) ? ld : [ld];
|
|
112
|
+
for (const schema of schemas) {
|
|
113
|
+
parts.push(`<script type="application/ld+json">${JSON.stringify(schema)}</script>`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (extraHead) parts.push(extraHead);
|
|
117
|
+
return parts.join("\n");
|
|
118
|
+
}
|
|
119
|
+
function mergeSEO(base, override) {
|
|
120
|
+
return { ...base ?? {}, ...override ?? {} };
|
|
121
|
+
}
|
|
122
|
+
var _assets = null;
|
|
123
|
+
async function resolveAssets(cfg, defaultEntry) {
|
|
124
|
+
if (_assets) return _assets;
|
|
125
|
+
if (cfg.manifestDir) {
|
|
126
|
+
try {
|
|
127
|
+
const [{ readFileSync }, { join }] = await Promise.all([
|
|
128
|
+
import("fs"),
|
|
129
|
+
import("path")
|
|
130
|
+
]);
|
|
131
|
+
const raw = readFileSync(join(cfg.manifestDir, "manifest.json"), "utf-8");
|
|
132
|
+
const manifest = JSON.parse(raw);
|
|
133
|
+
const entryKey = cfg.manifestEntry ?? Object.keys(manifest).find((k) => k.endsWith(defaultEntry)) ?? defaultEntry;
|
|
134
|
+
const entry = manifest[entryKey];
|
|
135
|
+
if (entry) {
|
|
136
|
+
_assets = {
|
|
137
|
+
scripts: [`/assets/${entry.file}`],
|
|
138
|
+
styles: (entry.css ?? []).map((f) => `/assets/${f}`)
|
|
139
|
+
};
|
|
140
|
+
return _assets;
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
_assets = {
|
|
146
|
+
scripts: cfg.scripts ?? ["/assets/client.js"],
|
|
147
|
+
styles: cfg.styles ?? []
|
|
148
|
+
};
|
|
149
|
+
return _assets;
|
|
150
|
+
}
|
|
151
|
+
function buildShell(o) {
|
|
152
|
+
const htmlAttrStr = Object.entries(o.htmlAttrs ?? { lang: "en" }).map(([k, v]) => `${k}="${esc(v)}"`).join(" ");
|
|
153
|
+
const styleLinks = o.styles.map((href) => `<link rel="stylesheet" href="${esc(href)}">`).join("\n");
|
|
154
|
+
const scriptTags = o.scripts.map((src) => `<script type="module" src="${esc(src)}"></script>`).join("\n");
|
|
155
|
+
return [
|
|
156
|
+
"<!DOCTYPE html>",
|
|
157
|
+
`<html ${htmlAttrStr}>`,
|
|
158
|
+
"<head>",
|
|
159
|
+
'<meta charset="UTF-8">',
|
|
160
|
+
'<meta name="viewport" content="width=device-width,initial-scale=1">',
|
|
161
|
+
`<title>${esc(o.title)}</title>`,
|
|
162
|
+
o.metaHtml,
|
|
163
|
+
generateHydrationScript(),
|
|
164
|
+
styleLinks,
|
|
165
|
+
"</head>",
|
|
166
|
+
"<body>",
|
|
167
|
+
`<div id="fnetro-app">${o.bodyHtml}</div>`,
|
|
168
|
+
"<script>",
|
|
169
|
+
`window.${STATE_KEY}=${o.stateJson};`,
|
|
170
|
+
`window.${PARAMS_KEY}=${o.paramsJson};`,
|
|
171
|
+
`window.${SEO_KEY}=${o.seoJson};`,
|
|
172
|
+
"</script>",
|
|
173
|
+
scriptTags,
|
|
174
|
+
"</body>",
|
|
175
|
+
"</html>"
|
|
176
|
+
].filter(Boolean).join("\n");
|
|
177
|
+
}
|
|
178
|
+
async function renderPage(route, data, url, params, appLayout) {
|
|
500
179
|
const layout = route.layout !== void 0 ? route.layout : appLayout;
|
|
501
|
-
|
|
502
|
-
|
|
180
|
+
return renderToStringAsync(() => {
|
|
181
|
+
const pageEl = createComponent(route.page.Page, { ...data, url, params });
|
|
182
|
+
if (!layout) return pageEl;
|
|
183
|
+
return createComponent(layout.Component, {
|
|
184
|
+
url,
|
|
185
|
+
params,
|
|
186
|
+
get children() {
|
|
187
|
+
return pageEl;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
});
|
|
503
191
|
}
|
|
504
|
-
async function renderFullPage(route, data, url, params,
|
|
505
|
-
const
|
|
192
|
+
async function renderFullPage(route, data, url, params, config, assets) {
|
|
193
|
+
const pageSEO = typeof route.page.seo === "function" ? route.page.seo(data, params) : route.page.seo;
|
|
194
|
+
const seo = mergeSEO(config.seo, pageSEO);
|
|
195
|
+
const title = seo.title ?? "FNetro";
|
|
196
|
+
const bodyHtml = await renderPage(route, data, url, params, config.layout);
|
|
506
197
|
return buildShell({
|
|
507
198
|
title,
|
|
199
|
+
metaHtml: buildHeadMeta(seo, config.head),
|
|
200
|
+
bodyHtml,
|
|
508
201
|
stateJson: JSON.stringify({ [url]: data }),
|
|
509
202
|
paramsJson: JSON.stringify(params),
|
|
510
|
-
|
|
203
|
+
seoJson: JSON.stringify(seo),
|
|
204
|
+
scripts: assets.scripts,
|
|
205
|
+
styles: assets.styles,
|
|
206
|
+
htmlAttrs: config.htmlAttrs
|
|
511
207
|
});
|
|
512
208
|
}
|
|
513
209
|
function createFNetro(config) {
|
|
514
210
|
const app = new Hono();
|
|
515
|
-
app.use("
|
|
516
|
-
await next();
|
|
517
|
-
});
|
|
518
|
-
(config.middleware ?? []).forEach((mw) => app.use("*", mw));
|
|
211
|
+
for (const mw of config.middleware ?? []) app.use("*", mw);
|
|
519
212
|
const { pages, apis } = resolveRoutes(config.routes, {
|
|
520
213
|
layout: config.layout,
|
|
521
214
|
middleware: []
|
|
522
215
|
});
|
|
523
|
-
const compiled = pages.map((r) => ({
|
|
524
|
-
|
|
525
|
-
compiled: compilePath(r.fullPath)
|
|
526
|
-
}));
|
|
527
|
-
apis.forEach((api) => {
|
|
216
|
+
const compiled = pages.map((r) => ({ route: r, cp: compilePath(r.fullPath) }));
|
|
217
|
+
for (const api of apis) {
|
|
528
218
|
const sub = new Hono();
|
|
529
219
|
api.register(sub, config.middleware ?? []);
|
|
530
220
|
app.route(api.path, sub);
|
|
531
|
-
}
|
|
221
|
+
}
|
|
532
222
|
app.all("*", async (c) => {
|
|
533
223
|
const url = new URL(c.req.url);
|
|
534
224
|
const pathname = url.pathname;
|
|
535
225
|
const isSPA = c.req.header(SPA_HEADER) === "1";
|
|
226
|
+
const isDev = process.env["NODE_ENV"] !== "production";
|
|
536
227
|
let matched = null;
|
|
537
|
-
for (const { route: route2,
|
|
228
|
+
for (const { route: route2, cp } of compiled) {
|
|
538
229
|
const params2 = matchPath(cp, pathname);
|
|
539
230
|
if (params2 !== null) {
|
|
540
231
|
matched = { route: route2, params: params2 };
|
|
@@ -543,55 +234,64 @@ function createFNetro(config) {
|
|
|
543
234
|
}
|
|
544
235
|
if (!matched) {
|
|
545
236
|
if (config.notFound) {
|
|
546
|
-
const
|
|
547
|
-
|
|
237
|
+
const html2 = await renderToStringAsync(
|
|
238
|
+
() => createComponent(config.notFound, {})
|
|
239
|
+
);
|
|
240
|
+
return c.html(
|
|
241
|
+
`<!DOCTYPE html><html lang="en"><body>${html2}</body></html>`,
|
|
242
|
+
404
|
|
243
|
+
);
|
|
548
244
|
}
|
|
549
245
|
return c.text("Not Found", 404);
|
|
550
246
|
}
|
|
551
247
|
const { route, params } = matched;
|
|
552
248
|
const origParam = c.req.param.bind(c.req);
|
|
553
|
-
c.req
|
|
554
|
-
let
|
|
249
|
+
c.req["param"] = (key) => key != null ? params[key] ?? origParam(key) : { ...origParam(), ...params };
|
|
250
|
+
let early;
|
|
555
251
|
const handlers = [...route.middleware];
|
|
556
252
|
let idx = 0;
|
|
557
|
-
const
|
|
253
|
+
const runNext = async () => {
|
|
558
254
|
const mw = handlers[idx++];
|
|
559
255
|
if (!mw) return;
|
|
560
|
-
const res = await mw(c,
|
|
561
|
-
if (res instanceof Response && !
|
|
256
|
+
const res = await mw(c, runNext);
|
|
257
|
+
if (res instanceof Response && !early) early = res;
|
|
562
258
|
};
|
|
563
|
-
await
|
|
564
|
-
if (
|
|
565
|
-
const
|
|
566
|
-
const
|
|
259
|
+
await runNext();
|
|
260
|
+
if (early) return early;
|
|
261
|
+
const rawData = route.page.loader ? await route.page.loader(c) : {};
|
|
262
|
+
const data = rawData ?? {};
|
|
567
263
|
if (isSPA) {
|
|
568
|
-
const
|
|
264
|
+
const pageSEO = typeof route.page.seo === "function" ? route.page.seo(data, params) : route.page.seo;
|
|
569
265
|
return c.json({
|
|
570
|
-
|
|
571
|
-
state: safeData,
|
|
266
|
+
state: data,
|
|
572
267
|
params,
|
|
573
|
-
url: pathname
|
|
268
|
+
url: pathname,
|
|
269
|
+
seo: mergeSEO(config.seo, pageSEO)
|
|
574
270
|
});
|
|
575
271
|
}
|
|
576
|
-
const
|
|
577
|
-
|
|
272
|
+
const assets = isDev ? { scripts: [], styles: [] } : await resolveAssets(
|
|
273
|
+
config.assets ?? {},
|
|
274
|
+
config.assets?.manifestEntry ?? "client.ts"
|
|
275
|
+
);
|
|
276
|
+
const html = await renderFullPage(route, data, pathname, params, config, assets);
|
|
277
|
+
return c.html(html);
|
|
578
278
|
});
|
|
579
|
-
return { app, handler: app.fetch };
|
|
279
|
+
return { app, handler: app.fetch.bind(app) };
|
|
580
280
|
}
|
|
581
281
|
function detectRuntime() {
|
|
582
|
-
if (typeof globalThis
|
|
583
|
-
if (typeof globalThis
|
|
282
|
+
if (typeof globalThis["Bun"] !== "undefined") return "bun";
|
|
283
|
+
if (typeof globalThis["Deno"] !== "undefined") return "deno";
|
|
584
284
|
if (typeof process !== "undefined" && process.versions?.node) return "node";
|
|
585
285
|
return "edge";
|
|
586
286
|
}
|
|
587
287
|
async function serve(opts) {
|
|
588
288
|
const runtime = opts.runtime ?? detectRuntime();
|
|
589
|
-
const port = opts.port ?? Number(
|
|
289
|
+
const port = opts.port ?? Number(process?.env?.["PORT"] ?? 3e3);
|
|
590
290
|
const hostname = opts.hostname ?? "0.0.0.0";
|
|
591
291
|
const staticDir = opts.staticDir ?? "./dist";
|
|
592
|
-
const
|
|
292
|
+
const displayHost = hostname === "0.0.0.0" ? "localhost" : hostname;
|
|
593
293
|
const logReady = () => console.log(`
|
|
594
|
-
\u{1F525} FNetro [${runtime}] ready
|
|
294
|
+
\u{1F525} FNetro [${runtime}] ready \u2192 http://${displayHost}:${port}
|
|
595
295
|
`);
|
|
596
296
|
switch (runtime) {
|
|
597
297
|
case "node": {
|
|
@@ -600,134 +300,159 @@ async function serve(opts) {
|
|
|
600
300
|
import("@hono/node-server/serve-static")
|
|
601
301
|
]);
|
|
602
302
|
opts.app.app.use("/assets/*", serveStatic({ root: staticDir }));
|
|
303
|
+
opts.app.app.use("/*", serveStatic({ root: "./public" }));
|
|
603
304
|
nodeServe({ fetch: opts.app.handler, port, hostname });
|
|
604
305
|
logReady();
|
|
605
306
|
break;
|
|
606
307
|
}
|
|
607
308
|
case "bun": {
|
|
608
309
|
;
|
|
609
|
-
globalThis
|
|
310
|
+
globalThis["Bun"].serve({ fetch: opts.app.handler, port, hostname });
|
|
610
311
|
logReady();
|
|
611
312
|
break;
|
|
612
313
|
}
|
|
613
314
|
case "deno": {
|
|
614
315
|
;
|
|
615
|
-
globalThis
|
|
316
|
+
globalThis["Deno"].serve({ port, hostname }, opts.app.handler);
|
|
616
317
|
logReady();
|
|
617
318
|
break;
|
|
618
319
|
}
|
|
619
320
|
default:
|
|
620
|
-
console.warn(
|
|
321
|
+
console.warn(
|
|
322
|
+
"[fnetro] serve() is a no-op on edge runtimes \u2014 export `fnetro.handler` instead."
|
|
323
|
+
);
|
|
621
324
|
}
|
|
622
325
|
}
|
|
623
326
|
var NODE_BUILTINS = /^node:|^(assert|buffer|child_process|cluster|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|perf_hooks|process|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|trace_events|tty|url|util|v8|vm|worker_threads|zlib)$/;
|
|
327
|
+
async function loadSolid() {
|
|
328
|
+
try {
|
|
329
|
+
const mod = await import("vite-plugin-solid");
|
|
330
|
+
return mod.default ?? mod;
|
|
331
|
+
} catch {
|
|
332
|
+
throw new Error(
|
|
333
|
+
"[fnetro] vite-plugin-solid is required.\n Install it: npm i -D vite-plugin-solid"
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function toPlugins(v) {
|
|
338
|
+
return Array.isArray(v) ? v : [v];
|
|
339
|
+
}
|
|
624
340
|
function fnetroVitePlugin(opts = {}) {
|
|
625
341
|
const {
|
|
626
|
-
serverEntry = "
|
|
627
|
-
clientEntry = "
|
|
342
|
+
serverEntry = "server.ts",
|
|
343
|
+
clientEntry = "client.ts",
|
|
628
344
|
serverOutDir = "dist/server",
|
|
629
345
|
clientOutDir = "dist/assets",
|
|
630
|
-
serverExternal = []
|
|
346
|
+
serverExternal = [],
|
|
347
|
+
solidOptions = {}
|
|
631
348
|
} = opts;
|
|
632
|
-
let
|
|
633
|
-
|
|
634
|
-
jsx: "automatic",
|
|
635
|
-
jsxImportSource: "hono/jsx"
|
|
636
|
-
};
|
|
349
|
+
let _solid = null;
|
|
350
|
+
let _solidPlugins = [];
|
|
637
351
|
const jsxPlugin = {
|
|
638
352
|
name: "fnetro:jsx",
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
importSource: "hono/jsx"
|
|
353
|
+
enforce: "pre",
|
|
354
|
+
// Sync config hook — must return Omit<UserConfig, 'plugins'> | null
|
|
355
|
+
config(_cfg, _env) {
|
|
356
|
+
return {
|
|
357
|
+
esbuild: {
|
|
358
|
+
jsx: "automatic",
|
|
359
|
+
jsxImportSource: "solid-js"
|
|
647
360
|
}
|
|
361
|
+
};
|
|
362
|
+
},
|
|
363
|
+
async buildStart() {
|
|
364
|
+
if (!_solid) {
|
|
365
|
+
_solid = await loadSolid();
|
|
366
|
+
_solidPlugins = toPlugins(_solid({ ssr: true, ...solidOptions }));
|
|
648
367
|
}
|
|
649
|
-
|
|
650
|
-
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
const solidProxy = {
|
|
371
|
+
name: "fnetro:solid-proxy",
|
|
372
|
+
enforce: "pre",
|
|
373
|
+
async transform(code, id, options) {
|
|
374
|
+
if (!_solidPlugins[0]?.transform) return null;
|
|
375
|
+
const hook = _solidPlugins[0].transform;
|
|
376
|
+
const fn = typeof hook === "function" ? hook : hook.handler;
|
|
377
|
+
if (!fn) return null;
|
|
378
|
+
return fn.call(this, code, id, options);
|
|
379
|
+
},
|
|
380
|
+
async resolveId(id) {
|
|
381
|
+
if (!_solidPlugins[0]?.resolveId) return null;
|
|
382
|
+
const hook = _solidPlugins[0].resolveId;
|
|
383
|
+
const fn = typeof hook === "function" ? hook : hook.handler;
|
|
384
|
+
if (!fn) return null;
|
|
385
|
+
return fn.call(this, id, void 0, {});
|
|
386
|
+
},
|
|
387
|
+
async load(id) {
|
|
388
|
+
if (!_solidPlugins[0]?.load) return null;
|
|
389
|
+
const hook = _solidPlugins[0].load;
|
|
390
|
+
const fn = typeof hook === "function" ? hook : hook.handler;
|
|
391
|
+
if (!fn) return null;
|
|
392
|
+
return fn.call(this, id, {});
|
|
393
|
+
}
|
|
651
394
|
};
|
|
652
|
-
const
|
|
653
|
-
name: "fnetro:
|
|
395
|
+
const buildPlugin = {
|
|
396
|
+
name: "fnetro:build",
|
|
654
397
|
apply: "build",
|
|
655
398
|
enforce: "pre",
|
|
656
|
-
config
|
|
399
|
+
// Sync config hook — Omit<UserConfig, 'plugins'> satisfies the ObjectHook constraint
|
|
400
|
+
config(_cfg, _env) {
|
|
657
401
|
return {
|
|
658
402
|
build: {
|
|
403
|
+
ssr: serverEntry,
|
|
659
404
|
outDir: serverOutDir,
|
|
660
|
-
ssr: true,
|
|
661
|
-
target: "node18",
|
|
662
|
-
lib: {
|
|
663
|
-
entry: serverEntry,
|
|
664
|
-
formats: ["es"],
|
|
665
|
-
fileName: "server"
|
|
666
|
-
},
|
|
667
405
|
rollupOptions: {
|
|
668
|
-
|
|
406
|
+
input: serverEntry,
|
|
407
|
+
output: {
|
|
408
|
+
format: "es",
|
|
409
|
+
entryFileNames: "server.js"
|
|
410
|
+
},
|
|
411
|
+
external: (id) => NODE_BUILTINS.test(id) || id === "@hono/node-server" || id === "@hono/node-server/serve-static" || serverExternal.includes(id)
|
|
669
412
|
}
|
|
670
|
-
}
|
|
671
|
-
// Vite ≤7 fallback (oxc is set by jsxPlugin for Vite 8+)
|
|
672
|
-
esbuild: jsxTransform
|
|
413
|
+
}
|
|
673
414
|
};
|
|
674
415
|
},
|
|
675
416
|
async closeBundle() {
|
|
676
417
|
console.log("\n\u26A1 FNetro: building client bundle\u2026\n");
|
|
418
|
+
const solid = _solid ?? await loadSolid();
|
|
677
419
|
const { build } = await import("vite");
|
|
678
420
|
await build({
|
|
679
421
|
configFile: false,
|
|
680
|
-
|
|
681
|
-
esbuild: jsxTransform,
|
|
422
|
+
plugins: toPlugins(solid({ ...solidOptions })),
|
|
682
423
|
build: {
|
|
683
424
|
outDir: clientOutDir,
|
|
684
|
-
|
|
685
|
-
entry: clientEntry,
|
|
686
|
-
formats: ["es"],
|
|
687
|
-
fileName: "client"
|
|
688
|
-
},
|
|
425
|
+
manifest: true,
|
|
689
426
|
rollupOptions: {
|
|
690
|
-
|
|
427
|
+
input: clientEntry,
|
|
428
|
+
output: {
|
|
429
|
+
format: "es",
|
|
430
|
+
entryFileNames: "[name]-[hash].js",
|
|
431
|
+
chunkFileNames: "[name]-[hash].js",
|
|
432
|
+
assetFileNames: "[name]-[hash][extname]"
|
|
433
|
+
}
|
|
691
434
|
}
|
|
692
435
|
}
|
|
693
436
|
});
|
|
694
|
-
console.log("\
|
|
437
|
+
console.log("\u2705 FNetro: both bundles ready\n");
|
|
695
438
|
}
|
|
696
439
|
};
|
|
697
|
-
return [jsxPlugin,
|
|
440
|
+
return [jsxPlugin, solidProxy, buildPlugin];
|
|
698
441
|
}
|
|
699
442
|
export {
|
|
443
|
+
PARAMS_KEY,
|
|
444
|
+
SEO_KEY,
|
|
700
445
|
SPA_HEADER,
|
|
701
446
|
STATE_KEY,
|
|
702
|
-
|
|
447
|
+
compilePath,
|
|
703
448
|
createFNetro,
|
|
704
449
|
defineApiRoute,
|
|
705
450
|
defineGroup,
|
|
706
451
|
defineLayout,
|
|
707
|
-
defineMiddleware,
|
|
708
452
|
definePage,
|
|
709
453
|
detectRuntime,
|
|
710
|
-
effect,
|
|
711
|
-
effectScope,
|
|
712
454
|
fnetroVitePlugin,
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
markRaw,
|
|
717
|
-
reactive,
|
|
718
|
-
readonly,
|
|
719
|
-
ref,
|
|
720
|
-
serve,
|
|
721
|
-
shallowReactive,
|
|
722
|
-
shallowRef,
|
|
723
|
-
toRaw,
|
|
724
|
-
toRef,
|
|
725
|
-
toRefs,
|
|
726
|
-
triggerRef,
|
|
727
|
-
unref,
|
|
728
|
-
use,
|
|
729
|
-
useLocalReactive,
|
|
730
|
-
useLocalRef,
|
|
731
|
-
watch,
|
|
732
|
-
watchEffect
|
|
455
|
+
matchPath,
|
|
456
|
+
resolveRoutes,
|
|
457
|
+
serve
|
|
733
458
|
};
|