🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@seahax/elemental

Package Overview
Dependencies
Maintainers
2
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@seahax/elemental - npm Package Compare versions

Comparing version
0.8.1
to
0.8.2
+5
-5
dist/html.d.ts
type Alpha = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
type ChildValue = Node | string | number | bigint | false | null | undefined;
type AttrValue = string | number | bigint | boolean | null | undefined;
type TagMap = HTMLElementTagNameMap & HTMLElementDeprecatedTagNameMap;
type IfEquals<T, U, Y = unknown, N = never> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? Y : N;
type ElementType<TElement> = TElement extends string ? TElement extends keyof TagMap ? TagMap[TElement] : HTMLElement : TElement extends CustomElementConstructor ? InstanceType<TElement> : TElement extends Element | Document | ShadowRoot ? TElement : never;
type ElementProps<TElement> = {
type Props<TElement> = {
readonly [P in keyof TElement & string as TElement[P] extends ((...args: any[]) => any) | null | undefined ? never : IfEquals<Record<P, TElement[P]>, Pick<TElement, P>, `:${P}`, never>]?: TElement[P];
};
type ElementAttrs<TElement> = TElement extends string | CustomElementConstructor | Element ? Readonly<Record<`${Alpha}${string}`, AttrValue>> : {};
export type HtmlConfig<TElement> = ElementAttrs<TElement> & ElementProps<ElementType<TElement>>;
type Attrs<TElement> = TElement extends string | CustomElementConstructor | Element ? Readonly<Record<`${Alpha}${string}`, AttrValue>> : {};
export type ChildValue = Node | string | number | bigint | false | null | undefined;
export type HtmlConfig<TElement> = Attrs<TElement> & Props<ElementType<TElement>>;
export type ElementType<TElement> = TElement extends string ? TElement extends keyof TagMap ? TagMap[TElement] : HTMLElement : TElement extends CustomElementConstructor ? InstanceType<TElement> : TElement extends Element | Document | ShadowRoot ? TElement : never;
/**

@@ -13,0 +13,0 @@ * Create or update an HTML element.

@@ -1,1 +0,1 @@

{"version":3,"file":"html.js","names":[],"sources":["../src/html.ts"],"sourcesContent":["// prettier-ignore\ntype Alpha = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';\ntype ChildValue = Node | string | number | bigint | false | null | undefined;\ntype AttrValue = string | number | bigint | boolean | null | undefined;\ntype TagMap = HTMLElementTagNameMap & HTMLElementDeprecatedTagNameMap;\n\ntype IfEquals<T, U, Y = unknown, N = never> =\n (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? Y : N;\n\ntype ElementType<TElement> = TElement extends string\n ? TElement extends keyof TagMap\n ? TagMap[TElement]\n : HTMLElement\n : TElement extends CustomElementConstructor\n ? InstanceType<TElement>\n : TElement extends Element | Document | ShadowRoot\n ? TElement\n : never;\n\ntype ElementProps<TElement> = {\n readonly [P in keyof TElement & string as TElement[P] extends ((...args: any[]) => any) | null | undefined\n ? never\n : IfEquals<Record<P, TElement[P]>, Pick<TElement, P>, `:${P}`, never>]?: TElement[P];\n};\n\ntype ElementAttrs<TElement> = TElement extends string | CustomElementConstructor | Element\n ? Readonly<Record<`${Alpha}${string}`, AttrValue>>\n : {};\n\nexport type HtmlConfig<TElement> = ElementAttrs<TElement> & ElementProps<ElementType<TElement>>;\n\n/**\n * Create or update an HTML element.\n */\nexport function html<\n TElement extends keyof TagMap | (string & {}) | CustomElementConstructor | Element | Document | ShadowRoot,\n>(el: TElement, config?: HtmlConfig<TElement>, children?: readonly ChildValue[]): ElementType<NoInfer<TElement>>;\nexport function html<\n TElement extends keyof TagMap | (string & {}) | CustomElementConstructor | Element | Document | ShadowRoot,\n>(el: TElement, children?: readonly ChildValue[]): ElementType<TElement>;\n\nexport function html(\n el: string | CustomElementConstructor | Element | Document | ShadowRoot,\n configOrChildren: readonly ChildValue[] | Readonly<Record<string, any>> = {},\n children?: readonly ChildValue[],\n): Node {\n if (typeof el === 'function') {\n let name = customElements.getName(el);\n\n if (name == null) {\n name = `ce-${crypto.randomUUID()}`;\n customElements.define(name, el);\n }\n\n el = name;\n }\n\n if (typeof el === 'string') el = document.createElement(el);\n\n let config: Readonly<Record<string, any>>;\n [config, children] = Array.isArray(configOrChildren)\n ? [{}, configOrChildren as readonly ChildValue[]]\n : [configOrChildren as Readonly<Record<string, any>>, children];\n\n if ('setAttribute' in el) {\n for (const [name, rawValue] of Object.entries(config)) {\n if (rawValue === undefined || name.startsWith(':')) continue;\n\n if (rawValue === null || rawValue === false) {\n if (el.hasAttribute(name)) el.removeAttribute(name);\n } else {\n const value = rawValue == true ? '' : String(rawValue);\n if (el.getAttribute(name) !== value) {\n try {\n el.setAttribute(name, value);\n } catch (error) {\n console.warn(error);\n }\n }\n }\n }\n }\n\n for (const [rawName, value] of Object.entries(config)) {\n if (value === undefined || !rawName.startsWith(':')) continue;\n const name = rawName.slice(1);\n if (Reflect.get(el, name) !== value) Reflect.set(el, name, value);\n }\n\n if (children) {\n const childNodes = children\n .filter((child) => child != null && child !== false)\n .map((child) => (typeof child !== 'object' ? document.createTextNode(String(child)) : child));\n\n el.replaceChildren(...childNodes);\n }\n\n return el;\n}\n"],"mappings":";AAyCA,SAAgB,EACd,GACA,IAA0E,EAAE,EAC5E,GACM;AACN,KAAI,OAAO,KAAO,YAAY;EAC5B,IAAI,IAAO,eAAe,QAAQ,EAAG;AAOrC,EALI,MACF,IAAO,MAAM,OAAO,YAAY,IAChC,eAAe,OAAO,GAAM,EAAG,GAGjC,IAAK;;AAGP,CAAI,OAAO,KAAO,aAAU,IAAK,SAAS,cAAc,EAAG;CAE3D,IAAI;AAKJ,KAJA,CAAC,GAAQ,KAAY,MAAM,QAAQ,EAAiB,GAChD,CAAC,EAAE,EAAE,EAA0C,GAC/C,CAAC,GAAmD,EAAS,EAE7D,kBAAkB,GACpB;OAAK,IAAM,CAAC,GAAM,MAAa,OAAO,QAAQ,EAAO,CAC/C,aAAa,KAAA,KAAa,EAAK,WAAW,IAAI,EAElD,KAAI,MAAa,QAAQ,MAAa,IAChC,EAAG,aAAa,EAAK,IAAE,EAAG,gBAAgB,EAAK;OAC9C;GACL,IAAM,IAAQ,KAAY,IAAO,KAAK,OAAO,EAAS;AACtD,OAAI,EAAG,aAAa,EAAK,KAAK,EAC5B,KAAI;AACF,MAAG,aAAa,GAAM,EAAM;YACrB,GAAO;AACd,YAAQ,KAAK,EAAM;;;;AAO7B,MAAK,IAAM,CAAC,GAAS,MAAU,OAAO,QAAQ,EAAO,EAAE;AACrD,MAAI,MAAU,KAAA,KAAa,CAAC,EAAQ,WAAW,IAAI,CAAE;EACrD,IAAM,IAAO,EAAQ,MAAM,EAAE;AAC7B,EAAI,QAAQ,IAAI,GAAI,EAAK,KAAK,KAAO,QAAQ,IAAI,GAAI,GAAM,EAAM;;AAGnE,KAAI,GAAU;EACZ,IAAM,IAAa,EAChB,QAAQ,MAAU,KAAS,QAAQ,MAAU,GAAM,CACnD,KAAK,MAAW,OAAO,KAAU,WAAoD,IAAzC,SAAS,eAAe,OAAO,EAAM,CAAC,CAAU;AAE/F,IAAG,gBAAgB,GAAG,EAAW;;AAGnC,QAAO"}
{"version":3,"file":"html.js","names":[],"sources":["../src/html.ts"],"sourcesContent":["// prettier-ignore\ntype Alpha = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';\ntype AttrValue = string | number | bigint | boolean | null | undefined;\ntype TagMap = HTMLElementTagNameMap & HTMLElementDeprecatedTagNameMap;\n\ntype IfEquals<T, U, Y = unknown, N = never> =\n (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? Y : N;\n\ntype Props<TElement> = {\n readonly [P in keyof TElement & string as TElement[P] extends ((...args: any[]) => any) | null | undefined\n ? never\n : IfEquals<Record<P, TElement[P]>, Pick<TElement, P>, `:${P}`, never>]?: TElement[P];\n};\n\ntype Attrs<TElement> = TElement extends string | CustomElementConstructor | Element\n ? Readonly<Record<`${Alpha}${string}`, AttrValue>>\n : {};\n\nexport type ChildValue = Node | string | number | bigint | false | null | undefined;\n\nexport type HtmlConfig<TElement> = Attrs<TElement> & Props<ElementType<TElement>>;\n\nexport type ElementType<TElement> = TElement extends string\n ? TElement extends keyof TagMap\n ? TagMap[TElement]\n : HTMLElement\n : TElement extends CustomElementConstructor\n ? InstanceType<TElement>\n : TElement extends Element | Document | ShadowRoot\n ? TElement\n : never;\n\n/**\n * Create or update an HTML element.\n */\nexport function html<\n TElement extends keyof TagMap | (string & {}) | CustomElementConstructor | Element | Document | ShadowRoot,\n>(el: TElement, config?: HtmlConfig<TElement>, children?: readonly ChildValue[]): ElementType<NoInfer<TElement>>;\nexport function html<\n TElement extends keyof TagMap | (string & {}) | CustomElementConstructor | Element | Document | ShadowRoot,\n>(el: TElement, children?: readonly ChildValue[]): ElementType<TElement>;\n\nexport function html(\n el: string | CustomElementConstructor | Element | Document | ShadowRoot,\n configOrChildren: readonly ChildValue[] | Readonly<Record<string, any>> = {},\n children?: readonly ChildValue[],\n): Node {\n if (typeof el === 'function') {\n let name = customElements.getName(el);\n\n if (name == null) {\n name = `ce-${crypto.randomUUID()}`;\n customElements.define(name, el);\n }\n\n el = name;\n }\n\n if (typeof el === 'string') el = document.createElement(el);\n\n let config: Readonly<Record<string, any>>;\n [config, children] = Array.isArray(configOrChildren)\n ? [{}, configOrChildren as readonly ChildValue[]]\n : [configOrChildren as Readonly<Record<string, any>>, children];\n\n if ('setAttribute' in el) {\n for (const [name, rawValue] of Object.entries(config)) {\n if (rawValue === undefined || name.startsWith(':')) continue;\n\n if (rawValue === null || rawValue === false) {\n if (el.hasAttribute(name)) el.removeAttribute(name);\n } else {\n const value = rawValue == true ? '' : String(rawValue);\n if (el.getAttribute(name) !== value) {\n try {\n el.setAttribute(name, value);\n } catch (error) {\n console.warn(error);\n }\n }\n }\n }\n }\n\n for (const [rawName, value] of Object.entries(config)) {\n if (value === undefined || !rawName.startsWith(':')) continue;\n const name = rawName.slice(1);\n if (Reflect.get(el, name) !== value) Reflect.set(el, name, value);\n }\n\n if (children) {\n const childNodes = children\n .filter((child) => child != null && child !== false)\n .map((child) => (typeof child !== 'object' ? document.createTextNode(String(child)) : child));\n\n el.replaceChildren(...childNodes);\n }\n\n return el;\n}\n"],"mappings":";AA0CA,SAAgB,EACd,GACA,IAA0E,EAAE,EAC5E,GACM;AACN,KAAI,OAAO,KAAO,YAAY;EAC5B,IAAI,IAAO,eAAe,QAAQ,EAAG;AAOrC,EALI,MACF,IAAO,MAAM,OAAO,YAAY,IAChC,eAAe,OAAO,GAAM,EAAG,GAGjC,IAAK;;AAGP,CAAI,OAAO,KAAO,aAAU,IAAK,SAAS,cAAc,EAAG;CAE3D,IAAI;AAKJ,KAJA,CAAC,GAAQ,KAAY,MAAM,QAAQ,EAAiB,GAChD,CAAC,EAAE,EAAE,EAA0C,GAC/C,CAAC,GAAmD,EAAS,EAE7D,kBAAkB,GACpB;OAAK,IAAM,CAAC,GAAM,MAAa,OAAO,QAAQ,EAAO,CAC/C,aAAa,KAAA,KAAa,EAAK,WAAW,IAAI,EAElD,KAAI,MAAa,QAAQ,MAAa,IAChC,EAAG,aAAa,EAAK,IAAE,EAAG,gBAAgB,EAAK;OAC9C;GACL,IAAM,IAAQ,KAAY,IAAO,KAAK,OAAO,EAAS;AACtD,OAAI,EAAG,aAAa,EAAK,KAAK,EAC5B,KAAI;AACF,MAAG,aAAa,GAAM,EAAM;YACrB,GAAO;AACd,YAAQ,KAAK,EAAM;;;;AAO7B,MAAK,IAAM,CAAC,GAAS,MAAU,OAAO,QAAQ,EAAO,EAAE;AACrD,MAAI,MAAU,KAAA,KAAa,CAAC,EAAQ,WAAW,IAAI,CAAE;EACrD,IAAM,IAAO,EAAQ,MAAM,EAAE;AAC7B,EAAI,QAAQ,IAAI,GAAI,EAAK,KAAK,KAAO,QAAQ,IAAI,GAAI,GAAM,EAAM;;AAGnE,KAAI,GAAU;EACZ,IAAM,IAAa,EAChB,QAAQ,MAAU,KAAS,QAAQ,MAAU,GAAM,CACnD,KAAK,MAAW,OAAO,KAAU,WAAoD,IAAzC,SAAS,eAAe,OAAO,EAAM,CAAC,CAAU;AAE/F,IAAG,gBAAgB,GAAG,EAAW;;AAGnC,QAAO"}

@@ -5,4 +5,4 @@ //#region src/router/compareRouteMatches.ts

if (e === null || t === null || e.component !== t.component) return !1;
let n = Object.entries(e.attributes), r = new Map(Object.entries(t.attributes));
return n.length === r.size && n.every(([e, t]) => r.has(e) && r.get(e) === t);
let n = Object.entries(e.config), r = new Map(Object.entries(t.config));
return !(!(n.length === r.size && n.every(([e, t]) => r.has(e) && r.get(e) === t)) || !(e.children.length === t.children.length && e.children.every((e, n) => e === t.children[n])));
}

@@ -9,0 +9,0 @@ //#endregion

@@ -1,1 +0,1 @@

{"version":3,"file":"compareRouteMatches.js","names":[],"sources":["../../src/router/compareRouteMatches.ts"],"sourcesContent":["import type { RouteMatch } from './defineRouterComponent.ts';\n\nexport function compareRouteMatches(route: RouteMatch | null, oldRoute: RouteMatch | null): boolean {\n if (Object.is(route, oldRoute)) return true;\n if (route === null || oldRoute === null) return false;\n if (route.component !== oldRoute.component) return false;\n\n const attrEntries = Object.entries(route.attributes);\n const oldAttrEntries = new Map(Object.entries(oldRoute.attributes));\n\n const match =\n attrEntries.length === oldAttrEntries.size &&\n attrEntries.every(([key, value]) => oldAttrEntries.has(key) && oldAttrEntries.get(key) === value);\n\n return match;\n}\n"],"mappings":";AAEA,SAAgB,EAAoB,GAA0B,GAAsC;AAClG,KAAI,OAAO,GAAG,GAAO,EAAS,CAAE,QAAO;AAEvC,KADI,MAAU,QAAQ,MAAa,QAC/B,EAAM,cAAc,EAAS,UAAW,QAAO;CAEnD,IAAM,IAAc,OAAO,QAAQ,EAAM,WAAW,EAC9C,IAAiB,IAAI,IAAI,OAAO,QAAQ,EAAS,WAAW,CAAC;AAMnE,QAHE,EAAY,WAAW,EAAe,QACtC,EAAY,OAAO,CAAC,GAAK,OAAW,EAAe,IAAI,EAAI,IAAI,EAAe,IAAI,EAAI,KAAK,EAAM"}
{"version":3,"file":"compareRouteMatches.js","names":[],"sources":["../../src/router/compareRouteMatches.ts"],"sourcesContent":["import type { RouteMatch } from './defineRouterComponent.ts';\n\nexport function compareRouteMatches(route: RouteMatch | null, oldRoute: RouteMatch | null): boolean {\n if (Object.is(route, oldRoute)) return true;\n if (route === null || oldRoute === null) return false;\n if (route.component !== oldRoute.component) return false;\n\n const configEntries = Object.entries(route.config);\n const oldConfigEntries = new Map(Object.entries(oldRoute.config));\n const configsMatch =\n configEntries.length === oldConfigEntries.size &&\n configEntries.every(([key, value]) => oldConfigEntries.has(key) && oldConfigEntries.get(key) === value);\n if (!configsMatch) return false;\n\n const childrenMatch =\n route.children.length === oldRoute.children.length &&\n route.children.every((child, index) => child === oldRoute.children[index]);\n if (!childrenMatch) return false;\n\n return true;\n}\n"],"mappings":";AAEA,SAAgB,EAAoB,GAA0B,GAAsC;AAClG,KAAI,OAAO,GAAG,GAAO,EAAS,CAAE,QAAO;AAEvC,KADI,MAAU,QAAQ,MAAa,QAC/B,EAAM,cAAc,EAAS,UAAW,QAAO;CAEnD,IAAM,IAAgB,OAAO,QAAQ,EAAM,OAAO,EAC5C,IAAmB,IAAI,IAAI,OAAO,QAAQ,EAAS,OAAO,CAAC;AAWjE,QAFA,EALI,EAFF,EAAc,WAAW,EAAiB,QAC1C,EAAc,OAAO,CAAC,GAAK,OAAW,EAAiB,IAAI,EAAI,IAAI,EAAiB,IAAI,EAAI,KAAK,EAAM,KAMrG,EAFF,EAAM,SAAS,WAAW,EAAS,SAAS,UAC5C,EAAM,SAAS,OAAO,GAAO,MAAU,MAAU,EAAS,SAAS,GAAO"}
import { type ComponentConstructor, type ComponentOptions } from '../defineComponent.ts';
import type { HtmlConfig } from '../html.ts';
type RouteParamNames<T extends string> = T extends `${infer TPrefix}/${infer TSuffix}` ? TPrefix extends `${':' | '*'}${infer TName}` ? TName | RouteParamNames<TSuffix> : RouteParamNames<TSuffix> : T extends `${':' | '*'}${infer TName}` ? TName : never;

@@ -9,2 +10,11 @@ export type RouteParams<T extends string> = string extends T ? Record<string, string> : {

readonly fallback?: CustomElementConstructor;
/**
* Component to render when an error is thrown by the `config` callback
* of a route, which indicates that a path or search parameter is invalid.
*
* - Child text contains the error message.
* - Attribute `data-error-name` contains the `Error` instance `name`.
* - Attribute `data-error-code` contains the `Error` instance `code`.
*/
readonly invalid?: CustomElementConstructor;
}

@@ -18,5 +28,6 @@ export interface RouterBuilder {

* @param component Component to render when this route is matched.
* @param attributes Get attributes to be applied to the component.
* @param config Get the config (attributes and properties) to be applied to
* the component.
*/
readonly addRoute: <const TPattern extends `/${string}`>(pattern: TPattern, component: CustomElementConstructor, attributes?: (pathParams: RouteParams<TPattern>, searchParams: URLSearchParams) => Record<string, string>) => this;
readonly addRoute: <const TPattern extends `/${string}`, TComponent extends CustomElementConstructor>(pattern: TPattern, component: TComponent, config?: (pathParams: RouteParams<TPattern>, searchParams: URLSearchParams) => HtmlConfig<TComponent>) => this;
/** Build the router component class. */

@@ -23,0 +34,0 @@ readonly build: () => ComponentConstructor<{}>;

@@ -10,3 +10,3 @@ import { createRouteMatcher as e } from "./createRouteMatcher.js";

component: n,
attributes: a,
config: a,
matcher: o

@@ -13,0 +13,0 @@ }), i;

@@ -1,1 +0,1 @@

{"version":3,"file":"defineRouter.js","names":[],"sources":["../../src/router/defineRouter.ts"],"sourcesContent":["import { type ComponentConstructor, type ComponentOptions } from '../defineComponent.ts';\nimport { createRouteMatcher, type RouteMatcher } from './createRouteMatcher.ts';\nimport { defineRouterComponent } from './defineRouterComponent.ts';\n\ninterface Route {\n readonly component: CustomElementConstructor;\n readonly attributes: (pathParams: Record<string, string>, searchParams?: URLSearchParams) => Record<string, string>;\n readonly matcher: RouteMatcher;\n}\n\ntype RouteParamNames<T extends string> = T extends `${infer TPrefix}/${infer TSuffix}`\n ? TPrefix extends `${':' | '*'}${infer TName}`\n ? TName | RouteParamNames<TSuffix>\n : RouteParamNames<TSuffix>\n : T extends `${':' | '*'}${infer TName}`\n ? TName\n : never;\n\nexport type RouteParams<T extends string> = string extends T\n ? Record<string, string>\n : { readonly [K in RouteParamNames<T>]: string };\n\nexport interface RouterOptions extends Pick<ComponentOptions<{}>, 'tagName'> {\n /** Component to render when no routes match. */\n readonly fallback?: CustomElementConstructor;\n}\n\nexport interface RouterBuilder {\n /**\n * Add a route. Routes are handled in the order they are added (`Map`\n * insertion order).\n *\n * @param pattern Route matching pattern, e.g. `/blog/:slug`.\n * @param component Component to render when this route is matched.\n * @param attributes Get attributes to be applied to the component.\n */\n readonly addRoute: <const TPattern extends `/${string}`>(\n pattern: TPattern,\n component: CustomElementConstructor,\n attributes?: (pathParams: RouteParams<TPattern>, searchParams: URLSearchParams) => Record<string, string>,\n ) => this;\n\n /** Build the router component class. */\n readonly build: () => ComponentConstructor<{}>;\n}\n\n/** Define a component that renders content based on route matching. */\nexport function defineRouter(options?: RouterOptions): RouterBuilder {\n const routes = new Map<`/${string}`, Route>();\n\n const builder: RouterBuilder = {\n addRoute: (pattern, component, attributes = () => ({})) => {\n const matcher = createRouteMatcher(pattern);\n routes.set(pattern, { component, attributes: attributes as Route['attributes'], matcher });\n return builder;\n },\n build: () => {\n return defineRouterComponent(new Map(routes), options);\n },\n };\n\n return builder;\n}\n"],"mappings":";;;AA+CA,SAAgB,EAAa,GAAwC;CACnE,IAAM,oBAAS,IAAI,KAA0B,EAEvC,IAAyB;EAC7B,WAAW,GAAS,GAAW,WAAoB,EAAE,MAAM;GACzD,IAAM,IAAU,EAAmB,EAAQ;AAE3C,UADA,EAAO,IAAI,GAAS;IAAE;IAAuB;IAAmC;IAAS,CAAC,EACnF;;EAET,aACS,EAAsB,IAAI,IAAI,EAAO,EAAE,EAAQ;EAEzD;AAED,QAAO"}
{"version":3,"file":"defineRouter.js","names":[],"sources":["../../src/router/defineRouter.ts"],"sourcesContent":["import { type ComponentConstructor, type ComponentOptions } from '../defineComponent.ts';\nimport type { HtmlConfig } from '../html.ts';\nimport { createRouteMatcher, type RouteMatcher } from './createRouteMatcher.ts';\nimport { defineRouterComponent } from './defineRouterComponent.ts';\n\ninterface Route {\n readonly component: CustomElementConstructor;\n readonly config: (pathParams: Record<string, string>, searchParams?: URLSearchParams) => HtmlConfig<HTMLElement>;\n readonly matcher: RouteMatcher;\n}\n\ntype RouteParamNames<T extends string> = T extends `${infer TPrefix}/${infer TSuffix}`\n ? TPrefix extends `${':' | '*'}${infer TName}`\n ? TName | RouteParamNames<TSuffix>\n : RouteParamNames<TSuffix>\n : T extends `${':' | '*'}${infer TName}`\n ? TName\n : never;\n\nexport type RouteParams<T extends string> = string extends T\n ? Record<string, string>\n : { readonly [K in RouteParamNames<T>]: string };\n\nexport interface RouterOptions extends Pick<ComponentOptions<{}>, 'tagName'> {\n /** Component to render when no routes match. */\n readonly fallback?: CustomElementConstructor;\n /**\n * Component to render when an error is thrown by the `config` callback\n * of a route, which indicates that a path or search parameter is invalid.\n *\n * - Child text contains the error message.\n * - Attribute `data-error-name` contains the `Error` instance `name`.\n * - Attribute `data-error-code` contains the `Error` instance `code`.\n */\n readonly invalid?: CustomElementConstructor;\n}\n\nexport interface RouterBuilder {\n /**\n * Add a route. Routes are handled in the order they are added (`Map`\n * insertion order).\n *\n * @param pattern Route matching pattern, e.g. `/blog/:slug`.\n * @param component Component to render when this route is matched.\n * @param config Get the config (attributes and properties) to be applied to\n * the component.\n */\n readonly addRoute: <const TPattern extends `/${string}`, TComponent extends CustomElementConstructor>(\n pattern: TPattern,\n component: TComponent,\n config?: (pathParams: RouteParams<TPattern>, searchParams: URLSearchParams) => HtmlConfig<TComponent>,\n ) => this;\n\n /** Build the router component class. */\n readonly build: () => ComponentConstructor<{}>;\n}\n\n/** Define a component that renders content based on route matching. */\nexport function defineRouter(options?: RouterOptions): RouterBuilder {\n const routes = new Map<`/${string}`, Route>();\n\n const builder: RouterBuilder = {\n addRoute: (pattern, component, config = () => ({})) => {\n const matcher = createRouteMatcher(pattern);\n routes.set(pattern, { component, config: config as Route['config'], matcher });\n return builder;\n },\n build: () => {\n return defineRouterComponent(new Map(routes), options);\n },\n };\n\n return builder;\n}\n"],"mappings":";;;AA0DA,SAAgB,EAAa,GAAwC;CACnE,IAAM,oBAAS,IAAI,KAA0B,EAEvC,IAAyB;EAC7B,WAAW,GAAS,GAAW,WAAgB,EAAE,MAAM;GACrD,IAAM,IAAU,EAAmB,EAAQ;AAE3C,UADA,EAAO,IAAI,GAAS;IAAE;IAAmB;IAA2B;IAAS,CAAC,EACvE;;EAET,aACS,EAAsB,IAAI,IAAI,EAAO,EAAE,EAAQ;EAEzD;AAED,QAAO"}
import { type ComponentConstructor } from '../defineComponent.ts';
import { type ChildValue, type HtmlConfig } from '../html.ts';
import type { RouterOptions } from './defineRouter.ts';
interface Route {
readonly component: CustomElementConstructor;
readonly attributes: (pathParams: Record<string, string>, searchParams?: URLSearchParams) => Record<string, string>;
readonly config: (pathParams: Record<string, string>, searchParams?: URLSearchParams) => HtmlConfig<HTMLElement>;
readonly matcher: (segments: readonly string[]) => Record<string, string> | null;

@@ -10,6 +11,7 @@ }

readonly component: CustomElementConstructor;
readonly attributes: Record<string, string>;
readonly config: HtmlConfig<HTMLElement>;
readonly children: readonly ChildValue[];
readonly pattern: string;
}
export declare function defineRouterComponent(routeEntries: ReadonlyMap<`/${string}`, Route>, { fallback, ...options }?: RouterOptions): ComponentConstructor<{}>;
export declare function defineRouterComponent(routeEntries: ReadonlyMap<`/${string}`, Route>, { fallback, invalid, ...options }?: RouterOptions): ComponentConstructor<{}>;
export {};

@@ -9,24 +9,43 @@ import { useRef as e } from "../hooks/useRef.js";

//#region src/router/defineRouterComponent.ts
function s(s, { fallback: c, ...l } = {}) {
function s(s, { fallback: c, invalid: l, ...u } = {}) {
return t((t) => {
let l = r(), u = e(null, { compare: o }), d;
n([l], (e) => {
let u = r(), d = e(null, { compare: o }), f;
n([u], (e) => {
let t = new URL(e), n = a(t.pathname);
for (let [e, { component: r, attributes: i, matcher: a }] of s.entries()) {
for (let [e, { component: r, config: i, matcher: a }] of s.entries()) {
let o = a(n);
if (o) {
u.value = {
pattern: e,
component: r,
attributes: i(o, i.length >= 2 ? new URLSearchParams(t.search) : void 0)
};
return;
let n = i.length >= 2 ? new URLSearchParams(t.search) : void 0;
try {
d.value = {
pattern: e,
component: r,
config: i(o, n),
children: []
};
return;
} catch (e) {
if (l) {
d.value = {
component: l,
pattern: "invalid",
config: {
"data-error-name": e instanceof Error ? e.name : null,
"data-error-code": typeof e?.code == "string" ? e.code : null
},
children: [e instanceof Error ? e.message : String(e)]
};
return;
}
console.warn(e);
}
}
}
u.value = c ? {
d.value = c ? {
component: c,
pattern: "fallback",
attributes: {}
config: {},
children: []
} : null;
}), n([u], (e) => {
}), n([d], (e) => {
if (!e) {

@@ -36,8 +55,8 @@ i(t, []);

}
if (d?.component === e.component && d.pattern === e.pattern) {
i(d.element, e.attributes);
if (f?.component === e.component && f.pattern === e.pattern) {
i(f.element, e.config);
return;
}
let n = i(e.component, e.attributes);
i(t, [n]), d = {
let n = i(e.component, e.config);
i(t, [n]), f = {
component: e.component,

@@ -49,3 +68,3 @@ pattern: e.pattern,

}, {
...l,
...u,
styles: [":host{display:contents;}"]

@@ -52,0 +71,0 @@ });

@@ -1,1 +0,1 @@

{"version":3,"file":"defineRouterComponent.js","names":[],"sources":["../../src/router/defineRouterComponent.ts"],"sourcesContent":["import { type ComponentConstructor, defineComponent } from '../defineComponent.ts';\nimport { useEffect } from '../hooks/useEffect.ts';\nimport { useLocationHref } from '../hooks/useLocationHref.ts';\nimport { useRef } from '../hooks/useRef.ts';\nimport { html } from '../html.ts';\nimport { compareRouteMatches } from './compareRouteMatches.ts';\nimport type { RouterOptions } from './defineRouter.ts';\nimport { parsePath } from './parsePath.ts';\n\ninterface Route {\n readonly component: CustomElementConstructor;\n readonly attributes: (pathParams: Record<string, string>, searchParams?: URLSearchParams) => Record<string, string>;\n readonly matcher: (segments: readonly string[]) => Record<string, string> | null;\n}\n\nexport interface RouteMatch {\n readonly component: CustomElementConstructor;\n readonly attributes: Record<string, string>;\n readonly pattern: string;\n}\n\nexport function defineRouterComponent(\n routeEntries: ReadonlyMap<`/${string}`, Route>,\n { fallback, ...options }: RouterOptions = {},\n): ComponentConstructor<{}> {\n return defineComponent(\n (shadow) => {\n const href = useLocationHref();\n const routeMatch = useRef<RouteMatch | null>(null, { compare: compareRouteMatches });\n\n let prev:\n | { readonly component: CustomElementConstructor; readonly pattern: string; readonly element: HTMLElement }\n | undefined;\n\n useEffect([href], (href) => {\n const url = new URL(href);\n const segments = parsePath(url.pathname);\n\n for (const [pattern, { component, attributes, matcher }] of routeEntries.entries()) {\n const pathParams = matcher(segments);\n\n if (pathParams) {\n const searchParams = attributes.length >= 2 ? new URLSearchParams(url.search) : undefined;\n const resolvedAttributes = attributes(pathParams, searchParams);\n routeMatch.value = { pattern, component, attributes: resolvedAttributes };\n return;\n }\n }\n\n routeMatch.value = fallback ? { component: fallback, pattern: 'fallback', attributes: {} } : null;\n });\n\n useEffect([routeMatch], (routeMatch) => {\n if (!routeMatch) {\n html(shadow, []);\n return;\n }\n\n if (prev?.component === routeMatch.component && prev.pattern === routeMatch.pattern) {\n html(prev.element, routeMatch.attributes);\n return;\n }\n\n const element = html(routeMatch.component, routeMatch.attributes);\n html(shadow, [element]);\n prev = { component: routeMatch.component, pattern: routeMatch.pattern, element };\n });\n },\n { ...options, styles: [':host{display:contents;}'] },\n );\n}\n"],"mappings":";;;;;;;;AAqBA,SAAgB,EACd,GACA,EAAE,aAAU,GAAG,MAA2B,EAAE,EAClB;AAC1B,QAAO,GACJ,MAAW;EACV,IAAM,IAAO,GAAiB,EACxB,IAAa,EAA0B,MAAM,EAAE,SAAS,GAAqB,CAAC,EAEhF;AAsBJ,EAlBA,EAAU,CAAC,EAAK,GAAG,MAAS;GAC1B,IAAM,IAAM,IAAI,IAAI,EAAK,EACnB,IAAW,EAAU,EAAI,SAAS;AAExC,QAAK,IAAM,CAAC,GAAS,EAAE,cAAW,eAAY,iBAAc,EAAa,SAAS,EAAE;IAClF,IAAM,IAAa,EAAQ,EAAS;AAEpC,QAAI,GAAY;AAGd,OAAW,QAAQ;MAAE;MAAS;MAAW,YADd,EAAW,GADjB,EAAW,UAAU,IAAI,IAAI,gBAAgB,EAAI,OAAO,GAAG,KAAA,EAE3B;MAAoB;AACzE;;;AAIJ,KAAW,QAAQ,IAAW;IAAE,WAAW;IAAU,SAAS;IAAY,YAAY,EAAE;IAAE,GAAG;IAC7F,EAEF,EAAU,CAAC,EAAW,GAAG,MAAe;AACtC,OAAI,CAAC,GAAY;AACf,MAAK,GAAQ,EAAE,CAAC;AAChB;;AAGF,OAAI,GAAM,cAAc,EAAW,aAAa,EAAK,YAAY,EAAW,SAAS;AACnF,MAAK,EAAK,SAAS,EAAW,WAAW;AACzC;;GAGF,IAAM,IAAU,EAAK,EAAW,WAAW,EAAW,WAAW;AAEjE,GADA,EAAK,GAAQ,CAAC,EAAQ,CAAC,EACvB,IAAO;IAAE,WAAW,EAAW;IAAW,SAAS,EAAW;IAAS;IAAS;IAChF;IAEJ;EAAE,GAAG;EAAS,QAAQ,CAAC,2BAA2B;EAAE,CACrD"}
{"version":3,"file":"defineRouterComponent.js","names":[],"sources":["../../src/router/defineRouterComponent.ts"],"sourcesContent":["import { type ComponentConstructor, defineComponent } from '../defineComponent.ts';\nimport { useEffect } from '../hooks/useEffect.ts';\nimport { useLocationHref } from '../hooks/useLocationHref.ts';\nimport { useRef } from '../hooks/useRef.ts';\nimport { type ChildValue, html, type HtmlConfig } from '../html.ts';\nimport { compareRouteMatches } from './compareRouteMatches.ts';\nimport type { RouterOptions } from './defineRouter.ts';\nimport { parsePath } from './parsePath.ts';\n\ninterface Route {\n readonly component: CustomElementConstructor;\n readonly config: (pathParams: Record<string, string>, searchParams?: URLSearchParams) => HtmlConfig<HTMLElement>;\n readonly matcher: (segments: readonly string[]) => Record<string, string> | null;\n}\n\nexport interface RouteMatch {\n readonly component: CustomElementConstructor;\n readonly config: HtmlConfig<HTMLElement>;\n readonly children: readonly ChildValue[];\n readonly pattern: string;\n}\n\nexport function defineRouterComponent(\n routeEntries: ReadonlyMap<`/${string}`, Route>,\n { fallback, invalid, ...options }: RouterOptions = {},\n): ComponentConstructor<{}> {\n return defineComponent(\n (shadow) => {\n const href = useLocationHref();\n const routeMatch = useRef<RouteMatch | null>(null, { compare: compareRouteMatches });\n\n let prev:\n | { readonly component: CustomElementConstructor; readonly pattern: string; readonly element: HTMLElement }\n | undefined;\n\n useEffect([href], (href) => {\n const url = new URL(href);\n const segments = parsePath(url.pathname);\n\n for (const [pattern, { component, config, matcher }] of routeEntries.entries()) {\n const pathParams = matcher(segments);\n\n if (pathParams) {\n const searchParams = config.length >= 2 ? new URLSearchParams(url.search) : undefined;\n try {\n const resolvedConfig = config(pathParams, searchParams);\n routeMatch.value = { pattern, component, config: resolvedConfig, children: [] };\n return;\n } catch (error) {\n if (invalid) {\n routeMatch.value = {\n component: invalid,\n pattern: 'invalid',\n config: {\n 'data-error-name': error instanceof Error ? error.name : null,\n 'data-error-code': typeof (error as any)?.code === 'string' ? (error as any).code : null,\n },\n children: [error instanceof Error ? error.message : String(error)],\n };\n\n return;\n }\n\n console.warn(error);\n }\n }\n }\n\n routeMatch.value = fallback ? { component: fallback, pattern: 'fallback', config: {}, children: [] } : null;\n });\n\n useEffect([routeMatch], (routeMatch) => {\n if (!routeMatch) {\n html(shadow, []);\n return;\n }\n\n if (prev?.component === routeMatch.component && prev.pattern === routeMatch.pattern) {\n html(prev.element, routeMatch.config);\n return;\n }\n\n const element = html(routeMatch.component, routeMatch.config);\n html(shadow, [element]);\n prev = { component: routeMatch.component, pattern: routeMatch.pattern, element };\n });\n },\n { ...options, styles: [':host{display:contents;}'] },\n );\n}\n"],"mappings":";;;;;;;;AAsBA,SAAgB,EACd,GACA,EAAE,aAAU,YAAS,GAAG,MAA2B,EAAE,EAC3B;AAC1B,QAAO,GACJ,MAAW;EACV,IAAM,IAAO,GAAiB,EACxB,IAAa,EAA0B,MAAM,EAAE,SAAS,GAAqB,CAAC,EAEhF;AAwCJ,EApCA,EAAU,CAAC,EAAK,GAAG,MAAS;GAC1B,IAAM,IAAM,IAAI,IAAI,EAAK,EACnB,IAAW,EAAU,EAAI,SAAS;AAExC,QAAK,IAAM,CAAC,GAAS,EAAE,cAAW,WAAQ,iBAAc,EAAa,SAAS,EAAE;IAC9E,IAAM,IAAa,EAAQ,EAAS;AAEpC,QAAI,GAAY;KACd,IAAM,IAAe,EAAO,UAAU,IAAI,IAAI,gBAAgB,EAAI,OAAO,GAAG,KAAA;AAC5E,SAAI;AAEF,QAAW,QAAQ;OAAE;OAAS;OAAW,QADlB,EAAO,GAAY,EACO;OAAgB,UAAU,EAAE;OAAE;AAC/E;cACO,GAAO;AACd,UAAI,GAAS;AACX,SAAW,QAAQ;QACjB,WAAW;QACX,SAAS;QACT,QAAQ;SACN,mBAAmB,aAAiB,QAAQ,EAAM,OAAO;SACzD,mBAAmB,OAAQ,GAAe,QAAS,WAAY,EAAc,OAAO;SACrF;QACD,UAAU,CAAC,aAAiB,QAAQ,EAAM,UAAU,OAAO,EAAM,CAAC;QACnE;AAED;;AAGF,cAAQ,KAAK,EAAM;;;;AAKzB,KAAW,QAAQ,IAAW;IAAE,WAAW;IAAU,SAAS;IAAY,QAAQ,EAAE;IAAE,UAAU,EAAE;IAAE,GAAG;IACvG,EAEF,EAAU,CAAC,EAAW,GAAG,MAAe;AACtC,OAAI,CAAC,GAAY;AACf,MAAK,GAAQ,EAAE,CAAC;AAChB;;AAGF,OAAI,GAAM,cAAc,EAAW,aAAa,EAAK,YAAY,EAAW,SAAS;AACnF,MAAK,EAAK,SAAS,EAAW,OAAO;AACrC;;GAGF,IAAM,IAAU,EAAK,EAAW,WAAW,EAAW,OAAO;AAE7D,GADA,EAAK,GAAQ,CAAC,EAAQ,CAAC,EACvB,IAAO;IAAE,WAAW,EAAW;IAAW,SAAS,EAAW;IAAS;IAAS;IAChF;IAEJ;EAAE,GAAG;EAAS,QAAQ,CAAC,2BAA2B;EAAE,CACrD"}

@@ -28,3 +28,3 @@ {

},
"version": "0.8.1"
"version": "0.8.2"
}