revojs 0.0.29 → 0.0.31

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
@@ -3,10 +3,10 @@ import { h } from "revojs/jsx-runtime";
3
3
 
4
4
  //#region src/app/index.ts
5
5
  const getRoutes = async () => {
6
- return await import("#virtual/routes").then((module) => module.index);
6
+ return await import("#virtual/routes").then((module) => module.routes);
7
7
  };
8
8
  const getAssets = async () => {
9
- return await import("#virtual/assets").then((module) => module.index);
9
+ return await import("#virtual/assets").then((module) => module.assets);
10
10
  };
11
11
  const createApp = (config) => {
12
12
  return {
@@ -252,14 +252,15 @@ const renderToString = async (scope, slot) => {
252
252
  if (typeof slot === "object") {
253
253
  if (Array.isArray(slot)) return await Promise.all(slot.map((slot$1) => renderToString(scope, slot$1))).then((chunks) => chunks.join(""));
254
254
  if (isTemplate(slot)) {
255
- const customElement = components.get(slot.tag);
255
+ const CustomElement = components.get(slot.tag);
256
256
  const prefix = Object.entries(slot.attributes).reduce((chunks, [name, value]) => {
257
257
  if (!name.startsWith("on")) chunks.push(`${name}='${toString(value)}'`);
258
258
  return chunks;
259
259
  }, [slot.tag]).join(" ");
260
260
  const children = await renderToString(scope, slot.children);
261
- if (customElement) {
262
- const element = new customElement(slot.attributes, scope);
261
+ if (CustomElement) {
262
+ const element = new CustomElement(slot.attributes, scope);
263
+ scope.onStop(() => element.scope.stop());
263
264
  const template = await renderToString(element.scope, await element.setup());
264
265
  if (element.shadowRoot) {
265
266
  const shadow = await renderToString(element.scope, {
@@ -556,7 +557,7 @@ var Radix = class Radix {
556
557
  children;
557
558
  constructor(input) {
558
559
  this.input = input;
559
- this.children = new Map();
560
+ this.children = {};
560
561
  }
561
562
  insert = (path, value) => {
562
563
  let node = this;
@@ -566,10 +567,10 @@ var Radix = class Radix {
566
567
  input = segment.substring(1);
567
568
  segment = ":";
568
569
  }
569
- let childNode = node.children.get(segment);
570
+ let childNode = node.children[segment];
570
571
  if (childNode === void 0) {
571
572
  childNode = new Radix(input);
572
- node.children.set(segment, childNode);
573
+ node.children[segment] = childNode;
573
574
  }
574
575
  node = childNode;
575
576
  }
@@ -580,7 +581,7 @@ var Radix = class Radix {
580
581
  let node = this;
581
582
  const match = { inputs: {} };
582
583
  for (const segment of path.split("/")) {
583
- node = node?.children.get(segment) ?? node?.children.get(":");
584
+ node = node?.children[segment] ?? node?.children[":"];
584
585
  if (node?.input) match.inputs[node.input] = segment;
585
586
  }
586
587
  match.value = node?.value;
@@ -597,7 +598,7 @@ const fileName = (path) => {
597
598
  return path.split("/").pop()?.split(".").slice(0, -1).join(".");
598
599
  };
599
600
  const toPath = (value) => {
600
- const path = (value.startsWith("/") ? value : "/" + value).replaceAll(/\/index/g, "").replaceAll(/\[(.*)\]/g, (_, name) => ":" + name);
601
+ const path = (value.startsWith("/") ? value : "/" + value).replaceAll(/\/index/g, "").replaceAll(/\[(.*?)\]/g, (_, name) => ":" + name);
601
602
  const route = path.startsWith("/") ? path : "/" + path;
602
603
  const split = route.split(".");
603
604
  return split.length === 3 ? [split.at(0), split.at(1)] : [split.at(0)];
@@ -629,8 +630,12 @@ const createRuntime = async () => {
629
630
  if (typeof route === "object") return route.fetch(event);
630
631
  if (route) {
631
632
  const scope = new Scope();
632
- scope.setContext(RUNTIME_CONTEXT, { event });
633
- return sendHtml(event, await renderToString(scope, await import("#virtual/client").then((module) => module.index)));
633
+ try {
634
+ scope.setContext(RUNTIME_CONTEXT, { event });
635
+ return sendHtml(event, await renderToString(scope, await import("#virtual/client").then((module) => module.client)));
636
+ } finally {
637
+ scope.stop();
638
+ }
634
639
  }
635
640
  } }));
636
641
  }
@@ -658,9 +663,9 @@ const createRuntime = async () => {
658
663
  if (response) return response;
659
664
  }
660
665
  return sendText(event, "Not found");
661
- } catch (response) {
662
- if (response instanceof Response) return response;
663
- throw response;
666
+ } catch (exception) {
667
+ if (exception instanceof Response) return exception;
668
+ throw exception;
664
669
  }
665
670
  }
666
671
  };
@@ -669,72 +674,124 @@ const RUNTIME_CONTEXT = defineContext("RUNTIME_CONTEXT");
669
674
 
670
675
  //#endregion
671
676
  //#region src/router/index.tsx
672
- const ROUTE_CONTEXT = defineContext("ROUTE_CONTEXT");
673
- const navigate = (url) => {
674
- if (isClient()) {
675
- const state = window.history.state;
676
- window.history.pushState(state, "", url);
677
- window.dispatchEvent(new PopStateEvent("popstate", { state }));
677
+ var NavigateEvent = class extends Event {
678
+ constructor() {
679
+ super("navigate");
678
680
  }
679
681
  };
680
- const anchorNavigate = (event) => {
681
- event.preventDefault();
682
- navigate(event.currentTarget?.getAttribute("href") ?? "/");
683
- };
684
- const Outlet = defineComponent({
685
- name: "x-outlet",
686
- shadowRoot: false,
687
- setup: async ({ scope }) => {
688
- const { event } = scope.getContext(RUNTIME_CONTEXT);
689
- const radix = new Radix();
690
- const routes = await getRoutes();
691
- for (const path in routes) {
692
- const [name] = toPath(path);
693
- if (name) radix.insert(name, routes[path]);
682
+ const ROUTER_CONTEXT = defineContext("ROUTER_CONTEXT");
683
+ const createRouter = (options) => {
684
+ const navigator = new EventTarget();
685
+ const radix = new Radix();
686
+ const route = createState();
687
+ const inputs = createState();
688
+ for (const path in options.routes) {
689
+ const [name] = toPath(path);
690
+ if (name) {
691
+ const value = options.routes[path];
692
+ if (value) radix.insert(name, value);
694
693
  }
695
- const url = createState(new URL(event ? event.request.url : window.location.href));
696
- if (isClient()) useEvent(scope, window, "popstate", () => url.value = new URL(window.location.href));
697
- return async () => {
698
- const { value, inputs } = radix.match(url.value.pathname);
699
- const Page = await value?.();
700
- if (Page) {
701
- scope.setContext(ROUTE_CONTEXT, { inputs });
702
- return /* @__PURE__ */ h(Page, inputs);
703
- }
694
+ }
695
+ const registerRouterContext = async (scope) => {
696
+ const { event } = scope.getContext(RUNTIME_CONTEXT);
697
+ const fetch$1 = async () => {
698
+ const url = new URL(event ? event.request.url : window.location.href);
699
+ const match = radix.match(url.pathname);
700
+ inputs.value = match.inputs;
701
+ const Page$1 = await match.value?.();
702
+ if (Page$1) route.value = /* @__PURE__ */ h(Page$1, inputs.value);
704
703
  };
704
+ if (isClient()) useEvent(scope, window, "popstate", () => navigator.dispatchEvent(new NavigateEvent()));
705
+ if (event) inputs.value = event.context.inputs;
706
+ await fetch$1().then(() => useEvent(scope, navigator, "navigate", fetch$1));
707
+ scope.setContext(ROUTER_CONTEXT, {
708
+ options,
709
+ navigator,
710
+ radix,
711
+ route,
712
+ inputs
713
+ });
714
+ return useRouter(scope);
715
+ };
716
+ return {
717
+ ROUTER_CONTEXT,
718
+ registerRouterContext
719
+ };
720
+ };
721
+ const useRouter = (scope, context) => {
722
+ const { route, inputs, navigator } = scope.getContext(context ?? ROUTER_CONTEXT);
723
+ const navigate = (path) => {
724
+ if (isClient()) window.history.pushState(window.history.state, "", path);
725
+ navigator.dispatchEvent(new NavigateEvent());
726
+ };
727
+ const anchorNavigate = (event) => {
728
+ event.preventDefault();
729
+ navigate(event.currentTarget?.getAttribute("href") ?? "/");
730
+ };
731
+ return {
732
+ route,
733
+ inputs,
734
+ navigator,
735
+ navigate,
736
+ anchorNavigate
737
+ };
738
+ };
739
+ const Page = defineComponent({
740
+ name: "x-page",
741
+ shadowRoot: false,
742
+ setup: ({ scope }) => {
743
+ const { route } = scope.getContext(ROUTER_CONTEXT);
744
+ if (route === void 0) return;
745
+ return () => route.value;
705
746
  }
706
747
  });
707
748
 
708
749
  //#endregion
709
750
  //#region src/locale/index.ts
710
- const createLocaleContext = (options) => {
711
- const LOCALE_CONTEXT = defineContext("LOCALE_CONTEXT");
712
- const registerLocaleContext = (scope) => {
713
- scope.setContext(LOCALE_CONTEXT, options);
751
+ const LOCALE_CONTEXT = defineContext("LOCALE_CONTEXT");
752
+ const createLocale = (options) => {
753
+ const locale = createState(options.defaultLocale);
754
+ const messages = createState();
755
+ const registerLocaleContext = async (scope) => {
756
+ const { inputs, navigator, navigate } = useRouter(scope);
757
+ const fetch$1 = async () => {
758
+ if (options.input) {
759
+ const input = inputs.value?.[options.input];
760
+ if (input && input in options.locales) locale.value = input;
761
+ else navigate("/" + (locale.value ?? ""));
762
+ }
763
+ if (locale.value) {
764
+ const target = options.locales[locale.value];
765
+ messages.value = typeof target === "function" ? await target() : target;
766
+ }
767
+ };
768
+ await fetch$1().then(() => useEvent(scope, navigator, "navigate", fetch$1));
769
+ scope.setContext(LOCALE_CONTEXT, {
770
+ locale,
771
+ messages,
772
+ options
773
+ });
774
+ return useLocale(scope);
714
775
  };
715
776
  return {
716
777
  LOCALE_CONTEXT,
717
778
  registerLocaleContext
718
779
  };
719
780
  };
720
- const useLocaleContext = async (scope, context) => {
721
- const { event } = scope.getContext(RUNTIME_CONTEXT);
722
- const { inputs } = scope.getContext(ROUTE_CONTEXT);
723
- const { locales, defaultLocale, cookie, input } = scope.getContext(context);
724
- const entries = await import("#virtual/locales").then((module) => module.index);
725
- let locale;
726
- if (cookie) {
727
- if (event) locale = getCookies(event)[cookie];
728
- }
729
- if (input) if (event) locale = event.context.inputs[input];
730
- else locale = inputs[input];
731
- locale ??= defaultLocale;
732
- if (locale) locales[locale] = await entries[locale]?.() ?? {};
781
+ const useLocale = (scope, context) => {
782
+ const { locale, messages } = scope.getContext(context ?? LOCALE_CONTEXT);
733
783
  const $ = (key) => {
734
- if (locale) return locales[locale]?.[key] ?? key;
735
- return key;
784
+ return () => messages.value?.[key] ?? key;
785
+ };
786
+ const prefix = (input) => {
787
+ return () => `/${locale.value}` + (input ?? "");
788
+ };
789
+ return {
790
+ locale,
791
+ messages,
792
+ prefix,
793
+ $
736
794
  };
737
- return { $ };
738
795
  };
739
796
 
740
797
  //#endregion
@@ -831,4 +888,4 @@ const markdownToSlot = (input, options) => {
831
888
  };
832
889
 
833
890
  //#endregion
834
- export { $fetch, Compute, Handler, MountedEvent, Outlet, ROUTE_CONTEXT, RUNTIME_CONTEXT, Radix, Scope, StopEvent, activeCompute, anchorNavigate, components, createApp, createCompute, createElement, createEvent, createLocaleContext, 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, useLocaleContext };
891
+ export { $fetch, Compute, Handler, LOCALE_CONTEXT, MountedEvent, NavigateEvent, Page, ROUTER_CONTEXT, RUNTIME_CONTEXT, Radix, Scope, StopEvent, activeCompute, components, createApp, createCompute, createElement, createEvent, createLocale, createMemo, createRouter, createRuntime, createState, defineComponent, defineContext, defineRoute, fileName, fromValue, getAssets, getCookies, getCustomElement, getGlobalStyles, getMimeType, getRequestUrl, getRoutes, getSetCookies, getVariables, isClient, isServer, isTemplate, markdownToSlot, preventDefault, registerComponent, renderToNode, renderToString, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, stopImmediatePropagation, stopPropagation, targets, toCustomElement, toFragment, toPath, toString, useEvent, useLocale, useRouter };
@@ -1,15 +1,28 @@
1
- import { type Descriptor, Scope } from "../signals";
2
- export type Locales = Record<string, Record<string, string>>;
3
- export type LocaleOptions<T extends Locales> = {
1
+ import { type Descriptor, Scope, type State } from "../signals";
2
+ export type Locales = Record<string, Record<string, string> | (() => Promise<Record<string, string>>)>;
3
+ export type LocaleOptions<T extends Locales = Locales> = {
4
4
  locales: T;
5
5
  defaultLocale?: keyof T;
6
- cookie?: string;
7
6
  input?: string;
8
7
  };
9
- export declare const createLocaleContext: <T extends LocaleOptions<Locales>>(options: T) => {
10
- LOCALE_CONTEXT: Descriptor<T>;
11
- registerLocaleContext: (scope: Scope) => void;
8
+ export type LocaleContext<T extends LocaleOptions = LocaleOptions> = {
9
+ locale: State<string | undefined>;
10
+ messages: State<Record<string, string> | undefined>;
11
+ options: T;
12
+ };
13
+ export declare const LOCALE_CONTEXT: Descriptor<LocaleContext<LocaleOptions<Locales>>>;
14
+ export declare const createLocale: <T extends LocaleOptions>(options: T) => {
15
+ LOCALE_CONTEXT: Descriptor<LocaleContext<T>>;
16
+ registerLocaleContext: (scope: Scope) => Promise<{
17
+ locale: State<string | undefined>;
18
+ messages: State<Record<string, string> | undefined>;
19
+ prefix: (input?: string) => () => string;
20
+ $: (key: never) => () => string | number | symbol;
21
+ }>;
22
+ };
23
+ export declare const useLocale: <T extends LocaleContext>(scope: Scope, context?: Descriptor<T>) => {
24
+ locale: State<string | undefined>;
25
+ messages: State<Record<string, string> | undefined>;
26
+ prefix: (input?: string) => () => string;
27
+ $: (key: keyof T["options"]["locales"][keyof T["options"]["locales"]]) => () => string | number | symbol;
12
28
  };
13
- export declare const useLocaleContext: <T extends LocaleOptions<Locales>>(scope: Scope, context: Descriptor<T>) => Promise<{
14
- $: (key: keyof T["locales"][keyof T["locales"]]) => string | number | symbol | keyof T["locales"][keyof T["locales"]];
15
- }>;
@@ -5,7 +5,7 @@ export type Match<T> = {
5
5
  export declare class Radix<T> {
6
6
  value?: T;
7
7
  input?: string;
8
- children: Map<string, Radix<T>>;
8
+ children: Record<string, Radix<T>>;
9
9
  constructor(input?: string);
10
10
  insert: (path: string, value: T) => this;
11
11
  match: (path: string) => Match<T>;
@@ -1,7 +1,41 @@
1
- import { type ComponentConstructor } from "../html";
2
- export declare const ROUTE_CONTEXT: import("..").Descriptor<{
3
- inputs: Record<string, string>;
4
- }>;
5
- export declare const navigate: (url: string) => void;
6
- export declare const anchorNavigate: (event: Event) => void;
7
- export declare const Outlet: ComponentConstructor<{}, {}>;
1
+ import { type Attributes, type ComponentConstructor, type Events, type Slot } from "../html";
2
+ import { Radix } from "../radix";
3
+ import { type Descriptor, Scope, type State } from "../signals";
4
+ export type Routes = Record<string, () => Promise<ComponentConstructor<Events, Attributes>>>;
5
+ export type RouterOptions<T extends Routes = Routes> = {
6
+ routes: T;
7
+ };
8
+ export type RouterContext<T extends RouterOptions = RouterOptions> = {
9
+ options: T;
10
+ navigator: EventTarget;
11
+ radix: Radix<() => Promise<ComponentConstructor<Events, Attributes>>>;
12
+ route: State<Slot | undefined>;
13
+ inputs: State<Record<string, string> | undefined>;
14
+ };
15
+ export declare class NavigateEvent extends Event {
16
+ constructor();
17
+ }
18
+ export declare const ROUTER_CONTEXT: Descriptor<RouterContext<RouterOptions<Routes>>>;
19
+ export declare const createRouter: <T extends RouterOptions>(options: T) => {
20
+ ROUTER_CONTEXT: Descriptor<RouterContext<T>>;
21
+ registerRouterContext: (scope: Scope) => Promise<{
22
+ route: State<unknown>;
23
+ inputs: State<Record<string, string> | undefined>;
24
+ navigator: EventTarget;
25
+ navigate: (path: string) => void;
26
+ anchorNavigate: (event: Event) => void;
27
+ }>;
28
+ };
29
+ export declare const useRouter: <T extends RouterContext>(scope: Scope, context?: Descriptor<T>) => {
30
+ route: State<unknown>;
31
+ inputs: State<Record<string, string> | undefined>;
32
+ navigator: EventTarget;
33
+ navigate: (path: string) => void;
34
+ anchorNavigate: (event: Event) => void;
35
+ };
36
+ export declare const Page: ComponentConstructor<{}, {}>;
37
+ declare global {
38
+ interface ElementEventMap {
39
+ navigate: NavigateEvent;
40
+ }
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revojs",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "type": "module",
5
5
  "repository": "coverbase/revojs",
6
6
  "license": "MIT",
@@ -16,12 +16,15 @@
16
16
  "./jsx-runtime": {
17
17
  "types": "./dist/jsx/index.d.ts",
18
18
  "import": "./dist/jsx/index.js"
19
+ },
20
+ "./types": {
21
+ "types": "./src/types/index.d.ts"
19
22
  }
20
23
  },
21
24
  "types": "./dist/index.d.ts",
22
25
  "module": "./dist/index.js",
23
26
  "main": "./dist/index.js",
24
- "files": ["dist"],
27
+ "files": ["dist", "src/virtual"],
25
28
  "scripts": {
26
29
  "build": "rolldown -c rolldown.config.ts && tsc",
27
30
  "watch": "rolldown -w -c rolldown.config.ts && tsc --watch"