revojs 0.0.24 → 0.0.26

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.
@@ -1,5 +1,5 @@
1
1
  import { type HtmlAttributes } from "../jsx";
2
- import { type Descriptor, Scope, type State, type Value } from "../signals";
2
+ import { Scope, type State, type Value } from "../signals";
3
3
  export type TypeOf<T> = {
4
4
  (): T;
5
5
  };
@@ -45,20 +45,17 @@ export interface ComponentOptions<TEvents extends Events, TAttributes extends At
45
45
  }
46
46
  export interface Component<TEvents extends Events, TAttributes extends Attributes> {
47
47
  readonly scope: Scope;
48
- readonly context: Map<string, unknown>;
49
48
  readonly events: EventOutput<TEvents>;
50
49
  readonly attributes: State<AttributeOutput<TAttributes>>;
51
50
  readonly shadowRoot: false | ShadowRootInit;
52
51
  readonly host?: CustomElement<TEvents, TAttributes>;
53
- getContext: <T>(input: Descriptor<T>) => T;
54
- setContext: <T>(input: Descriptor<T>, value: T) => void;
55
52
  setup: () => Slot | Promise<Slot>;
56
53
  }
57
54
  export interface ComponentConstructor<TEvents extends Events, TAttributes extends Attributes> {
58
55
  $name: string;
59
56
  $events: TEvents;
60
57
  $attributes: TAttributes;
61
- new (input?: Input<TEvents, TAttributes>, context?: Map<string, unknown>, host?: CustomElement<TEvents, TAttributes>): Component<TEvents, TAttributes>;
58
+ new (input?: Input<TEvents, TAttributes>, scope?: Scope, host?: CustomElement<TEvents, TAttributes>): Component<TEvents, TAttributes>;
62
59
  }
63
60
  export interface CustomElement<TEvents extends Events, TAttributes extends Attributes> extends HTMLElement {
64
61
  readonly component: Component<TEvents, TAttributes>;
@@ -67,26 +64,27 @@ export interface CustomElementConstructor<TEvents extends Events, TAttributes ex
67
64
  new (): CustomElement<TEvents, TAttributes>;
68
65
  }
69
66
  export declare class MountedEvent extends Event {
70
- constructor(event?: EventInit);
67
+ constructor();
71
68
  }
72
69
  export declare const isTemplate: (value: object) => value is Template;
73
70
  export declare const createElement: <TEvents extends Events, TAttributes extends Attributes>(input: string | ComponentConstructor<TEvents, TAttributes>, attributes?: AttributeInput<TAttributes>, ...children: Array<Slot>) => Slot;
74
71
  export declare const toString: (slot: Slot) => string;
75
72
  export declare const toFragment: (nodes: Array<Node>) => DocumentFragment;
76
- export declare const renderToString: (slot: Slot, context: Map<string, unknown>) => Promise<string>;
73
+ export declare const renderToString: (scope: Scope, slot: Slot) => Promise<string>;
77
74
  export declare const renderToNode: (scope: Scope, slot: Slot) => Promise<Node>;
78
75
  export declare const defineComponent: <TEvents extends Events = {}, TAttributes extends Attributes = {}>(options: ComponentOptions<TEvents, TAttributes>) => ComponentConstructor<TEvents, TAttributes>;
79
76
  export declare const toCustomElement: <TEvents extends Events = {}, TAttributes extends Attributes = {}>(component: ComponentConstructor<TEvents, TAttributes>) => CustomElementConstructor<TEvents, TAttributes>;
80
77
  export declare const registerComponent: <TEvents extends Events = {}, TAttributes extends Attributes = {}>(component: ComponentConstructor<TEvents, TAttributes>) => ComponentConstructor<TEvents, TAttributes>;
81
78
  export declare const getGlobalStyles: () => CSSStyleSheet[];
82
- export declare const useEvent: (scope: Scope, target: EventTarget | undefined, event: string, input: EventListener<Event>, options?: AddEventListenerOptions) => number;
79
+ export declare function useEvent<T extends keyof ElementEventMap>(scope: Scope, target: EventTarget | undefined, event: T, input: EventListener<ElementEventMap[T]>, options?: AddEventListenerOptions): void;
80
+ export declare function useEvent<T extends keyof WindowEventMap>(scope: Scope, target: Window | undefined, event: T, input: EventListener<WindowEventMap[T]>, options?: AddEventListenerOptions): void;
81
+ export declare function useEvent<T extends keyof HTMLElementEventMap>(scope: Scope, target: HTMLElement | undefined, event: T, input: EventListener<HTMLElementEventMap[T]>, options?: AddEventListenerOptions): void;
83
82
  export declare const getCustomElement: (node: Node | null) => CustomElement<Events, Attributes> | undefined;
84
83
  export declare const isClient: () => boolean;
85
84
  export declare const isServer: () => boolean;
86
85
  export declare const preventDefault: (event: Event) => void;
87
86
  export declare const stopPropagation: (event: Event) => void;
88
87
  export declare const stopImmediatePropagation: (event: Event) => void;
89
- export declare const MOUNTED_HOOK: Descriptor<HTMLElement>;
90
88
  export declare const components: Map<string, ComponentConstructor<Events, Attributes>>;
91
89
  declare global {
92
90
  interface HTMLElementEventMap {
package/dist/index.js CHANGED
@@ -107,29 +107,37 @@ const namespace = (tag) => {
107
107
 
108
108
  //#endregion
109
109
  //#region src/signals/index.ts
110
- var Hooks = class {
111
- hooks;
110
+ var StopEvent = class extends Event {
112
111
  constructor() {
113
- this.hooks = new Map();
112
+ super("stop");
114
113
  }
115
- on(scope, input, invoke) {
116
- const invokes = this.hooks.get(input) ?? new Set();
117
- invokes.add(invoke);
118
- this.hooks.set(input, invokes);
119
- return scope.dispose.push(() => invokes.delete(invoke));
114
+ };
115
+ var Scope = class extends EventTarget {
116
+ parentScope;
117
+ context;
118
+ constructor(scope) {
119
+ super();
120
+ this.parentScope = scope;
121
+ this.context = new Map();
120
122
  }
121
- dispatch(input, value) {
122
- const invokes = this.hooks.get(input);
123
- if (invokes) for (const invoke of invokes) invoke(value);
123
+ getContext(input) {
124
+ let scope = this;
125
+ const seen = new Set();
126
+ while (scope && !seen.has(scope)) {
127
+ seen.add(scope);
128
+ if (scope.context.has(input)) return scope.context.get(input);
129
+ scope = scope.parentScope;
130
+ }
131
+ return {};
124
132
  }
125
- };
126
- var Scope = class {
127
- dispose;
128
- constructor() {
129
- this.dispose = new Array();
133
+ setContext(input, value) {
134
+ this.context.set(input, value);
135
+ }
136
+ onStop(input) {
137
+ this.addEventListener("stop", input, { once: true });
130
138
  }
131
139
  stop() {
132
- while (this.dispose.length) this.dispose.pop()?.(this);
140
+ return this.dispatchEvent(new StopEvent());
133
141
  }
134
142
  };
135
143
  var Compute = class extends Scope {
@@ -153,7 +161,7 @@ var Handler = class Handler {
153
161
  const set = computes.get(key) ?? new Set();
154
162
  computes.set(key, set.add(compute));
155
163
  targets.set(target, computes);
156
- compute.scope.dispose.push(() => {
164
+ compute.scope.onStop(() => {
157
165
  compute.stop();
158
166
  set.delete(compute);
159
167
  });
@@ -197,9 +205,6 @@ function fromValue(value) {
197
205
  if (value instanceof Function) return fromValue(value());
198
206
  return value;
199
207
  }
200
- function defineHook(key) {
201
- return key;
202
- }
203
208
  function defineContext(key) {
204
209
  return key;
205
210
  }
@@ -209,8 +214,8 @@ const targets = new WeakMap();
209
214
  //#endregion
210
215
  //#region src/html/index.ts
211
216
  var MountedEvent = class extends Event {
212
- constructor(event) {
213
- super("mounted", event);
217
+ constructor() {
218
+ super("mounted");
214
219
  }
215
220
  };
216
221
  const isTemplate = (value) => {
@@ -240,28 +245,28 @@ const toFragment = (nodes) => {
240
245
  fragment.replaceChildren(...nodes);
241
246
  return fragment;
242
247
  };
243
- const renderToString = async (slot, context) => {
248
+ const renderToString = async (scope, slot) => {
244
249
  if (slot) {
245
250
  if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return slot.toString();
246
- if (typeof slot === "function") return await renderToString(await slot(), context);
251
+ if (typeof slot === "function") return await renderToString(scope, await slot());
247
252
  if (typeof slot === "object") {
248
- if (Array.isArray(slot)) return await Promise.all(slot.map((slot$1) => renderToString(slot$1, context))).then((chunks) => chunks.join(""));
253
+ if (Array.isArray(slot)) return await Promise.all(slot.map((slot$1) => renderToString(scope, slot$1))).then((chunks) => chunks.join(""));
249
254
  if (isTemplate(slot)) {
250
255
  const customElement = components.get(slot.tag);
251
256
  const prefix = Object.entries(slot.attributes).reduce((chunks, [name, value]) => {
252
257
  if (!name.startsWith("on")) chunks.push(`${name}='${toString(value)}'`);
253
258
  return chunks;
254
259
  }, [slot.tag]).join(" ");
255
- const children = await renderToString(slot.children, context);
260
+ const children = await renderToString(scope, slot.children);
256
261
  if (customElement) {
257
- const element = new customElement(slot.attributes, context);
258
- const template = await renderToString(await element.setup(), context);
262
+ const element = new customElement(slot.attributes, scope);
263
+ const template = await renderToString(scope, await element.setup());
259
264
  if (element.shadowRoot) {
260
- const shadow = await renderToString({
265
+ const shadow = await renderToString(scope, {
261
266
  tag: "template",
262
267
  attributes: { shadowRootMode: element.shadowRoot.mode },
263
268
  children: [template]
264
- }, context);
269
+ });
265
270
  return `<${prefix}>` + shadow + children + `</${slot.tag}>`;
266
271
  }
267
272
  return `<${prefix}>` + template + children + `</${slot.tag}>`;
@@ -325,8 +330,10 @@ const renderToNode = async (scope, slot) => {
325
330
  const element = document.createElementNS(namespace(slot.tag), slot.tag);
326
331
  for (const name in slot.attributes) {
327
332
  const value = slot.attributes[name];
328
- if (name.startsWith("on")) useEvent(scope, element, name.substring(2).toLowerCase(), value);
329
- else createCompute(scope, () => {
333
+ if (name.startsWith("on")) {
334
+ const event = name.substring(2).toLowerCase();
335
+ useEvent(scope, element, event, value);
336
+ } else createCompute(scope, () => {
330
337
  const set = toString(value);
331
338
  if (set === "" || set === "false") return element.removeAttribute(name);
332
339
  return element.setAttribute(name, set);
@@ -346,14 +353,12 @@ const defineComponent = (options) => {
346
353
  static $events = options.events ?? {};
347
354
  static $attributes = options.attributes ?? {};
348
355
  scope;
349
- context;
350
356
  events;
351
357
  attributes;
352
358
  shadowRoot;
353
359
  host;
354
- constructor(input, context, host) {
355
- this.scope = new Scope();
356
- this.context = context ?? new Map();
360
+ constructor(input, scope, host) {
361
+ this.scope = new Scope(scope);
357
362
  this.events = Object.keys(options.events ?? {}).reduce((output, name) => {
358
363
  Reflect.set(output, name, input?.[name], output);
359
364
  return output;
@@ -365,13 +370,17 @@ const defineComponent = (options) => {
365
370
  this.shadowRoot = options.shadowRoot ?? { mode: "open" };
366
371
  this.host = host;
367
372
  }
368
- getContext = (input) => {
369
- return this.context.get(input) ?? {};
370
- };
371
- setContext = (input, value) => {
372
- this.context.set(input, value);
373
+ setup = () => {
374
+ const findParent = (node) => {
375
+ if (node) {
376
+ if ("component" in node) return node;
377
+ return findParent(node.parentNode);
378
+ }
379
+ };
380
+ const parentNode = findParent(this.host);
381
+ if (parentNode) this.scope.parentScope = parentNode.component.scope;
382
+ return options.setup(this);
373
383
  };
374
- setup = () => options.setup(this);
375
384
  });
376
385
  };
377
386
  const toCustomElement = (component) => {
@@ -384,7 +393,6 @@ const toCustomElement = (component) => {
384
393
  }
385
394
  async connectedCallback() {
386
395
  const rootNode = this.component.shadowRoot ? this.attachShadow(this.component.shadowRoot) : this;
387
- const parentNode = getCustomElement(this.parentNode);
388
396
  for (const [name, event] of Object.entries(component.$events)) Reflect.set(this.component.events, name, (value) => {
389
397
  if (value instanceof Event) return;
390
398
  this.dispatchEvent(new CustomEvent(name.substring(2).toLowerCase(), {
@@ -392,7 +400,6 @@ const toCustomElement = (component) => {
392
400
  detail: value
393
401
  }));
394
402
  }, this.component.events);
395
- if (parentNode) for (const [name, value] of parentNode.component.context) this.component.context.set(name, value);
396
403
  rootNode.replaceChildren(await renderToNode(this.component.scope, await this.component.setup()));
397
404
  this.dispatchEvent(new MountedEvent());
398
405
  }
@@ -442,7 +449,7 @@ const getGlobalStyles = () => {
442
449
  return sheet;
443
450
  });
444
451
  };
445
- const useEvent = (scope, target, event, input, options) => {
452
+ function useEvent(scope, target, event, input, options) {
446
453
  const controller = new AbortController();
447
454
  target?.addEventListener(event, (event$1) => {
448
455
  if (Array.isArray(input)) for (const target$1 of input) target$1?.(event$1);
@@ -451,8 +458,8 @@ const useEvent = (scope, target, event, input, options) => {
451
458
  signal: controller.signal,
452
459
  ...options
453
460
  });
454
- return scope.dispose.push(() => controller.abort());
455
- };
461
+ scope.onStop(() => controller.abort());
462
+ }
456
463
  const getCustomElement = (node) => {
457
464
  if (node) {
458
465
  if ("component" in node) return node;
@@ -464,7 +471,6 @@ const isServer = () => typeof window === "undefined";
464
471
  const preventDefault = (event) => event.preventDefault();
465
472
  const stopPropagation = (event) => event.stopPropagation();
466
473
  const stopImmediatePropagation = (event) => event.stopImmediatePropagation();
467
- const MOUNTED_HOOK = defineHook("MOUNTED_HOOK");
468
474
  const components = new Map();
469
475
 
470
476
  //#endregion
@@ -689,7 +695,8 @@ const toPath = (value) => {
689
695
  const split = route.split(".");
690
696
  return split.length === 3 ? [split.at(0), split.at(1)] : [split.at(0)];
691
697
  };
692
- const $fetch = async (event, input, init) => {
698
+ const $fetch = async (scope, input, init) => {
699
+ const { event } = scope.getContext(RUNTIME_CONTEXT);
693
700
  let response;
694
701
  if (event) {
695
702
  const url = new URL(input.toString(), event.request.url);
@@ -714,8 +721,9 @@ const createRuntime = async () => {
714
721
  const route = await routes[path]?.();
715
722
  if (typeof route === "object") return route.fetch(event);
716
723
  if (route) {
717
- const slot = await import("#virtual/client").then((module) => module.index);
718
- return sendHtml(event, await renderToString(slot, new Map().set(RUNTIME_CONTEXT, { event })));
724
+ const scope = new Scope();
725
+ scope.setContext(RUNTIME_CONTEXT, { event });
726
+ return sendHtml(event, await renderToString(scope, await import("#virtual/client").then((module) => module.index)));
719
727
  }
720
728
  } }));
721
729
  }
@@ -757,8 +765,8 @@ const RUNTIME_CONTEXT = defineContext("RUNTIME_CONTEXT");
757
765
  const Outlet = defineComponent({
758
766
  name: "x-outlet",
759
767
  shadowRoot: false,
760
- setup: async ({ scope, getContext }) => {
761
- const { event } = getContext(RUNTIME_CONTEXT);
768
+ setup: async ({ scope }) => {
769
+ const { event } = scope.getContext(RUNTIME_CONTEXT);
762
770
  const radix = new Radix();
763
771
  const routes = await getRoutes();
764
772
  for (const path in routes) {
@@ -766,11 +774,7 @@ const Outlet = defineComponent({
766
774
  if (name) radix.insert(name, routes[path]);
767
775
  }
768
776
  const url = createState(new URL(event ? event.request.url : window.location.href));
769
- if (isClient()) {
770
- const controller = new AbortController();
771
- window.addEventListener("popstate", () => url.value = new URL(window.location.href), { signal: controller.signal });
772
- scope.dispose.push(() => controller.abort());
773
- }
777
+ if (isClient()) useEvent(scope, window, "popstate", () => url.value = new URL(window.location.href));
774
778
  return async () => {
775
779
  const { value, inputs } = radix.match(url.value.pathname);
776
780
  const Page = await value?.();
@@ -791,4 +795,4 @@ const anchorNavigate = (event) => {
791
795
  };
792
796
 
793
797
  //#endregion
794
- export { $fetch, Compute, Handler, Hooks, MOUNTED_HOOK, MountedEvent, Outlet, RUNTIME_CONTEXT, Radix, Scope, activeCompute, anchorNavigate, components, createApp, createCompute, createElement, createEvent, createMemo, createRuntime, createState, defineComponent, defineContext, defineHook, defineRoute, fileName, fromValue, getAssets, getCookies, getCustomElement, getGlobalStyles, getMimeType, getRequestUrl, getRoutes, getSetCookies, getVariables, isClient, isServer, isTemplate, markdownToSlot, navigate, preventDefault, registerComponent, renderToNode, renderToString, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, stopImmediatePropagation, stopPropagation, targets, toCustomElement, toFragment, toPath, toString, useEvent };
798
+ export { $fetch, Compute, Handler, MountedEvent, Outlet, RUNTIME_CONTEXT, Radix, Scope, StopEvent, activeCompute, anchorNavigate, components, createApp, createCompute, createElement, createEvent, createMemo, createRuntime, createState, defineComponent, defineContext, defineRoute, fileName, fromValue, getAssets, getCookies, getCustomElement, getGlobalStyles, getMimeType, getRequestUrl, getRoutes, getSetCookies, getVariables, isClient, isServer, isTemplate, markdownToSlot, navigate, preventDefault, registerComponent, renderToNode, renderToString, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, stopImmediatePropagation, stopPropagation, targets, toCustomElement, toFragment, toPath, toString, useEvent };
package/dist/jsx/index.js CHANGED
@@ -1,10 +1,4 @@
1
1
 
2
- //#region src/signals/index.ts
3
- function defineHook(key) {
4
- return key;
5
- }
6
-
7
- //#endregion
8
2
  //#region src/html/index.ts
9
3
  const createElement = (input, attributes, ...children) => {
10
4
  return {
@@ -13,7 +7,6 @@ const createElement = (input, attributes, ...children) => {
13
7
  children
14
8
  };
15
9
  };
16
- const MOUNTED_HOOK = defineHook("MOUNTED_HOOK");
17
10
 
18
11
  //#endregion
19
12
  //#region src/jsx/index.ts
@@ -1,5 +1,6 @@
1
1
  import { type Context, type Event, type Handle, type Middleware } from "../http";
2
2
  import { Radix } from "../radix";
3
+ import { Scope } from "../signals";
3
4
  export type Route<T> = {
4
5
  fetch: Handle<T>;
5
6
  };
@@ -11,7 +12,7 @@ export type Runtime<T> = {
11
12
  export declare const defineRoute: <T>(route: Route<T>) => Route<T>;
12
13
  export declare const fileName: (path: string) => string | undefined;
13
14
  export declare const toPath: (value: string) => (string | undefined)[];
14
- export declare const $fetch: <T>(event: Event | undefined, input: string | URL, init?: RequestInit) => Promise<T>;
15
+ export declare const $fetch: <T>(scope: Scope, input: string | URL, init?: RequestInit) => Promise<T>;
15
16
  export declare const getVariables: <T>(event?: Event<T>) => T;
16
17
  export declare const createRuntime: <T>() => Promise<Runtime<T>>;
17
18
  export declare const RUNTIME_CONTEXT: import("..").Descriptor<{
@@ -6,16 +6,17 @@ export type Descriptor<T> = string & {
6
6
  export interface State<T> {
7
7
  value: T;
8
8
  }
9
- export declare class Hooks {
10
- readonly hooks: Map<string, Set<(value: any) => unknown>>;
9
+ export declare class StopEvent extends Event {
11
10
  constructor();
12
- on<T>(scope: Scope, input: Descriptor<T>, invoke: (value: T) => unknown): number;
13
- dispatch<T>(input: Descriptor<T>, value: T): void;
14
11
  }
15
- export declare class Scope {
16
- readonly dispose: Array<(scope: Scope) => void>;
17
- constructor();
18
- stop(): void;
12
+ export declare class Scope extends EventTarget {
13
+ parentScope?: Scope;
14
+ readonly context: Map<string, unknown>;
15
+ constructor(scope?: Scope);
16
+ getContext<T>(input: Descriptor<T>): T;
17
+ setContext<T>(input: Descriptor<T>, value: T): void;
18
+ onStop(input: (event: StopEvent) => void): void;
19
+ stop(): boolean;
19
20
  }
20
21
  export declare class Compute<T = void> extends Scope {
21
22
  readonly scope: Scope;
@@ -32,8 +33,12 @@ export declare function createState<T>(value: T): State<T>;
32
33
  export declare function createCompute<T>(scope: Scope, invoke: (scope: Scope) => T): T;
33
34
  export declare function createMemo<T>(scope: Scope, invoke: (scope: Scope) => T): State<T>;
34
35
  export declare function fromValue<T>(value: Value<T>): T;
35
- export declare function defineHook<T>(key: string): Descriptor<T>;
36
36
  export declare function defineContext<T>(key: string): Descriptor<T>;
37
37
  export declare let activeCompute: Compute | undefined;
38
38
  export declare const targets: WeakMap<object, Map<string | symbol, Set<Compute<void>>>>;
39
+ declare global {
40
+ interface ElementEventMap {
41
+ stop: StopEvent;
42
+ }
43
+ }
39
44
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revojs",
3
- "version": "0.0.24",
3
+ "version": "0.0.26",
4
4
  "type": "module",
5
5
  "repository": "coverbase/revojs",
6
6
  "license": "MIT",