revojs 0.0.1 → 0.0.3

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/index.js CHANGED
@@ -1,107 +1,249 @@
1
- import defu, { defu as defu$1 } from "defu";
1
+ import { defu } from "defu";
2
2
  import { h } from "revojs/jsx-runtime";
3
3
 
4
4
  //#region src/app/index.ts
5
- const getAssets = async (regex) => {
6
- const assets = await import("#virtual/assets").then((module) => module.assets);
7
- return Object.entries(assets).reduce((assets$1, [name, value]) => {
8
- if (regex.test(name)) assets$1[name] = value;
9
- return assets$1;
10
- }, {});
5
+ const defineContent = (key) => {
6
+ return Object.assign(String(key), { key });
7
+ };
8
+ const getContent = async (key) => {
9
+ return await import("#virtual/content").then((module) => module.index[key.toString()]);
11
10
  };
12
11
  const createApp = (config) => {
13
12
  return {
14
- config: defu$1(config, {
15
- assets: [{ path: "./assets" }],
16
- routes: [{ path: "./routes" }],
13
+ config: defu(config, {
17
14
  client: { entry: "./index.html" },
18
15
  server: { entry: "revojs/presets/node" },
19
- markdown: {}
16
+ content: {
17
+ [STATIC_CONTENT]: {
18
+ type: "static",
19
+ source: "./dist/client",
20
+ include: ["**/*.*"]
21
+ },
22
+ [PAGES_CONTENT]: {
23
+ type: "asset",
24
+ source: "./routes",
25
+ include: [
26
+ "**/*.js",
27
+ "**/*.ts",
28
+ "**/*.jsx",
29
+ "**/*.tsx",
30
+ "!**/*.*.js",
31
+ "!**/*.*.ts",
32
+ "!**/*.*.jsx",
33
+ "!**/*.*.tsx"
34
+ ]
35
+ },
36
+ [ROUTES_CONTENT]: {
37
+ type: "asset",
38
+ source: "./routes",
39
+ include: [
40
+ "**/*.*.js",
41
+ "**/*.*.ts",
42
+ "**/*.*.jsx",
43
+ "**/*.*.tsx"
44
+ ]
45
+ }
46
+ },
47
+ markdown: {},
48
+ dev: { middleware: [] }
20
49
  }),
21
50
  virtuals: {}
22
51
  };
23
52
  };
53
+ const STATIC_CONTENT = defineContent("STATIC");
54
+ const PAGES_CONTENT = defineContent("PAGES");
55
+ const ROUTES_CONTENT = defineContent("ROUTES");
24
56
 
25
57
  //#endregion
26
- //#region src/event/index.ts
27
- const createEvent = (request, context) => {
28
- return {
29
- request,
30
- response: { headers: new Headers() },
31
- context
32
- };
33
- };
34
- const sendText = (event, text) => {
35
- return new Response(text, event.response);
36
- };
37
- const sendHtml = (event, text) => {
38
- setHeader(event, "Content-Type", "text/html");
39
- return new Response(text, event.response);
40
- };
41
- const sendJson = (event, value) => {
42
- return new Response(JSON.stringify(value), event.response);
43
- };
44
- const setHeader = (event, name, value) => {
45
- event.response.headers.set(name, value);
58
+ //#region src/jsx/index.ts
59
+ const svgElements = new Set([
60
+ "altGlyph",
61
+ "altGlyphDef",
62
+ "altGlyphItem",
63
+ "animate",
64
+ "animateColor",
65
+ "animateMotion",
66
+ "animateTransform",
67
+ "circle",
68
+ "clipPath",
69
+ "color-profile",
70
+ "cursor",
71
+ "defs",
72
+ "desc",
73
+ "ellipse",
74
+ "feBlend",
75
+ "feColorMatrix",
76
+ "feComponentTransfer",
77
+ "feComposite",
78
+ "feConvolveMatrix",
79
+ "feDiffuseLighting",
80
+ "feDisplacementMap",
81
+ "feDistantLight",
82
+ "feDropShadow",
83
+ "feFlood",
84
+ "feFuncA",
85
+ "feFuncB",
86
+ "feFuncG",
87
+ "feFuncR",
88
+ "feGaussianBlur",
89
+ "feImage",
90
+ "feMerge",
91
+ "feMergeNode",
92
+ "feMorphology",
93
+ "feOffset",
94
+ "fePointLight",
95
+ "feSpecularLighting",
96
+ "feSpotLight",
97
+ "feTile",
98
+ "feTurbulence",
99
+ "filter",
100
+ "font",
101
+ "font-face",
102
+ "font-face-format",
103
+ "font-face-name",
104
+ "font-face-src",
105
+ "font-face-uri",
106
+ "foreignObject",
107
+ "g",
108
+ "glyph",
109
+ "glyphRef",
110
+ "hkern",
111
+ "image",
112
+ "line",
113
+ "linearGradient",
114
+ "marker",
115
+ "mask",
116
+ "metadata",
117
+ "missing-glyph",
118
+ "mpath",
119
+ "path",
120
+ "pattern",
121
+ "polygon",
122
+ "polyline",
123
+ "radialGradient",
124
+ "rect",
125
+ "set",
126
+ "stop",
127
+ "svg",
128
+ "switch",
129
+ "symbol",
130
+ "text",
131
+ "textPath",
132
+ "tref",
133
+ "tspan",
134
+ "use",
135
+ "view",
136
+ "vkern"
137
+ ]);
138
+ const namespace = (tag) => {
139
+ return svgElements.has(tag) ? "http://www.w3.org/2000/svg" : "http://www.w3.org/1999/xhtml";
46
140
  };
47
141
 
48
142
  //#endregion
49
143
  //#region src/signals/index.ts
144
+ var Scope = class {
145
+ dispose;
146
+ constructor() {
147
+ this.dispose = new Array();
148
+ }
149
+ run(invoke) {
150
+ let previous = activeScope;
151
+ activeScope = this;
152
+ try {
153
+ return invoke();
154
+ } finally {
155
+ activeScope = previous;
156
+ }
157
+ }
158
+ stop() {
159
+ while (this.dispose.length) this.dispose.pop()?.(this);
160
+ }
161
+ };
162
+ var Compute = class {
163
+ invoke;
164
+ scope;
165
+ constructor(invoke) {
166
+ this.invoke = invoke;
167
+ this.scope = new Scope();
168
+ }
169
+ run() {
170
+ this.scope.stop();
171
+ return this.scope.run(this.invoke);
172
+ }
173
+ };
50
174
  var Handler = class Handler {
51
- get = (target, path) => {
52
- if (runningCompute) {
53
- const effects = targets.get(target) ?? new Map();
54
- const set = effects.get(path) ?? new Set();
55
- effects.set(path, set.add(runningCompute));
56
- targets.set(target, effects);
57
- runningCompute.previous?.cleanUps.push((compute) => set.delete(compute));
175
+ get(target, key) {
176
+ const compute = activeCompute;
177
+ if (compute) {
178
+ const computes = targets.get(target) ?? new Map();
179
+ const set = computes.get(key) ?? new Set();
180
+ computes.set(key, set.add(compute));
181
+ targets.set(target, computes);
182
+ onCleanUp(() => {
183
+ compute.scope.stop();
184
+ set.delete(compute);
185
+ });
58
186
  }
59
- const value = Reflect.get(target, path);
60
- if (value instanceof Object) return new Proxy(value, new Handler());
61
- return value;
62
- };
63
- set = (target, path, value) => {
64
- const result = Reflect.set(target, path, value);
65
- for (const effect of targets.get(target)?.get(path) ?? []) {
66
- while (effect.cleanUps.length) effect.cleanUps.pop()?.(effect);
67
- effect.invoke();
187
+ const value = Reflect.get(target, key);
188
+ if (value) {
189
+ if (typeof value === "function") return value.bind(target);
190
+ if (typeof value === "object") return new Proxy(value, new Handler());
68
191
  }
192
+ return value;
193
+ }
194
+ set(target, key, value) {
195
+ const result = Reflect.set(target, key, value);
196
+ for (const compute of targets.get(target)?.get(key) ?? []) compute.run();
69
197
  return result;
70
- };
198
+ }
71
199
  };
72
- const createState = (value) => {
200
+ function createState(value) {
73
201
  return new Proxy({ value }, new Handler());
74
- };
75
- const createCompute = (invoke) => {
76
- return runCompute({
77
- invoke,
78
- cleanUps: []
79
- });
80
- };
81
- const runCompute = (compute) => {
82
- compute.previous = runningCompute;
83
- runningCompute = compute;
202
+ }
203
+ function createCompute(invoke) {
204
+ let previous = activeCompute;
205
+ activeCompute = new Compute(invoke);
84
206
  try {
85
- return compute.invoke();
207
+ return invoke();
86
208
  } finally {
87
- runningCompute = compute.previous;
209
+ activeCompute = previous;
88
210
  }
89
- };
90
- const fromValue = (value) => {
211
+ }
212
+ function createMemo(invoke) {
213
+ let state;
214
+ const compute = createCompute(() => {
215
+ const value = invoke();
216
+ if (typeof state === "object") state.value = value;
217
+ return value;
218
+ });
219
+ state = createState(compute);
220
+ return state;
221
+ }
222
+ function onCleanUp(invoke) {
223
+ activeScope?.dispose.push(invoke);
224
+ }
225
+ function fromValue(value) {
91
226
  if (value) {
92
227
  if (value instanceof Function) return fromValue(value());
93
228
  if (value instanceof Object) return value.value;
94
229
  }
95
230
  return value;
96
- };
97
- const onCleanUp = (cleanUp) => {
98
- runningCompute?.cleanUps.push(cleanUp);
99
- };
231
+ }
232
+ let activeScope;
233
+ let activeCompute;
100
234
  const targets = new WeakMap();
101
- let runningCompute;
102
235
 
103
236
  //#endregion
104
237
  //#region src/html/index.ts
238
+ const isTemplate = (value) => {
239
+ return "tag" in value && "attributes" in value && "children" in value;
240
+ };
241
+ const descriptor = (descriptor$1) => {
242
+ return (typeof descriptor$1 === "object" ? descriptor$1.key : descriptor$1).toString();
243
+ };
244
+ const defineContext = (description) => {
245
+ return { key: Symbol(description) };
246
+ };
105
247
  const createElement = (input, attributes, ...children) => {
106
248
  return {
107
249
  tag: typeof input === "function" ? input.$name : input,
@@ -121,59 +263,85 @@ const toString = (slot) => {
121
263
  default: return "";
122
264
  }
123
265
  };
124
- const slotToString = async (slot) => {
125
- if (Array.isArray(slot)) return await Promise.all(slot.map(slotToString)).then((chunks) => chunks.join(" "));
126
- if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return toString(slot);
127
- if (typeof slot === "function") return await slotToString(await slot());
266
+ const toFragment = (nodes) => {
267
+ const fragment = document.createDocumentFragment();
268
+ fragment.replaceChildren(...nodes);
269
+ return fragment;
270
+ };
271
+ const renderToString = async (slot, context) => {
272
+ if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return slot.toString();
273
+ if (typeof slot === "function") return await renderToString(await slot(), context);
128
274
  if (typeof slot === "object") {
129
- if ("tag" in slot && "attributes" in slot && "children" in slot) {
130
- const Constructor = components.get(slot.tag);
131
- const prefix = [slot.tag, ...Object.entries(slot.attributes ?? {}).map(([name, value]) => {
132
- if (!name.startsWith("on")) return `${name}="${toString(value)}"`;
133
- })].filter(Boolean).join(" ");
134
- const children = await slotToString(slot.children);
135
- if (Constructor) {
136
- const component = new Constructor(slot.attributes);
137
- const template = await slotToString(await component.setup());
138
- if (component.shadowRoot) return `<${prefix}> <template shadowRootMode="${component.shadowRoot.mode}"> ${template} </template> ${children} </${slot.tag}>`;
139
- return `<${prefix}> ${template} ${children} </${slot.tag}>`;
275
+ if (Array.isArray(slot)) return await Promise.all(slot.map((slot$1) => renderToString(slot$1, context))).then((chunks) => chunks.join(""));
276
+ if (isTemplate(slot)) {
277
+ const customElement = components.get(slot.tag);
278
+ const prefix = Object.entries(slot.attributes).reduce((chunks, [name, value]) => {
279
+ if (!name.startsWith("on")) chunks.push(`${name}='${toString(value)}'`);
280
+ return chunks;
281
+ }, [slot.tag]).join(" ");
282
+ const children = await renderToString(slot.children, context);
283
+ if (customElement) {
284
+ const element = new customElement(slot.attributes, context);
285
+ const template = await renderToString(await element.setup(), context);
286
+ if (element.shadowRoot) {
287
+ const shadow = await renderToString({
288
+ tag: "template",
289
+ attributes: { shadowRootMode: element.shadowRoot.mode },
290
+ children: [template]
291
+ }, context);
292
+ return `<${prefix}>` + shadow + children + `</${slot.tag}>`;
293
+ }
294
+ return `<${prefix}>` + template + children + `</${slot.tag}>`;
140
295
  }
141
- return `<${prefix}> ${children} </${slot.tag}>`;
296
+ return `<${prefix}>` + children + `</${slot.tag}>`;
142
297
  }
143
- return toString(slot);
298
+ return JSON.stringify(slot);
144
299
  }
145
- return "";
300
+ return "<!---->";
146
301
  };
147
- const slotToNode = async (slot) => {
148
- if (Array.isArray(slot)) {
149
- const fragment = document.createDocumentFragment();
150
- fragment.replaceChildren(...await Promise.all(slot.map(slotToNode)));
151
- return fragment;
152
- }
302
+ const renderToNode = async (slot) => {
153
303
  if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return document.createTextNode(slot.toString());
154
304
  if (typeof slot === "function") {
155
- let oldNode;
156
- return await createCompute(async () => {
157
- const node = await slotToNode(await slot());
158
- oldNode?.parentNode?.replaceChild(node, oldNode);
159
- oldNode = node;
160
- return node;
305
+ let node;
306
+ await createCompute(async () => {
307
+ let input = slot;
308
+ let parentNode = Array.isArray(node) ? node.at(0)?.parentNode : node?.parentNode;
309
+ while (typeof input === "function") input = await input();
310
+ let next = await (Array.isArray(input) ? Promise.all(input.map(renderToNode)) : renderToNode(input));
311
+ if (Array.isArray(next)) if (Array.isArray(node)) {
312
+ if (parentNode) {
313
+ const range = document.createRange();
314
+ const firstNode = node.at(0);
315
+ if (firstNode) range.setStartBefore(firstNode);
316
+ const lastNode = node.at(-1);
317
+ if (lastNode) range.setEndAfter(lastNode);
318
+ range.deleteContents();
319
+ for (const child of next) parentNode?.appendChild(child);
320
+ }
321
+ } else node?.parentNode?.replaceChild(toFragment(next), node);
322
+ else if (Array.isArray(node)) {} else node?.parentNode?.replaceChild(next, node);
323
+ node = next;
161
324
  });
325
+ if (Array.isArray(node)) return toFragment(node);
326
+ return node ?? document.createComment("");
162
327
  }
163
328
  if (typeof slot === "object") {
164
- if ("tag" in slot && "attributes" in slot && "children" in slot) {
165
- const node = document.createElement(slot.tag);
166
- for (const key in slot.attributes) {
167
- const value = slot.attributes[key];
168
- if (value) if (key.startsWith("on")) {
169
- const event = key.substring(2).toLowerCase();
170
- node.addEventListener(event, value);
171
- onCleanUp(() => node.removeEventListener(event, value));
172
- } else node.setAttribute(key, toString(value));
329
+ if (Array.isArray(slot)) return toFragment(await Promise.all(slot.map(renderToNode)));
330
+ if (isTemplate(slot)) {
331
+ const element = document.createElementNS(namespace(slot.tag), slot.tag);
332
+ for (const name in slot.attributes) {
333
+ const value = slot.attributes[name];
334
+ if (value) if (name.startsWith("on") && typeof value === "function") {
335
+ const event = name.substring(2).toLowerCase();
336
+ const controller = new AbortController();
337
+ element.addEventListener(event, value, { signal: controller.signal });
338
+ onCleanUp(() => controller.abort());
339
+ } else createCompute(() => element.setAttribute(name, toString(value)));
173
340
  }
174
- node.replaceChildren(await slotToNode(slot.children));
175
- return node;
341
+ element.replaceChildren(await renderToNode(slot.children));
342
+ return element;
176
343
  }
344
+ return document.createTextNode(JSON.stringify(slot));
177
345
  }
178
346
  return document.createComment("");
179
347
  };
@@ -185,7 +353,8 @@ const defineComponent = (options) => {
185
353
  events;
186
354
  attributes;
187
355
  shadowRoot;
188
- constructor(input) {
356
+ context;
357
+ constructor(input, context) {
189
358
  this.events = Object.keys(options.events ?? {}).reduce((output, name) => {
190
359
  Reflect.set(output, name, input?.[name], output);
191
360
  return output;
@@ -195,34 +364,48 @@ const defineComponent = (options) => {
195
364
  return output;
196
365
  }, createState({}));
197
366
  this.shadowRoot = options.shadowRoot ?? { mode: "open" };
367
+ this.context = context ?? {};
198
368
  }
369
+ getContext = (input) => {
370
+ return this.context[descriptor(input)];
371
+ };
372
+ setContext = (input, value) => {
373
+ this.context[descriptor(input)] = value;
374
+ };
199
375
  setup = () => options.setup(this);
200
376
  });
201
377
  };
202
378
  const toCustomElement = (component) => {
203
379
  return class extends HTMLElement {
380
+ static formAssociated = true;
381
+ scope;
204
382
  component;
205
383
  constructor() {
206
384
  super();
385
+ this.scope = new Scope();
207
386
  this.component = new component();
208
387
  }
209
388
  async connectedCallback() {
210
389
  let previous = activeElement;
390
+ const parentNode = getCustomElement(this.parentNode);
211
391
  activeElement = this;
212
392
  try {
213
393
  const shadow = this.component.shadowRoot ? this.attachShadow(this.component.shadowRoot) : this;
214
394
  for (const [name, event] of Object.entries(component.$events)) Reflect.set(this.component.events, name, (value) => {
395
+ if (value instanceof Event) return;
215
396
  this.dispatchEvent(new CustomEvent(name.substring(2).toLowerCase(), {
216
397
  ...event,
217
398
  detail: value
218
399
  }));
219
400
  }, this.component.events);
220
- shadow.replaceChildren(await slotToNode(await this.component.setup()));
401
+ if (parentNode) for (const key in parentNode.component.context) this.component.setContext(key, parentNode.component.context[key]);
402
+ await this.scope.run(async () => shadow.replaceChildren(await renderToNode(await this.component.setup())));
221
403
  } finally {
222
404
  activeElement = previous;
223
405
  }
224
406
  }
225
- attributeChangedCallback(name, _, value) {
407
+ attributeChangedCallback(name, oldValue, value) {
408
+ if (value === oldValue) return;
226
409
  const attribute = component.$attributes?.[name];
227
410
  if (attribute) {
228
411
  let convertedValue;
@@ -243,6 +426,9 @@ const toCustomElement = (component) => {
243
426
  Reflect.set(this.component.attributes.value, name, convertedValue ?? attribute.default, this.component.attributes.value);
244
427
  }
245
428
  }
429
+ disconnectedCallback() {
430
+ this.scope.stop();
431
+ }
246
432
  static get observedAttributes() {
247
433
  return Object.keys(component.$attributes ?? {});
248
434
  }
@@ -270,21 +456,121 @@ const getGlobalStyles = () => {
270
456
  return sheet;
271
457
  });
272
458
  };
273
- let globalStyles;
459
+ const getCustomElement = (node) => {
460
+ if (node) {
461
+ if ("component" in node) return node;
462
+ return getCustomElement(node.parentNode);
463
+ }
464
+ };
465
+ const preventDefault = (event) => {
466
+ event.preventDefault();
467
+ };
468
+ const stopPropagation = (event) => {
469
+ event.stopPropagation();
470
+ };
471
+ const stopImmediatePropagation = (event) => {
472
+ event.stopImmediatePropagation();
473
+ };
274
474
  let activeElement;
275
475
  const components = new Map();
276
476
 
477
+ //#endregion
478
+ //#region src/hooks/index.ts
479
+ var Hooks = class {
480
+ hooks;
481
+ constructor() {
482
+ this.hooks = new Map();
483
+ }
484
+ on = (input, invoke) => {
485
+ const key = descriptor(input);
486
+ const invokes = this.hooks.get(key) ?? new Set();
487
+ invokes.add(invoke);
488
+ this.hooks.set(key, invokes);
489
+ return onCleanUp(() => invokes.delete(invoke));
490
+ };
491
+ dispatch = (input, value) => {
492
+ const invokes = this.hooks.get(descriptor(input));
493
+ if (invokes) for (const invoke of invokes) invoke(value);
494
+ };
495
+ };
496
+ const defineHook = (description) => {
497
+ return { key: Symbol(description) };
498
+ };
499
+
277
500
  //#endregion
278
501
  //#region src/http/index.ts
279
- const mimeTypes = {
280
- css: "text/css",
281
- js: "text/javascript",
282
- txt: "text/plain"
502
+ const createEvent = (request, context) => {
503
+ return {
504
+ request,
505
+ response: { headers: new Headers() },
506
+ context
507
+ };
508
+ };
509
+ const sendText = (event, text) => {
510
+ event.response.headers.set("Content-Type", "text/plain");
511
+ return new Response(text, event.response);
512
+ };
513
+ const sendHtml = (event, text) => {
514
+ event.response.headers.set("Content-Type", "text/html");
515
+ return new Response(text, event.response);
516
+ };
517
+ const sendJson = (event, value) => {
518
+ event.response.headers.set("Content-Type", "application/json");
519
+ return new Response(JSON.stringify(value), event.response);
520
+ };
521
+ const sendRedirect = (event, path) => {
522
+ event.response.status = 302;
523
+ event.response.headers.set("Location", path);
524
+ return new Response(null, event.response);
525
+ };
526
+ const sendBadRequest = (event, text) => {
527
+ event.response.status = 400;
528
+ return new Response(text, event.response);
529
+ };
530
+ const sendUnauthorized = (event) => {
531
+ event.response.status = 401;
532
+ return new Response(null, event.response);
533
+ };
534
+ const getRequestUrl = (event) => {
535
+ return new URL(event instanceof Request ? event.url : event.request.url);
536
+ };
537
+ const getCookies = (event) => {
538
+ const cookies = event.request.headers.get("Cookie")?.split("; ") ?? [];
539
+ return cookies.reduce((result, cookie) => {
540
+ const [name, value] = cookie.split("=");
541
+ if (name && value) result[name] = decodeURIComponent(value);
542
+ return result;
543
+ }, {});
544
+ };
545
+ const getSetCookies = (event) => {
546
+ const cookies = event.request.headers.getSetCookie();
547
+ return cookies.reduce((result, cookie) => {
548
+ const [name, value] = cookie.split("=");
549
+ if (name && value) result[name] = decodeURIComponent(value);
550
+ return result;
551
+ }, {});
552
+ };
553
+ const setCookie = (event, name, value, options) => {
554
+ let cookie = name + "=" + encodeURIComponent(value);
555
+ if (options?.domain) cookie += `; Domain=${options.domain}`;
556
+ if (options?.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
557
+ if (options?.httpOnly) cookie += `; HttpOnly`;
558
+ if (options?.maxAge) cookie += `; Max-Age=${options.maxAge}`;
559
+ if (options?.path) cookie += `; Path=${options.path}`;
560
+ if (options?.priority) cookie += `; Priority=${options.priority}`;
561
+ if (options?.sameSite) cookie += `; SameSite=${options.sameSite}`;
562
+ if (options?.secure) cookie += `; Secure`;
563
+ event.response.headers.append("Set-Cookie", cookie);
283
564
  };
284
565
  const getMimeType = (file) => {
285
566
  const extension = /\.([a-zA-Z0-9]+?)$/.exec(file)?.at(1);
286
567
  return mimeTypes[extension ?? ""] ?? "text/plain";
287
568
  };
569
+ const mimeTypes = {
570
+ css: "text/css",
571
+ js: "text/javascript",
572
+ txt: "text/plain"
573
+ };
288
574
 
289
575
  //#endregion
290
576
  //#region src/markdown/index.ts
@@ -424,44 +710,79 @@ var Radix = class Radix {
424
710
  const defineRoute = (route) => {
425
711
  return route;
426
712
  };
427
- const defineSocket = (socket) => {
428
- return socket;
713
+ const fileName = (path) => {
714
+ return path.split("/").pop()?.split(".").slice(0, -1).join(".");
429
715
  };
430
716
  const toPath = (value) => {
431
- return value.replace(/\/index/, "").replace(/\.(js|ts|jsx|tsx)$/, "").replaceAll(/\[(.*)\]/g, (_, name) => ":" + name).padStart(1, "/");
717
+ const path = (value.startsWith("/") ? value : "/" + value).replaceAll(/\/index/g, "").replaceAll(/\[(.*)\]/g, (_, name) => ":" + name);
718
+ return (path.startsWith("/") ? path : "/" + path).split(".");
719
+ };
720
+ const $fetch = async (input, init) => {
721
+ let response;
722
+ if (activeEvent) {
723
+ const url = new URL(input.toString(), activeEvent.request.url);
724
+ response = await (await import("#virtual/runtime")).runtime.fetch(new Request(url, init), activeEvent.context);
725
+ } else response = await fetch(input, init);
726
+ if (response.ok === false) throw response;
727
+ switch (response.headers.get("Content-Type")) {
728
+ case "application/json": return response.json();
729
+ default: return response;
730
+ }
731
+ };
732
+ const getVariables = (event) => {
733
+ return event ? event.context.variables : import.meta.env;
432
734
  };
433
735
  const createRuntime = async () => {
434
736
  const radix = new Radix();
435
- const assets = await import("#virtual/assets").then((module) => module.assets);
436
- for (const path in assets) radix.insert("GET" + path, defineRoute({ fetch: async (event) => {
437
- setHeader(event, "Content-Type", getMimeType(path));
438
- return sendText(event, await assets[path]());
737
+ const middlewares = new Array();
738
+ const assets = await getContent(STATIC_CONTENT);
739
+ for (const path in assets) radix.insert("GET/" + path, defineRoute({ fetch: async (event) => {
740
+ event.response.headers.set("Content-Type", getMimeType(path));
741
+ return new Response(await assets[path]?.(), event.response);
439
742
  } }));
440
- const routes = await import("#virtual/routes").then((module) => module.routes);
441
- for (const path in routes) radix.insert("GET" + toPath(path), defineRoute({ fetch: async (event) => {
442
- const route = await routes[path]();
443
- if (route) {
444
- if ("message" in route) return sendText(event, "WebSocket");
445
- if ("$name" in route) {
446
- const slot = await import("#virtual/client").then((module) => module.index);
447
- return sendHtml(event, await slotToString(slot));
743
+ const pages = await getContent(PAGES_CONTENT);
744
+ for (const path in pages) {
745
+ const [name] = toPath(path);
746
+ radix.insert("GET" + name, defineRoute({ fetch: async (event) => {
747
+ const slot = await import("#virtual/client").then((module) => module.index);
748
+ return sendHtml(event, await renderToString(slot, {}));
749
+ } }));
750
+ }
751
+ const routes = await getContent(ROUTES_CONTENT);
752
+ for (const path in routes) {
753
+ const [name, method] = toPath(path);
754
+ radix.insert((method ?? "GET").toUpperCase() + name, defineRoute({ fetch: async (event) => {
755
+ const route = await routes[path]?.();
756
+ if (route) return route.fetch(event);
757
+ } }));
758
+ }
759
+ const invoke = (event, next, index) => {
760
+ return middlewares.at(index)?.(event, () => invoke(event, next, index + 1)) ?? next(event);
761
+ };
762
+ return {
763
+ radix,
764
+ middlewares,
765
+ fetch: async (request, context) => {
766
+ const url = getRequestUrl(request);
767
+ const { value: route, inputs } = radix.match(request.method + url.pathname);
768
+ activeEvent = createEvent(request, {
769
+ ...context,
770
+ inputs
771
+ });
772
+ try {
773
+ if (route) {
774
+ const response = await invoke(activeEvent, route.fetch, 0);
775
+ if (response) return response;
776
+ }
777
+ return sendText(activeEvent, "Not found");
778
+ } catch (response) {
779
+ if (response instanceof Response) return response;
780
+ throw response;
781
+ } finally {
782
+ activeEvent = undefined;
448
783
  }
449
- return route.fetch(event);
450
784
  }
451
- } }));
452
- return { fetch: async (request, context) => {
453
- const url = new URL(request.url);
454
- const { value: route, inputs } = radix.match(request.method + url.pathname);
455
- activeEvent = createEvent(request, {
456
- ...context,
457
- inputs
458
- });
459
- try {
460
- return await route?.fetch(activeEvent) ?? sendText(activeEvent, "Not found");
461
- } finally {
462
- activeEvent = undefined;
463
- }
464
- } };
785
+ };
465
786
  };
466
787
  let activeEvent;
467
788
 
@@ -469,10 +790,14 @@ let activeEvent;
469
790
  //#region src/router/index.tsx
470
791
  const Outlet = defineComponent({
471
792
  name: "x-outlet",
793
+ shadowRoot: false,
472
794
  setup: async () => {
473
795
  const radix = new Radix();
474
- const routes = await import("#virtual/routes").then((module) => module.routes);
475
- for (const path in routes) radix.insert("GET" + toPath(path), routes[path]);
796
+ const routes = await getContent(PAGES_CONTENT);
797
+ for (const path in routes) {
798
+ const [name] = toPath(path);
799
+ radix.insert(name, routes[path]);
800
+ }
476
801
  const url = createState(new URL(activeEvent ? activeEvent.request.url : window.location.href));
477
802
  if (isClient()) {
478
803
  const controller = new AbortController();
@@ -480,7 +805,7 @@ const Outlet = defineComponent({
480
805
  onCleanUp(() => controller.abort());
481
806
  }
482
807
  return async () => {
483
- const { value, inputs } = radix.match("GET" + url.value.pathname);
808
+ const { value, inputs } = radix.match(url.value.pathname);
484
809
  const Page = await value?.();
485
810
  if (Page) return h(Page, inputs);
486
811
  };
@@ -499,4 +824,4 @@ const anchorNavigate = (event) => {
499
824
  };
500
825
 
501
826
  //#endregion
502
- export { Handler, Outlet, Radix, activeElement, activeEvent, addStyles, anchorNavigate, components, createApp, createCompute, createElement, createEvent, createRuntime, createState, defineComponent, defineRoute, defineSocket, fromValue, getAssets, getGlobalStyles, getMimeType, globalStyles, isClient, isServer, markdownToSlot, navigate, onCleanUp, registerComponent, runCompute, runningCompute, sendHtml, sendJson, sendText, setHeader, slotToNode, slotToString, targets, toCustomElement, toPath, toString };
827
+ export { $fetch, Compute, Handler, Hooks, Outlet, PAGES_CONTENT, ROUTES_CONTENT, Radix, STATIC_CONTENT, Scope, activeCompute, activeElement, activeEvent, activeScope, addStyles, anchorNavigate, components, createApp, createCompute, createElement, createEvent, createMemo, createRuntime, createState, defineComponent, defineContent, defineContext, defineHook, defineRoute, descriptor, fileName, fromValue, getContent, getCookies, getCustomElement, getGlobalStyles, getMimeType, getRequestUrl, getSetCookies, getVariables, isClient, isServer, isTemplate, markdownToSlot, navigate, onCleanUp, preventDefault, registerComponent, renderToNode, renderToString, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, stopImmediatePropagation, stopPropagation, targets, toCustomElement, toFragment, toPath, toString };