revojs 0.0.25 → 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>;
@@ -73,7 +70,7 @@ 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>;
package/dist/index.js CHANGED
@@ -113,8 +113,25 @@ var StopEvent = class extends Event {
113
113
  }
114
114
  };
115
115
  var Scope = class extends EventTarget {
116
- constructor() {
116
+ parentScope;
117
+ context;
118
+ constructor(scope) {
117
119
  super();
120
+ this.parentScope = scope;
121
+ this.context = new Map();
122
+ }
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 {};
132
+ }
133
+ setContext(input, value) {
134
+ this.context.set(input, value);
118
135
  }
119
136
  onStop(input) {
120
137
  this.addEventListener("stop", input, { once: true });
@@ -228,28 +245,28 @@ const toFragment = (nodes) => {
228
245
  fragment.replaceChildren(...nodes);
229
246
  return fragment;
230
247
  };
231
- const renderToString = async (slot, context) => {
248
+ const renderToString = async (scope, slot) => {
232
249
  if (slot) {
233
250
  if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return slot.toString();
234
- if (typeof slot === "function") return await renderToString(await slot(), context);
251
+ if (typeof slot === "function") return await renderToString(scope, await slot());
235
252
  if (typeof slot === "object") {
236
- 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(""));
237
254
  if (isTemplate(slot)) {
238
255
  const customElement = components.get(slot.tag);
239
256
  const prefix = Object.entries(slot.attributes).reduce((chunks, [name, value]) => {
240
257
  if (!name.startsWith("on")) chunks.push(`${name}='${toString(value)}'`);
241
258
  return chunks;
242
259
  }, [slot.tag]).join(" ");
243
- const children = await renderToString(slot.children, context);
260
+ const children = await renderToString(scope, slot.children);
244
261
  if (customElement) {
245
- const element = new customElement(slot.attributes, context);
246
- 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());
247
264
  if (element.shadowRoot) {
248
- const shadow = await renderToString({
265
+ const shadow = await renderToString(scope, {
249
266
  tag: "template",
250
267
  attributes: { shadowRootMode: element.shadowRoot.mode },
251
268
  children: [template]
252
- }, context);
269
+ });
253
270
  return `<${prefix}>` + shadow + children + `</${slot.tag}>`;
254
271
  }
255
272
  return `<${prefix}>` + template + children + `</${slot.tag}>`;
@@ -336,14 +353,12 @@ const defineComponent = (options) => {
336
353
  static $events = options.events ?? {};
337
354
  static $attributes = options.attributes ?? {};
338
355
  scope;
339
- context;
340
356
  events;
341
357
  attributes;
342
358
  shadowRoot;
343
359
  host;
344
- constructor(input, context, host) {
345
- this.scope = new Scope();
346
- this.context = context ?? new Map();
360
+ constructor(input, scope, host) {
361
+ this.scope = new Scope(scope);
347
362
  this.events = Object.keys(options.events ?? {}).reduce((output, name) => {
348
363
  Reflect.set(output, name, input?.[name], output);
349
364
  return output;
@@ -355,13 +370,17 @@ const defineComponent = (options) => {
355
370
  this.shadowRoot = options.shadowRoot ?? { mode: "open" };
356
371
  this.host = host;
357
372
  }
358
- getContext = (input) => {
359
- return this.context.get(input) ?? {};
360
- };
361
- setContext = (input, value) => {
362
- 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);
363
383
  };
364
- setup = () => options.setup(this);
365
384
  });
366
385
  };
367
386
  const toCustomElement = (component) => {
@@ -374,7 +393,6 @@ const toCustomElement = (component) => {
374
393
  }
375
394
  async connectedCallback() {
376
395
  const rootNode = this.component.shadowRoot ? this.attachShadow(this.component.shadowRoot) : this;
377
- const parentNode = getCustomElement(this.parentNode);
378
396
  for (const [name, event] of Object.entries(component.$events)) Reflect.set(this.component.events, name, (value) => {
379
397
  if (value instanceof Event) return;
380
398
  this.dispatchEvent(new CustomEvent(name.substring(2).toLowerCase(), {
@@ -382,7 +400,6 @@ const toCustomElement = (component) => {
382
400
  detail: value
383
401
  }));
384
402
  }, this.component.events);
385
- if (parentNode) for (const [name, value] of parentNode.component.context) this.component.context.set(name, value);
386
403
  rootNode.replaceChildren(await renderToNode(this.component.scope, await this.component.setup()));
387
404
  this.dispatchEvent(new MountedEvent());
388
405
  }
@@ -678,7 +695,8 @@ const toPath = (value) => {
678
695
  const split = route.split(".");
679
696
  return split.length === 3 ? [split.at(0), split.at(1)] : [split.at(0)];
680
697
  };
681
- const $fetch = async (event, input, init) => {
698
+ const $fetch = async (scope, input, init) => {
699
+ const { event } = scope.getContext(RUNTIME_CONTEXT);
682
700
  let response;
683
701
  if (event) {
684
702
  const url = new URL(input.toString(), event.request.url);
@@ -703,8 +721,9 @@ const createRuntime = async () => {
703
721
  const route = await routes[path]?.();
704
722
  if (typeof route === "object") return route.fetch(event);
705
723
  if (route) {
706
- const slot = await import("#virtual/client").then((module) => module.index);
707
- 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)));
708
727
  }
709
728
  } }));
710
729
  }
@@ -746,8 +765,8 @@ const RUNTIME_CONTEXT = defineContext("RUNTIME_CONTEXT");
746
765
  const Outlet = defineComponent({
747
766
  name: "x-outlet",
748
767
  shadowRoot: false,
749
- setup: async ({ scope, getContext }) => {
750
- const { event } = getContext(RUNTIME_CONTEXT);
768
+ setup: async ({ scope }) => {
769
+ const { event } = scope.getContext(RUNTIME_CONTEXT);
751
770
  const radix = new Radix();
752
771
  const routes = await getRoutes();
753
772
  for (const path in routes) {
@@ -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<{
@@ -10,7 +10,11 @@ export declare class StopEvent extends Event {
10
10
  constructor();
11
11
  }
12
12
  export declare class Scope extends EventTarget {
13
- constructor();
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;
14
18
  onStop(input: (event: StopEvent) => void): void;
15
19
  stop(): boolean;
16
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revojs",
3
- "version": "0.0.25",
3
+ "version": "0.0.26",
4
4
  "type": "module",
5
5
  "repository": "coverbase/revojs",
6
6
  "license": "MIT",