@elurjs/core 3.5.0 → 3.6.0
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/README.md +5 -5
- package/dist/lib/component.cjs +1 -1
- package/dist/lib/component.js +1 -1
- package/dist/lib/devtools.cjs +1 -1
- package/dist/lib/devtools.js +1 -1
- package/dist/lib/elur/component.cjs +1 -1
- package/dist/lib/elur/component.cjs.map +1 -1
- package/dist/lib/elur/component.d.cts +1 -1
- package/dist/lib/elur/component.d.ts +1 -1
- package/dist/lib/elur/component.js +1 -1
- package/dist/lib/elur/component.js.map +1 -1
- package/dist/lib/elur/devtools.cjs +1 -1
- package/dist/lib/elur/devtools.cjs.map +1 -1
- package/dist/lib/elur/devtools.js +1 -1
- package/dist/lib/elur/devtools.js.map +1 -1
- package/dist/lib/elur/lifecycle.cjs +1 -1
- package/dist/lib/elur/lifecycle.cjs.map +1 -1
- package/dist/lib/elur/lifecycle.d.cts +6 -0
- package/dist/lib/elur/lifecycle.d.ts +6 -0
- package/dist/lib/elur/lifecycle.js +40 -7
- package/dist/lib/elur/lifecycle.js.map +1 -1
- package/dist/lib/elur/reactivity.cjs +1 -1
- package/dist/lib/elur/reactivity.cjs.map +1 -1
- package/dist/lib/elur/reactivity.d.cts +7 -0
- package/dist/lib/elur/reactivity.d.ts +7 -0
- package/dist/lib/elur/reactivity.js +53 -23
- package/dist/lib/elur/reactivity.js.map +1 -1
- package/dist/lib/elur/router-registry.cjs +2 -0
- package/dist/lib/elur/router-registry.cjs.map +1 -0
- package/dist/lib/elur/router-registry.d.cts +14 -0
- package/dist/lib/elur/router-registry.d.ts +14 -0
- package/dist/lib/elur/router-registry.js +15 -0
- package/dist/lib/elur/router-registry.js.map +1 -0
- package/dist/lib/elur/router.cjs +3 -3
- package/dist/lib/elur/router.cjs.map +1 -1
- package/dist/lib/elur/router.d.cts +2 -5
- package/dist/lib/elur/router.d.ts +2 -5
- package/dist/lib/elur/router.js +159 -166
- package/dist/lib/elur/router.js.map +1 -1
- package/dist/lib/elur/template/html.cjs +1 -1
- package/dist/lib/elur/template/html.cjs.map +1 -1
- package/dist/lib/elur/template/html.js +1 -1
- package/dist/lib/elur/template/html.js.map +1 -1
- package/dist/lib/elur/template/types.cjs +1 -1
- package/dist/lib/elur/template/types.cjs.map +1 -1
- package/dist/lib/elur/template/types.js +1 -1
- package/dist/lib/elur/template/types.js.map +1 -1
- package/dist/lib/elur.cjs +1 -1
- package/dist/lib/elur.js +6 -5
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/index.js +6 -5
- package/dist/lib/lifecycle.cjs +1 -1
- package/dist/lib/lifecycle.js +40 -7
- package/dist/lib/router.cjs +3 -3
- package/dist/lib/router.js +159 -166
- package/dist/lib/signals.cjs +1 -1
- package/dist/lib/signals.js +53 -23
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","names":[],"sources":["../../src/elur/router.ts"],"sourcesContent":["import { signal } from \"./reactivity.js\";\nimport type { Signal } from \"./reactivity.js\";\nimport { ElurComponent } from \"./lifecycle.js\";\nimport type { ElurTemplate } from \"./template/index.js\";\nimport { html } from \"./template/index.js\";\nimport { createInjectionKey, inject } from \"./context.js\";\n\n// =============================================================================\n// Public types\n// =============================================================================\n\n/**\n * Value returned (or resolved) by a navigation guard.\n *\n * - `true` / `void` / `undefined` — allow.\n * - `false` — cancel (no redirect).\n * - `string` — redirect to that path.\n * - `{ redirect: string }` — redirect, object form.\n *\n * The object form exists so guards written for the outlet API can be reused\n * verbatim by the core. See `elur-ionic`'s `GuardResult`.\n */\nexport type NavigationGuardResult =\n | void\n | undefined\n | boolean\n | string\n | { redirect: string };\n\n/** Guard function invoked before navigation commits. */\nexport type NavigationGuard = (\n to: string,\n from: string,\n) => NavigationGuardResult | Promise<NavigationGuardResult>;\n\nexport interface RouteRecord {\n /** Optional unique name to enable named navigation. */\n name?: string;\n /** Route path segment. Supports literals, params (`:id`), and wildcards (`*`). */\n path: string;\n /**\n * Factory returning the view for this route level.\n *\n * OPTIONAL — when the core router is auto-bootstrapped by an outlet\n * (Ionic's IonRouterOutlet, future others), the outlet owns component\n * mounting and the core never invokes this. In that case, omit it.\n */\n component?: () => ElurTemplate | ElurComponent;\n /** Optional arbitrary metadata for guards, layouts, and auth checks. */\n meta?: Record<string, unknown>;\n /** Child routes. Paths are joined with the parent. */\n children?: RouteRecord[];\n /** Route-level guard. Runs only when entering this specific route. */\n beforeEnter?: NavigationGuard;\n}\n\n/** Callback for `afterEach` hooks — receives the committed `to` and `from` paths. */\nexport type AfterEachHook = (to: string, from: string) => void;\n\n/** Named route target for programmatic navigation. */\nexport interface NamedRouteLocation {\n name: string;\n params?: Record<string, string | number>;\n query?: Record<string, string | number | boolean | null | undefined>;\n}\n\n/** Navigation input accepted by `navigate` / `replace`. */\nexport type RouteLocation = string | NamedRouteLocation;\n\n/** Serializable scroll position used by the router for history restoration. */\nexport interface ScrollPosition {\n left: number;\n top: number;\n}\n\nexport type ScrollBehavior = (\n to: string,\n from: string,\n savedPosition: ScrollPosition | null,\n) => ScrollPosition | false | void;\n\nexport type RouterMode = \"history\" | \"hash\";\n\nexport interface ResolvedRoute {\n matched: boolean;\n params: Record<string, string>;\n route: RouteRecord | undefined;\n}\n\n// -----------------------------------------------------------------------------\n// Navigation intent\n// -----------------------------------------------------------------------------\n\nexport type NavigationAction = \"push\" | \"replace\" | \"pop\" | \"initial\";\nexport type NavigationDirection = \"forward\" | \"back\" | \"root\" | \"none\";\n\nexport interface NavigationIntent {\n action: NavigationAction;\n direction: NavigationDirection;\n animation?: unknown;\n}\n\nexport interface NavigateOptions {\n query?: Record<string, string | number | boolean | null | undefined>;\n direction?: NavigationDirection;\n animation?: unknown;\n}\n\nexport interface RouterOptions {\n base?: string;\n mode?: RouterMode;\n scrollBehavior?: ScrollBehavior;\n}\n\nexport interface Router {\n readonly current: Signal<string>;\n readonly params: Signal<Record<string, string>>;\n readonly query: Signal<Record<string, string>>;\n readonly base: string;\n readonly intent: Signal<NavigationIntent>;\n readonly canGoBack: Signal<boolean>;\n navigate(location: RouteLocation, options?: NavigateOptions): void;\n replace(location: RouteLocation, options?: NavigateOptions): void;\n back(animation?: unknown): void;\n forward(animation?: unknown): void;\n go(delta: number): void;\n isActive(path: string, exact?: boolean): boolean;\n resolve(path: string): ResolvedRoute;\n readonly routes: RouteRecord[];\n beforeEach(guard: NavigationGuard): () => void;\n afterEach(hook: AfterEachHook): () => void;\n}\n\nexport const RouterKey = createInjectionKey<Router>(\"elur:router\");\n\n// =============================================================================\n// Internals\n// =============================================================================\n\ntype Segment =\n | { kind: \"literal\"; value: string }\n | { kind: \"param\"; name: string }\n | { kind: \"wildcard\" };\n\ninterface FlatRoute {\n fullPath: string;\n segments: Segment[];\n chain: Array<(() => ElurTemplate | ElurComponent) | undefined>;\n name?: string;\n meta?: Record<string, unknown>;\n beforeEnter?: NavigationGuard;\n record: RouteRecord;\n}\n\ninterface RouterInternal extends Router {\n _flat: FlatRoute[];\n _guards: NavigationGuard[];\n _base: string;\n _mode: RouterMode;\n}\n\nlet _currentRouter: RouterInternal | null = null;\nlet _currentPopstateCleanup: (() => void) | null = null;\n\n// Routers injected via mount({ router }) are not the global singleton. DevTools\n// tracks them separately so it can inspect the router actually active in the UI.\nconst _mountedRouters: Router[] = [];\n\nconst SCROLL_STATE_KEY = \"__elur_scroll\";\nconst POSITION_STATE_KEY = \"__elur_pos\";\n\nfunction getRouter(): RouterInternal {\n if (!_currentRouter) {\n throw new Error(\n \"[elur] No active router. Call createRouter() first, \" +\n \"or instantiate an outlet that auto-bootstraps one (e.g. IonRouterOutlet).\"\n );\n }\n return _currentRouter;\n}\n\n/**\n * @internal Whether a router is currently active. Used by outlets that\n * want to auto-bootstrap if the user didn't call `createRouter()` themselves.\n */\nexport function _hasActiveRouter(): boolean {\n return _currentRouter !== null;\n}\n\n// =============================================================================\n// History state helpers\n// =============================================================================\n\nfunction getCurrentScrollPosition(): ScrollPosition {\n return {\n left: window.scrollX ?? window.pageXOffset ?? 0,\n top: window.scrollY ?? window.pageYOffset ?? 0,\n };\n}\n\nfunction readScrollPositionFromState(state: unknown): ScrollPosition | null {\n if (!state || typeof state !== \"object\") return null;\n const raw = (state as Record<string, unknown>)[SCROLL_STATE_KEY];\n if (!raw || typeof raw !== \"object\") return null;\n const left = (raw as Record<string, unknown>).left;\n const top = (raw as Record<string, unknown>).top;\n if (typeof left !== \"number\" || typeof top !== \"number\") return null;\n return { left, top };\n}\n\nfunction readPositionFromState(state: unknown): number | null {\n if (!state || typeof state !== \"object\") return null;\n const raw = (state as Record<string, unknown>)[POSITION_STATE_KEY];\n return typeof raw === \"number\" ? raw : null;\n}\n\nfunction buildHistoryState(\n prev: unknown,\n scroll: ScrollPosition,\n position: number,\n): Record<string, unknown> {\n const base = prev && typeof prev === \"object\"\n ? { ...(prev as Record<string, unknown>) }\n : {};\n base[SCROLL_STATE_KEY] = { left: scroll.left, top: scroll.top };\n base[POSITION_STATE_KEY] = position;\n return base;\n}\n\n// =============================================================================\n// Query / path helpers\n// =============================================================================\n\nfunction parseQuery(search: string): Record<string, string> {\n const result: Record<string, string> = {};\n new URLSearchParams(search).forEach((v, k) => { result[k] = v; });\n return result;\n}\n\nfunction buildQueryString(\n q: Record<string, string | number | boolean | null | undefined>,\n): string {\n const p = new URLSearchParams();\n for (const [k, v] of Object.entries(q)) {\n if (v != null && v !== false) p.set(k, String(v));\n }\n const s = p.toString();\n return s ? \"?\" + s : \"\";\n}\n\nfunction parseSegments(fullPath: string): Segment[] {\n if (fullPath === \"*\") return [{ kind: \"wildcard\" }];\n return fullPath\n .split(\"/\")\n .filter(Boolean)\n .map((part): Segment => {\n if (part === \"*\") return { kind: \"wildcard\" };\n if (part.startsWith(\":\")) return { kind: \"param\", name: part.slice(1) };\n return { kind: \"literal\", value: part };\n });\n}\n\nfunction joinPaths(parent: string, child: string): string {\n if (child === \"*\") return parent === \"\" ? \"*\" : parent + \"/*\";\n const segment = child.startsWith(\"/\") ? child : \"/\" + child;\n return (parent + segment).replace(/\\/+/g, \"/\") || \"/\";\n}\n\nfunction flattenRoutes(\n routes: RouteRecord[],\n parentPath = \"\",\n parentChain: Array<(() => ElurTemplate | ElurComponent) | undefined> = [],\n): FlatRoute[] {\n const result: FlatRoute[] = [];\n for (const route of routes) {\n const fullPath = joinPaths(parentPath, route.path);\n const chain = [...parentChain, route.component];\n const segments = parseSegments(fullPath);\n result.push({\n fullPath,\n segments,\n chain,\n name: route.name,\n meta: route.meta,\n beforeEnter: route.beforeEnter,\n record: route,\n });\n if (route.children?.length) {\n result.push(...flattenRoutes(route.children, fullPath, chain));\n }\n }\n return result;\n}\n\nfunction tryMatch(path: string, route: FlatRoute): Record<string, string> | null {\n const parts = path.split(\"/\").filter(Boolean);\n const segs = route.segments;\n if (segs.length === 1 && segs[0].kind === \"wildcard\") return {};\n const lastIsWild = segs.length > 0 && segs[segs.length - 1].kind === \"wildcard\";\n const fixedSegs = lastIsWild ? segs.slice(0, -1) : segs;\n if (lastIsWild) {\n if (parts.length < fixedSegs.length) return null;\n } else {\n if (parts.length !== fixedSegs.length) return null;\n }\n const params: Record<string, string> = {};\n for (let i = 0; i < fixedSegs.length; i++) {\n const seg = fixedSegs[i];\n if (seg.kind === \"literal\") {\n if (parts[i] !== seg.value) return null;\n } else if (seg.kind === \"param\") {\n try {\n params[seg.name] = decodeURIComponent(parts[i] ?? \"\");\n } catch {\n params[seg.name] = parts[i] ?? \"\";\n }\n }\n }\n return params;\n}\n\nfunction specificity(route: FlatRoute): number {\n return route.segments.reduce((acc, seg) => {\n if (seg.kind === \"literal\") return acc + 2;\n if (seg.kind === \"param\") return acc + 1;\n return acc;\n }, 0);\n}\n\nfunction matchFlat(\n path: string,\n flat: FlatRoute[],\n): { route: FlatRoute; params: Record<string, string> } | undefined {\n let best: FlatRoute | undefined;\n let bestParams: Record<string, string> = {};\n let bestScore = -1;\n for (const route of flat) {\n const params = tryMatch(path, route);\n if (params === null) continue;\n const score = specificity(route);\n if (score > bestScore) {\n best = route;\n bestParams = params;\n bestScore = score;\n }\n }\n return best ? { route: best, params: bestParams } : undefined;\n}\n\n// =============================================================================\n// Base path helpers\n// =============================================================================\n\nfunction normalizeBase(raw: string): string {\n let b = raw.trim();\n if (!b || b === \"/\") return \"\";\n if (!b.startsWith(\"/\")) b = \"/\" + b;\n if (b.endsWith(\"/\")) b = b.slice(0, -1);\n return b;\n}\n\nfunction detectBase(): string {\n if (typeof document === \"undefined\") return \"\";\n const baseEl = document.querySelector(\"base\");\n if (!baseEl) return \"\";\n const href = baseEl.getAttribute(\"href\") || \"\";\n try {\n const url = new URL(href, window.location.origin);\n return normalizeBase(url.pathname);\n } catch {\n return normalizeBase(href);\n }\n}\n\n// =============================================================================\n// Guard result normalization\n// =============================================================================\n\n/**\n * Normalize any guard result into the internal flow's {allow, redirect} shape.\n * Accepts: `true`, `false`, `void`/`undefined`, `string`, `{ redirect: string }`.\n */\nfunction normalizeGuardResult(\n r: NavigationGuardResult,\n): { allow: boolean; redirect?: string } {\n if (r === false) return { allow: false };\n if (r === true || r === undefined || r === null) return { allow: true };\n if (typeof r === \"string\") return { allow: false, redirect: r };\n if (typeof r === \"object\" && \"redirect\" in r && typeof r.redirect === \"string\") {\n return { allow: false, redirect: r.redirect };\n }\n // Unknown — be permissive rather than break navigation.\n return { allow: true };\n}\n\n// =============================================================================\n// createRouter\n// =============================================================================\n\nexport function createRouter(routes: RouteRecord[], options?: RouterOptions): Router {\n const _base = options?.base != null ? normalizeBase(options.base) : detectBase();\n const _mode: RouterMode = options?.mode ?? \"history\";\n const _isHashMode = _mode === \"hash\";\n const _scrollBehavior = options?.scrollBehavior;\n const _hashScrollPositions = new Map<string, ScrollPosition>();\n let _ignoreNextHashChange = false;\n\n function normalizeAppPath(raw: string): string {\n if (!raw) return \"/\";\n return raw.startsWith(\"/\") ? raw : \"/\" + raw;\n }\n\n function stripBase(rawPath: string): string {\n const path = normalizeAppPath(rawPath || \"/\");\n if (_base && path.startsWith(_base)) {\n const stripped = path.slice(_base.length);\n return stripped === \"\" ? \"/\" : normalizeAppPath(stripped);\n }\n return path;\n }\n\n function withBase(appPath: string): string {\n const p = normalizeAppPath(appPath);\n if (!_base) return p;\n return (_base + p).replace(/\\/+/g, \"/\") || \"/\";\n }\n\n function readHashLocation(): { pathname: string; search: string } {\n let raw = window.location.hash || \"\";\n if (raw.startsWith(\"#\")) raw = raw.slice(1);\n if (!raw) return { pathname: \"/\", search: \"\" };\n if (!raw.startsWith(\"/\")) raw = \"/\" + raw;\n const qIdx = raw.indexOf(\"?\");\n const pathname = qIdx === -1 ? raw : raw.slice(0, qIdx);\n const search = qIdx === -1 ? \"\" : raw.slice(qIdx);\n return { pathname: stripBase(pathname), search };\n }\n\n function readLocation(): { pathname: string; search: string } {\n if (_isHashMode) return readHashLocation();\n return {\n pathname: stripBase(window.location.pathname || \"/\"),\n search: window.location.search || \"\",\n };\n }\n\n function buildUrl(pathname: string, stringQuery: Record<string, string>): string {\n const fullPath = withBase(pathname) + buildQueryString(stringQuery);\n return _isHashMode ? \"#\" + fullPath : fullPath;\n }\n\n function routeKey(pathname: string, stringQuery: Record<string, string>): string {\n return normalizeAppPath(pathname) + buildQueryString(stringQuery);\n }\n\n // -------------------------------------------------------------------------\n // Initial state\n // -------------------------------------------------------------------------\n\n const initialLoc = readLocation();\n const initialPath = initialLoc.pathname;\n const initialQuery = parseQuery(initialLoc.search);\n const flat = flattenRoutes(routes);\n\n const _nameIndex = new Map<string, FlatRoute>();\n for (const route of flat) {\n if (!route.name) continue;\n if (_nameIndex.has(route.name)) {\n console.warn(`[Elur Router] Duplicate route name: \"${route.name}\"`);\n }\n _nameIndex.set(route.name, route);\n }\n const initialMatch = matchFlat(initialPath, flat);\n\n const current = signal(initialPath);\n const params = signal<Record<string, string>>(initialMatch?.params ?? {});\n const query = signal<Record<string, string>>(initialQuery);\n\n let _currentPosition = readPositionFromState(history.state) ?? 0;\n\n const intent = signal<NavigationIntent>({\n action: \"initial\",\n direction: \"none\",\n });\n\n const canGoBack = signal<boolean>(_currentPosition > 0);\n\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(initialPath, initialQuery), getCurrentScrollPosition());\n } else {\n history.replaceState(\n buildHistoryState(history.state, getCurrentScrollPosition(), _currentPosition),\n \"\",\n );\n }\n\n // -------------------------------------------------------------------------\n // Scroll\n // -------------------------------------------------------------------------\n\n function _scrollTo(pos: ScrollPosition): void {\n window.scrollTo(pos.left, pos.top);\n }\n\n function _applyScroll(to: string, from: string, savedPosition: ScrollPosition | null): void {\n if (_scrollBehavior) {\n const result = _scrollBehavior(to, from, savedPosition);\n if (!result) return;\n _scrollTo(result);\n return;\n }\n _scrollTo(savedPosition ?? { left: 0, top: 0 });\n }\n\n function _saveCurrentEntryScroll(pathname: string, stringQuery: Record<string, string>): void {\n const pos = getCurrentScrollPosition();\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(pathname, stringQuery), pos);\n return;\n }\n history.replaceState(\n buildHistoryState(history.state, pos, _currentPosition),\n \"\",\n );\n }\n\n // -------------------------------------------------------------------------\n // Guards\n // -------------------------------------------------------------------------\n\n const _guards: NavigationGuard[] = [];\n const _afterHooks: AfterEachHook[] = [];\n let _navGeneration = 0;\n\n function _runGuards(\n to: string,\n from: string,\n routeGuard: NavigationGuard | undefined,\n onCommit: () => void,\n onCancel?: () => void,\n ): void {\n const guards: NavigationGuard[] = [..._guards];\n if (routeGuard) guards.push(routeGuard);\n\n const gen = ++_navGeneration;\n\n if (guards.length === 0) { onCommit(); return; }\n\n let idx = 0;\n function runNext(prev: NavigationGuardResult): void {\n if (gen !== _navGeneration) return;\n\n const norm = normalizeGuardResult(prev);\n if (!norm.allow) {\n if (norm.redirect && norm.redirect !== to) {\n navigate(norm.redirect);\n return;\n }\n if (norm.redirect === to) {\n // Guarding TO the same path — treat as allow to avoid loops\n onCommit();\n return;\n }\n onCancel?.();\n return;\n }\n if (idx >= guards.length) { onCommit(); return; }\n const result = guards[idx++](to, from);\n if (result instanceof Promise) { result.then(runNext); return; }\n runNext(result);\n }\n runNext(undefined);\n }\n\n // -------------------------------------------------------------------------\n // Path / location resolution\n // -------------------------------------------------------------------------\n\n let _hasNavigated = false;\n\n function _parsePath(\n path: string,\n queryObj?: Record<string, string | number | boolean | null | undefined>,\n ): { pathname: string; stringQuery: Record<string, string> } {\n const qIdx = path.indexOf(\"?\");\n const rawPath = qIdx === -1 ? path : path.slice(0, qIdx);\n const pathname = normalizeAppPath(rawPath || \"/\");\n const inlineQ = qIdx === -1 ? {} : parseQuery(path.slice(qIdx));\n const finalQuery = queryObj ? { ...inlineQ, ...queryObj } : inlineQ;\n const stringQuery: Record<string, string> = {};\n for (const [k, v] of Object.entries(finalQuery)) {\n if (v != null && v !== false) stringQuery[k] = String(v);\n }\n return { pathname, stringQuery };\n }\n\n function _resolveNamedPath(location: NamedRouteLocation): string {\n const found = _nameIndex.get(location.name);\n if (!found) {\n throw new Error(`[Elur Router] No route with name \"${location.name}\"`);\n }\n const parts = found.segments.map((seg) => {\n if (seg.kind === \"literal\") return seg.value;\n if (seg.kind === \"wildcard\") return \"\";\n const value = location.params?.[seg.name];\n if (value == null) {\n throw new Error(\n `[Elur Router] Missing param \"${seg.name}\" for route \"${location.name}\"`,\n );\n }\n return encodeURIComponent(String(value));\n });\n return \"/\" + parts.filter(Boolean).join(\"/\");\n }\n\n function _resolveLocation(\n location: RouteLocation,\n options?: NavigateOptions,\n ): { pathname: string; stringQuery: Record<string, string> } {\n if (typeof location === \"string\") {\n return _parsePath(location, options?.query);\n }\n const pathname = _resolveNamedPath(location);\n const mergedQuery = { ...(location.query ?? {}), ...(options?.query ?? {}) };\n return _parsePath(pathname, mergedQuery);\n }\n\n // -------------------------------------------------------------------------\n // Popstate / hashchange listener\n // -------------------------------------------------------------------------\n\n if (_currentPopstateCleanup) {\n _currentPopstateCleanup();\n _currentPopstateCleanup = null;\n }\n\n const handleBrowserNav = (\n p: string,\n newQuery: Record<string, string>,\n savedPos: ScrollPosition | null,\n nextPosition: number | null,\n onCancelRestore: (from: string, fromQuery: Record<string, string>) => void,\n ) => {\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(p, flat);\n\n let direction: NavigationDirection = \"none\";\n if (nextPosition != null) {\n if (nextPosition < _currentPosition) direction = \"back\";\n else if (nextPosition > _currentPosition) direction = \"forward\";\n }\n\n _runGuards(\n p,\n from,\n m?.route.beforeEnter,\n () => {\n if (nextPosition != null) _currentPosition = nextPosition;\n const animation = _pendingPopAnimation;\n _pendingPopAnimation = undefined;\n intent.value = { action: \"pop\", direction, animation };\n params.value = m?.params ?? {};\n query.value = newQuery;\n current.value = p;\n canGoBack.value = _currentPosition > 0;\n _applyScroll(p, from, savedPos);\n for (const hook of _afterHooks) {\n try { hook(p, from); } catch { /* ignore */ }\n }\n },\n () => onCancelRestore(from, fromQuery),\n );\n };\n\n if (_isHashMode) {\n const onHashChange = () => {\n if (_ignoreNextHashChange) {\n _ignoreNextHashChange = false;\n return;\n }\n const loc = readLocation();\n const nextQuery = parseQuery(loc.search);\n const savedPos = _hashScrollPositions.get(routeKey(loc.pathname, nextQuery)) ?? null;\n handleBrowserNav(\n loc.pathname,\n nextQuery,\n savedPos,\n null,\n (from, fromQuery) => {\n _ignoreNextHashChange = true;\n window.location.hash = buildUrl(from, fromQuery).slice(1);\n queueMicrotask(() => { _ignoreNextHashChange = false; });\n },\n );\n };\n window.addEventListener(\"hashchange\", onHashChange);\n _currentPopstateCleanup = () => window.removeEventListener(\"hashchange\", onHashChange);\n } else {\n const onPopstate = (ev: PopStateEvent) => {\n const loc = readLocation();\n const nextQuery = parseQuery(loc.search);\n const savedPos = readScrollPositionFromState(ev.state ?? history.state);\n const nextPos = readPositionFromState(ev.state ?? history.state);\n handleBrowserNav(\n loc.pathname,\n nextQuery,\n savedPos,\n nextPos,\n (from, fromQuery) => {\n history.pushState(\n buildHistoryState({}, getCurrentScrollPosition(), _currentPosition),\n \"\",\n buildUrl(from, fromQuery),\n );\n },\n );\n };\n window.addEventListener(\"popstate\", onPopstate);\n _currentPopstateCleanup = () => window.removeEventListener(\"popstate\", onPopstate);\n }\n\n // -------------------------------------------------------------------------\n // Internal commit (programmatic navigation)\n // -------------------------------------------------------------------------\n\n function _commit(\n pathname: string,\n stringQuery: Record<string, string>,\n from: string,\n fromQuery: Record<string, string>,\n m: ReturnType<typeof matchFlat>,\n nextIntent: NavigationIntent,\n useReplace: boolean,\n ): void {\n if (!useReplace) {\n _saveCurrentEntryScroll(from, fromQuery);\n _currentPosition += 1;\n }\n\n intent.value = nextIntent;\n params.value = m?.params ?? {};\n query.value = stringQuery;\n current.value = pathname;\n canGoBack.value = _currentPosition > 0;\n\n const url = buildUrl(pathname, stringQuery);\n\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(pathname, stringQuery), { left: 0, top: 0 });\n if (useReplace) {\n history.replaceState(history.state, \"\", url);\n } else {\n _ignoreNextHashChange = true;\n window.location.hash = url.slice(1);\n queueMicrotask(() => { _ignoreNextHashChange = false; });\n }\n } else {\n const nextState = buildHistoryState({}, { left: 0, top: 0 }, _currentPosition);\n if (useReplace) {\n history.replaceState(nextState, \"\", url);\n } else {\n history.pushState(nextState, \"\", url);\n }\n }\n\n _applyScroll(pathname, from, null);\n for (const hook of _afterHooks) {\n try { hook(pathname, from); } catch { /* ignore */ }\n }\n }\n\n // -------------------------------------------------------------------------\n // Public navigation API\n // -------------------------------------------------------------------------\n\n let _pendingPopAnimation: unknown = undefined;\n\n function navigate(location: RouteLocation, options?: NavigateOptions): void {\n _hasNavigated = true;\n const { pathname, stringQuery } = _resolveLocation(location, options);\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(pathname, flat);\n\n const nextIntent: NavigationIntent = {\n action: \"push\",\n direction: options?.direction ?? \"forward\",\n animation: options?.animation,\n };\n\n _runGuards(\n pathname,\n from,\n m?.route.beforeEnter,\n () => _commit(pathname, stringQuery, from, fromQuery, m, nextIntent, false),\n );\n }\n\n function replace(location: RouteLocation, options?: NavigateOptions): void {\n _hasNavigated = true;\n const { pathname, stringQuery } = _resolveLocation(location, options);\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(pathname, flat);\n\n const nextIntent: NavigationIntent = {\n action: \"replace\",\n direction: options?.direction ?? \"root\",\n animation: options?.animation,\n };\n\n _runGuards(\n pathname,\n from,\n m?.route.beforeEnter,\n () => _commit(pathname, stringQuery, from, fromQuery, m, nextIntent, true),\n );\n }\n\n function back(animation?: unknown): void {\n if (animation !== undefined) _pendingPopAnimation = animation;\n history.back();\n }\n\n function forward(animation?: unknown): void {\n if (animation !== undefined) _pendingPopAnimation = animation;\n history.forward();\n }\n\n function go(delta: number): void { history.go(delta); }\n\n function isActive(path: string, exact = true): boolean {\n const cur = current.value;\n if (exact) return cur === path;\n return cur === path || cur.startsWith(path.endsWith(\"/\") ? path : path + \"/\");\n }\n\n function resolve(path: string): ResolvedRoute {\n const m = matchFlat(path, flat);\n if (!m) return { matched: false, params: {}, route: undefined };\n return { matched: true, params: m.params, route: m.route.record };\n }\n\n function beforeEach(guard: NavigationGuard): () => void {\n _guards.push(guard);\n return () => {\n const idx = _guards.indexOf(guard);\n if (idx !== -1) _guards.splice(idx, 1);\n };\n }\n\n function afterEach(hook: AfterEachHook): () => void {\n _afterHooks.push(hook);\n return () => {\n const idx = _afterHooks.indexOf(hook);\n if (idx !== -1) _afterHooks.splice(idx, 1);\n };\n }\n\n const router: RouterInternal = {\n current, params, query, intent, canGoBack,\n base: _base || \"/\",\n navigate, replace, back, forward, go,\n isActive, resolve,\n beforeEach, afterEach, routes,\n _flat: flat, _guards, _base, _mode,\n };\n\n if (_currentRouter) {\n console.warn(\n \"[elur] A router already exists. The previous router is being replaced. \" +\n \"Only one router instance should be active at a time.\",\n );\n }\n _currentRouter = router;\n\n queueMicrotask(() => {\n if (_hasNavigated) return;\n\n const m = matchFlat(initialPath, flat);\n _runGuards(\n initialPath,\n \"\",\n m?.route.beforeEnter,\n () => { /* allowed */ },\n () => {\n const fallback = \"/\";\n const url = buildUrl(fallback, {});\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(fallback, {}), { left: 0, top: 0 });\n history.replaceState(history.state, \"\", url);\n } else {\n history.replaceState(\n buildHistoryState({}, { left: 0, top: 0 }, _currentPosition),\n \"\",\n url,\n );\n }\n const fm = matchFlat(fallback, flat);\n intent.value = { action: \"replace\", direction: \"root\" };\n current.value = fallback;\n params.value = fm?.params ?? {};\n query.value = {};\n canGoBack.value = _currentPosition > 0;\n _applyScroll(fallback, initialPath, null);\n },\n );\n });\n\n return router;\n}\n\nexport function elurRouter(): Router {\n const injected = inject(RouterKey);\n if (injected) return injected;\n return getRouter();\n}\n\n/** @internal */\nexport function _resetRouter(): void {\n if (_currentPopstateCleanup) {\n _currentPopstateCleanup();\n _currentPopstateCleanup = null;\n }\n _currentRouter = null;\n _mountedRouters.length = 0;\n}\n\nexport class RouterView extends ElurComponent {\n private _depth: number;\n private _router?: RouterInternal;\n\n constructor(depth = 0, router?: Router) {\n super();\n this._depth = depth;\n this._router = router as RouterInternal | undefined;\n }\n\n render(): ElurTemplate {\n const depth = this._depth;\n const explicitRouter = this._router;\n return html`<div class=\"router-view\">${() => {\n const router = explicitRouter ?? elurRouter() as RouterInternal;\n const matched = matchFlat(router.current.value, router._flat);\n if (!matched) {\n return html`\n <div style=\"color:#f87171;padding:16px 0\">\n 404 — Route not found: <strong>${router.current.value}</strong>\n </div>\n `;\n }\n if (depth >= matched.route.chain.length) {\n return html`\n <span></span>\n `;\n }\n const factory = matched.route.chain[depth];\n // chain entries can be undefined when the route was registered without\n // a `component` (typical when an outlet auto-bootstraps the router).\n // In that case there's nothing to render at this depth.\n if (!factory) return html`\n <span></span>\n `;\n return factory();\n }}</div>`;\n }\n}\n\nexport class Link extends ElurComponent {\n private _to: string;\n private _label: string;\n private _router?: RouterInternal;\n\n constructor(to: string, label: string, router?: Router) {\n super();\n this._to = to;\n this._label = label;\n this._router = router as RouterInternal | undefined;\n }\n\n render(): ElurTemplate {\n const to = this._to;\n const label = this._label;\n const router = this._router ?? elurRouter() as RouterInternal;\n const appPath = to.startsWith(\"/\") ? to : \"/\" + to;\n const fullPath = (router._base ? (router._base + appPath) : appPath).replace(/\\/+/g, \"/\");\n const href = router._mode === \"hash\" ? \"#\" + fullPath : fullPath;\n return html`\n <a href=${href} style=${() => router.current.value === to\n ? \"color:#38bdf8;font-weight:700;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px;background:#0c2a3a\"\n : \"color:#a3a3a3;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px\"} @click=${(e: Event) => { e.preventDefault(); router.navigate(to); }}>${label}</a>\n `;\n }\n}\n\nexport interface _RouterDebugInternal {\n mode: RouterMode;\n base: string;\n currentPath: string;\n params: Record<string, string>;\n query: Record<string, string>;\n matchedPath: string | null;\n activeGuards: { globalCount: number; hasRouteGuard: boolean; names: string[] };\n}\n\n/** @internal Register a router that was injected via mount({ router }). */\nexport function _debugRegisterRouter(router: Router): void {\n const idx = _mountedRouters.indexOf(router);\n if (idx >= 0) _mountedRouters.splice(idx, 1);\n _mountedRouters.push(router);\n}\n\n/** @internal Unregister a router when its mount point is unmounted. */\nexport function _debugUnregisterRouter(router: Router): void {\n const idx = _mountedRouters.indexOf(router);\n if (idx >= 0) _mountedRouters.splice(idx, 1);\n}\n\nexport function _debugGetRouterInternal(): _RouterDebugInternal | null {\n // Prefer the most recently mounted injected router over the global singleton\n // so devtools inspect the router actually active in the current UI.\n const router = _mountedRouters.length\n ? _mountedRouters[_mountedRouters.length - 1]\n : _currentRouter;\n if (!router) return null;\n const internal = router as RouterInternal;\n const currentPath = internal.current.value;\n const matched = matchFlat(currentPath, internal._flat);\n const routeGuard = matched?.route.beforeEnter;\n const names = internal._guards.map((g, idx) => g.name || `beforeEach#${idx + 1}`);\n if (routeGuard) names.push(routeGuard.name || \"beforeEnter\");\n return {\n mode: internal._mode,\n base: internal._base || \"/\",\n currentPath,\n params: { ...internal.params.value },\n query: { ...internal.query.value },\n matchedPath: matched?.route.fullPath ?? null,\n activeGuards: {\n globalCount: internal._guards.length,\n hasRouteGuard: Boolean(routeGuard),\n names,\n },\n };\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.js","names":[],"sources":["../../src/elur/router.ts"],"sourcesContent":["import { signal } from \"./reactivity.js\";\nimport type { Signal } from \"./reactivity.js\";\nimport { ElurComponent } from \"./lifecycle.js\";\nimport type { ElurTemplate } from \"./template/index.js\";\nimport { html } from \"./template/index.js\";\nimport { inject } from \"./context.js\";\nimport { RouterKey, _mountedRouters, _debugRegisterRouter, _debugUnregisterRouter } from \"./router-registry.js\";\n\n// =============================================================================\n// Public types\n// =============================================================================\n\n/**\n * Value returned (or resolved) by a navigation guard.\n *\n * - `true` / `void` / `undefined` — allow.\n * - `false` — cancel (no redirect).\n * - `string` — redirect to that path.\n * - `{ redirect: string }` — redirect, object form.\n *\n * The object form exists so guards written for the outlet API can be reused\n * verbatim by the core. See `elur-ionic`'s `GuardResult`.\n */\nexport type NavigationGuardResult =\n | void\n | undefined\n | boolean\n | string\n | { redirect: string };\n\n/** Guard function invoked before navigation commits. */\nexport type NavigationGuard = (\n to: string,\n from: string,\n) => NavigationGuardResult | Promise<NavigationGuardResult>;\n\nexport interface RouteRecord {\n /** Optional unique name to enable named navigation. */\n name?: string;\n /** Route path segment. Supports literals, params (`:id`), and wildcards (`*`). */\n path: string;\n /**\n * Factory returning the view for this route level.\n *\n * OPTIONAL — when the core router is auto-bootstrapped by an outlet\n * (Ionic's IonRouterOutlet, future others), the outlet owns component\n * mounting and the core never invokes this. In that case, omit it.\n */\n component?: () => ElurTemplate | ElurComponent;\n /** Optional arbitrary metadata for guards, layouts, and auth checks. */\n meta?: Record<string, unknown>;\n /** Child routes. Paths are joined with the parent. */\n children?: RouteRecord[];\n /** Route-level guard. Runs only when entering this specific route. */\n beforeEnter?: NavigationGuard;\n}\n\n/** Callback for `afterEach` hooks — receives the committed `to` and `from` paths. */\nexport type AfterEachHook = (to: string, from: string) => void;\n\n/** Named route target for programmatic navigation. */\nexport interface NamedRouteLocation {\n name: string;\n params?: Record<string, string | number>;\n query?: Record<string, string | number | boolean | null | undefined>;\n}\n\n/** Navigation input accepted by `navigate` / `replace`. */\nexport type RouteLocation = string | NamedRouteLocation;\n\n/** Serializable scroll position used by the router for history restoration. */\nexport interface ScrollPosition {\n left: number;\n top: number;\n}\n\nexport type ScrollBehavior = (\n to: string,\n from: string,\n savedPosition: ScrollPosition | null,\n) => ScrollPosition | false | void;\n\nexport type RouterMode = \"history\" | \"hash\";\n\nexport interface ResolvedRoute {\n matched: boolean;\n params: Record<string, string>;\n route: RouteRecord | undefined;\n}\n\n// -----------------------------------------------------------------------------\n// Navigation intent\n// -----------------------------------------------------------------------------\n\nexport type NavigationAction = \"push\" | \"replace\" | \"pop\" | \"initial\";\nexport type NavigationDirection = \"forward\" | \"back\" | \"root\" | \"none\";\n\nexport interface NavigationIntent {\n action: NavigationAction;\n direction: NavigationDirection;\n animation?: unknown;\n}\n\nexport interface NavigateOptions {\n query?: Record<string, string | number | boolean | null | undefined>;\n direction?: NavigationDirection;\n animation?: unknown;\n}\n\nexport interface RouterOptions {\n base?: string;\n mode?: RouterMode;\n scrollBehavior?: ScrollBehavior;\n}\n\nexport interface Router {\n readonly current: Signal<string>;\n readonly params: Signal<Record<string, string>>;\n readonly query: Signal<Record<string, string>>;\n readonly base: string;\n readonly intent: Signal<NavigationIntent>;\n readonly canGoBack: Signal<boolean>;\n navigate(location: RouteLocation, options?: NavigateOptions): void;\n replace(location: RouteLocation, options?: NavigateOptions): void;\n back(animation?: unknown): void;\n forward(animation?: unknown): void;\n go(delta: number): void;\n isActive(path: string, exact?: boolean): boolean;\n resolve(path: string): ResolvedRoute;\n readonly routes: RouteRecord[];\n beforeEach(guard: NavigationGuard): () => void;\n afterEach(hook: AfterEachHook): () => void;\n}\n\n// RouterKey and the mounted-router debug registry live in router-registry.ts\n// so that component.ts (mount) can use them without importing this module —\n// keeping the router out of bundles that never call createRouter().\nexport { RouterKey, _debugRegisterRouter, _debugUnregisterRouter };\n\n// =============================================================================\n// Internals\n// =============================================================================\n\ntype Segment =\n | { kind: \"literal\"; value: string }\n | { kind: \"param\"; name: string }\n | { kind: \"wildcard\" };\n\ninterface FlatRoute {\n fullPath: string;\n segments: Segment[];\n chain: Array<(() => ElurTemplate | ElurComponent) | undefined>;\n name?: string;\n meta?: Record<string, unknown>;\n beforeEnter?: NavigationGuard;\n record: RouteRecord;\n}\n\ninterface RouterInternal extends Router {\n _flat: FlatRoute[];\n _guards: NavigationGuard[];\n _base: string;\n _mode: RouterMode;\n}\n\nlet _currentRouter: RouterInternal | null = null;\nlet _currentPopstateCleanup: (() => void) | null = null;\n\nconst SCROLL_STATE_KEY = \"__elur_scroll\";\nconst POSITION_STATE_KEY = \"__elur_pos\";\n\nfunction getRouter(): RouterInternal {\n if (!_currentRouter) {\n throw new Error(\n \"[elur] No active router. Call createRouter() first, \" +\n \"or instantiate an outlet that auto-bootstraps one (e.g. IonRouterOutlet).\"\n );\n }\n return _currentRouter;\n}\n\n/**\n * @internal Whether a router is currently active. Used by outlets that\n * want to auto-bootstrap if the user didn't call `createRouter()` themselves.\n */\nexport function _hasActiveRouter(): boolean {\n return _currentRouter !== null;\n}\n\n// =============================================================================\n// History state helpers\n// =============================================================================\n\nfunction getCurrentScrollPosition(): ScrollPosition {\n return {\n left: window.scrollX ?? window.pageXOffset ?? 0,\n top: window.scrollY ?? window.pageYOffset ?? 0,\n };\n}\n\nfunction readScrollPositionFromState(state: unknown): ScrollPosition | null {\n if (!state || typeof state !== \"object\") return null;\n const raw = (state as Record<string, unknown>)[SCROLL_STATE_KEY];\n if (!raw || typeof raw !== \"object\") return null;\n const left = (raw as Record<string, unknown>).left;\n const top = (raw as Record<string, unknown>).top;\n if (typeof left !== \"number\" || typeof top !== \"number\") return null;\n return { left, top };\n}\n\nfunction readPositionFromState(state: unknown): number | null {\n if (!state || typeof state !== \"object\") return null;\n const raw = (state as Record<string, unknown>)[POSITION_STATE_KEY];\n return typeof raw === \"number\" ? raw : null;\n}\n\nfunction buildHistoryState(\n prev: unknown,\n scroll: ScrollPosition,\n position: number,\n): Record<string, unknown> {\n const base = prev && typeof prev === \"object\"\n ? { ...(prev as Record<string, unknown>) }\n : {};\n base[SCROLL_STATE_KEY] = { left: scroll.left, top: scroll.top };\n base[POSITION_STATE_KEY] = position;\n return base;\n}\n\n// =============================================================================\n// Query / path helpers\n// =============================================================================\n\nfunction parseQuery(search: string): Record<string, string> {\n const result: Record<string, string> = {};\n new URLSearchParams(search).forEach((v, k) => { result[k] = v; });\n return result;\n}\n\nfunction buildQueryString(\n q: Record<string, string | number | boolean | null | undefined>,\n): string {\n const p = new URLSearchParams();\n for (const [k, v] of Object.entries(q)) {\n if (v != null && v !== false) p.set(k, String(v));\n }\n const s = p.toString();\n return s ? \"?\" + s : \"\";\n}\n\nfunction parseSegments(fullPath: string): Segment[] {\n if (fullPath === \"*\") return [{ kind: \"wildcard\" }];\n return fullPath\n .split(\"/\")\n .filter(Boolean)\n .map((part): Segment => {\n if (part === \"*\") return { kind: \"wildcard\" };\n if (part.startsWith(\":\")) return { kind: \"param\", name: part.slice(1) };\n return { kind: \"literal\", value: part };\n });\n}\n\nfunction joinPaths(parent: string, child: string): string {\n if (child === \"*\") return parent === \"\" ? \"*\" : parent + \"/*\";\n const segment = child.startsWith(\"/\") ? child : \"/\" + child;\n return (parent + segment).replace(/\\/+/g, \"/\") || \"/\";\n}\n\nfunction flattenRoutes(\n routes: RouteRecord[],\n parentPath = \"\",\n parentChain: Array<(() => ElurTemplate | ElurComponent) | undefined> = [],\n): FlatRoute[] {\n const result: FlatRoute[] = [];\n for (const route of routes) {\n const fullPath = joinPaths(parentPath, route.path);\n const chain = [...parentChain, route.component];\n const segments = parseSegments(fullPath);\n result.push({\n fullPath,\n segments,\n chain,\n name: route.name,\n meta: route.meta,\n beforeEnter: route.beforeEnter,\n record: route,\n });\n if (route.children?.length) {\n result.push(...flattenRoutes(route.children, fullPath, chain));\n }\n }\n return result;\n}\n\nfunction tryMatch(path: string, route: FlatRoute): Record<string, string> | null {\n const parts = path.split(\"/\").filter(Boolean);\n const segs = route.segments;\n if (segs.length === 1 && segs[0].kind === \"wildcard\") return {};\n const lastIsWild = segs.length > 0 && segs[segs.length - 1].kind === \"wildcard\";\n const fixedSegs = lastIsWild ? segs.slice(0, -1) : segs;\n if (lastIsWild) {\n if (parts.length < fixedSegs.length) return null;\n } else {\n if (parts.length !== fixedSegs.length) return null;\n }\n const params: Record<string, string> = {};\n for (let i = 0; i < fixedSegs.length; i++) {\n const seg = fixedSegs[i];\n if (seg.kind === \"literal\") {\n if (parts[i] !== seg.value) return null;\n } else if (seg.kind === \"param\") {\n try {\n params[seg.name] = decodeURIComponent(parts[i] ?? \"\");\n } catch {\n params[seg.name] = parts[i] ?? \"\";\n }\n }\n }\n return params;\n}\n\nfunction specificity(route: FlatRoute): number {\n return route.segments.reduce((acc, seg) => {\n if (seg.kind === \"literal\") return acc + 2;\n if (seg.kind === \"param\") return acc + 1;\n return acc;\n }, 0);\n}\n\nfunction matchFlat(\n path: string,\n flat: FlatRoute[],\n): { route: FlatRoute; params: Record<string, string> } | undefined {\n let best: FlatRoute | undefined;\n let bestParams: Record<string, string> = {};\n let bestScore = -1;\n for (const route of flat) {\n const params = tryMatch(path, route);\n if (params === null) continue;\n const score = specificity(route);\n if (score > bestScore) {\n best = route;\n bestParams = params;\n bestScore = score;\n }\n }\n return best ? { route: best, params: bestParams } : undefined;\n}\n\n// =============================================================================\n// Base path helpers\n// =============================================================================\n\nfunction normalizeBase(raw: string): string {\n let b = raw.trim();\n if (!b || b === \"/\") return \"\";\n if (!b.startsWith(\"/\")) b = \"/\" + b;\n if (b.endsWith(\"/\")) b = b.slice(0, -1);\n return b;\n}\n\nfunction detectBase(): string {\n if (typeof document === \"undefined\") return \"\";\n const baseEl = document.querySelector(\"base\");\n if (!baseEl) return \"\";\n const href = baseEl.getAttribute(\"href\") || \"\";\n try {\n const url = new URL(href, window.location.origin);\n return normalizeBase(url.pathname);\n } catch {\n return normalizeBase(href);\n }\n}\n\n// =============================================================================\n// Guard result normalization\n// =============================================================================\n\n/**\n * Normalize any guard result into the internal flow's {allow, redirect} shape.\n * Accepts: `true`, `false`, `void`/`undefined`, `string`, `{ redirect: string }`.\n */\nfunction normalizeGuardResult(\n r: NavigationGuardResult,\n): { allow: boolean; redirect?: string } {\n if (r === false) return { allow: false };\n if (r === true || r === undefined || r === null) return { allow: true };\n if (typeof r === \"string\") return { allow: false, redirect: r };\n if (typeof r === \"object\" && \"redirect\" in r && typeof r.redirect === \"string\") {\n return { allow: false, redirect: r.redirect };\n }\n // Unknown — be permissive rather than break navigation.\n return { allow: true };\n}\n\n// =============================================================================\n// createRouter\n// =============================================================================\n\nexport function createRouter(routes: RouteRecord[], options?: RouterOptions): Router {\n const _base = options?.base != null ? normalizeBase(options.base) : detectBase();\n const _mode: RouterMode = options?.mode ?? \"history\";\n const _isHashMode = _mode === \"hash\";\n const _scrollBehavior = options?.scrollBehavior;\n const _hashScrollPositions = new Map<string, ScrollPosition>();\n let _ignoreNextHashChange = false;\n\n function normalizeAppPath(raw: string): string {\n if (!raw) return \"/\";\n return raw.startsWith(\"/\") ? raw : \"/\" + raw;\n }\n\n function stripBase(rawPath: string): string {\n const path = normalizeAppPath(rawPath || \"/\");\n if (_base && path.startsWith(_base)) {\n const stripped = path.slice(_base.length);\n return stripped === \"\" ? \"/\" : normalizeAppPath(stripped);\n }\n return path;\n }\n\n function withBase(appPath: string): string {\n const p = normalizeAppPath(appPath);\n if (!_base) return p;\n return (_base + p).replace(/\\/+/g, \"/\") || \"/\";\n }\n\n function readHashLocation(): { pathname: string; search: string } {\n let raw = window.location.hash || \"\";\n if (raw.startsWith(\"#\")) raw = raw.slice(1);\n if (!raw) return { pathname: \"/\", search: \"\" };\n if (!raw.startsWith(\"/\")) raw = \"/\" + raw;\n const qIdx = raw.indexOf(\"?\");\n const pathname = qIdx === -1 ? raw : raw.slice(0, qIdx);\n const search = qIdx === -1 ? \"\" : raw.slice(qIdx);\n return { pathname: stripBase(pathname), search };\n }\n\n function readLocation(): { pathname: string; search: string } {\n if (_isHashMode) return readHashLocation();\n return {\n pathname: stripBase(window.location.pathname || \"/\"),\n search: window.location.search || \"\",\n };\n }\n\n function buildUrl(pathname: string, stringQuery: Record<string, string>): string {\n const fullPath = withBase(pathname) + buildQueryString(stringQuery);\n return _isHashMode ? \"#\" + fullPath : fullPath;\n }\n\n function routeKey(pathname: string, stringQuery: Record<string, string>): string {\n return normalizeAppPath(pathname) + buildQueryString(stringQuery);\n }\n\n // -------------------------------------------------------------------------\n // Initial state\n // -------------------------------------------------------------------------\n\n const initialLoc = readLocation();\n const initialPath = initialLoc.pathname;\n const initialQuery = parseQuery(initialLoc.search);\n const flat = flattenRoutes(routes);\n\n const _nameIndex = new Map<string, FlatRoute>();\n for (const route of flat) {\n if (!route.name) continue;\n if (_nameIndex.has(route.name)) {\n console.warn(`[Elur Router] Duplicate route name: \"${route.name}\"`);\n }\n _nameIndex.set(route.name, route);\n }\n const initialMatch = matchFlat(initialPath, flat);\n\n const current = signal(initialPath);\n const params = signal<Record<string, string>>(initialMatch?.params ?? {});\n const query = signal<Record<string, string>>(initialQuery);\n\n let _currentPosition = readPositionFromState(history.state) ?? 0;\n\n const intent = signal<NavigationIntent>({\n action: \"initial\",\n direction: \"none\",\n });\n\n const canGoBack = signal<boolean>(_currentPosition > 0);\n\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(initialPath, initialQuery), getCurrentScrollPosition());\n } else {\n history.replaceState(\n buildHistoryState(history.state, getCurrentScrollPosition(), _currentPosition),\n \"\",\n );\n }\n\n // -------------------------------------------------------------------------\n // Scroll\n // -------------------------------------------------------------------------\n\n function _scrollTo(pos: ScrollPosition): void {\n window.scrollTo(pos.left, pos.top);\n }\n\n function _applyScroll(to: string, from: string, savedPosition: ScrollPosition | null): void {\n if (_scrollBehavior) {\n const result = _scrollBehavior(to, from, savedPosition);\n if (!result) return;\n _scrollTo(result);\n return;\n }\n _scrollTo(savedPosition ?? { left: 0, top: 0 });\n }\n\n function _saveCurrentEntryScroll(pathname: string, stringQuery: Record<string, string>): void {\n const pos = getCurrentScrollPosition();\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(pathname, stringQuery), pos);\n return;\n }\n history.replaceState(\n buildHistoryState(history.state, pos, _currentPosition),\n \"\",\n );\n }\n\n // -------------------------------------------------------------------------\n // Guards\n // -------------------------------------------------------------------------\n\n const _guards: NavigationGuard[] = [];\n const _afterHooks: AfterEachHook[] = [];\n let _navGeneration = 0;\n\n function _runGuards(\n to: string,\n from: string,\n routeGuard: NavigationGuard | undefined,\n onCommit: () => void,\n onCancel?: () => void,\n ): void {\n const guards: NavigationGuard[] = [..._guards];\n if (routeGuard) guards.push(routeGuard);\n\n const gen = ++_navGeneration;\n\n if (guards.length === 0) { onCommit(); return; }\n\n let idx = 0;\n function runNext(prev: NavigationGuardResult): void {\n if (gen !== _navGeneration) return;\n\n const norm = normalizeGuardResult(prev);\n if (!norm.allow) {\n if (norm.redirect && norm.redirect !== to) {\n navigate(norm.redirect);\n return;\n }\n if (norm.redirect === to) {\n // Guarding TO the same path — treat as allow to avoid loops\n onCommit();\n return;\n }\n onCancel?.();\n return;\n }\n if (idx >= guards.length) { onCommit(); return; }\n const result = guards[idx++](to, from);\n if (result instanceof Promise) { result.then(runNext); return; }\n runNext(result);\n }\n runNext(undefined);\n }\n\n // -------------------------------------------------------------------------\n // Path / location resolution\n // -------------------------------------------------------------------------\n\n let _hasNavigated = false;\n\n function _parsePath(\n path: string,\n queryObj?: Record<string, string | number | boolean | null | undefined>,\n ): { pathname: string; stringQuery: Record<string, string> } {\n const qIdx = path.indexOf(\"?\");\n const rawPath = qIdx === -1 ? path : path.slice(0, qIdx);\n const pathname = normalizeAppPath(rawPath || \"/\");\n const inlineQ = qIdx === -1 ? {} : parseQuery(path.slice(qIdx));\n const finalQuery = queryObj ? { ...inlineQ, ...queryObj } : inlineQ;\n const stringQuery: Record<string, string> = {};\n for (const [k, v] of Object.entries(finalQuery)) {\n if (v != null && v !== false) stringQuery[k] = String(v);\n }\n return { pathname, stringQuery };\n }\n\n function _resolveNamedPath(location: NamedRouteLocation): string {\n const found = _nameIndex.get(location.name);\n if (!found) {\n throw new Error(`[Elur Router] No route with name \"${location.name}\"`);\n }\n const parts = found.segments.map((seg) => {\n if (seg.kind === \"literal\") return seg.value;\n if (seg.kind === \"wildcard\") return \"\";\n const value = location.params?.[seg.name];\n if (value == null) {\n throw new Error(\n `[Elur Router] Missing param \"${seg.name}\" for route \"${location.name}\"`,\n );\n }\n return encodeURIComponent(String(value));\n });\n return \"/\" + parts.filter(Boolean).join(\"/\");\n }\n\n function _resolveLocation(\n location: RouteLocation,\n options?: NavigateOptions,\n ): { pathname: string; stringQuery: Record<string, string> } {\n if (typeof location === \"string\") {\n return _parsePath(location, options?.query);\n }\n const pathname = _resolveNamedPath(location);\n const mergedQuery = { ...(location.query ?? {}), ...(options?.query ?? {}) };\n return _parsePath(pathname, mergedQuery);\n }\n\n // -------------------------------------------------------------------------\n // Popstate / hashchange listener\n // -------------------------------------------------------------------------\n\n if (_currentPopstateCleanup) {\n _currentPopstateCleanup();\n _currentPopstateCleanup = null;\n }\n\n const handleBrowserNav = (\n p: string,\n newQuery: Record<string, string>,\n savedPos: ScrollPosition | null,\n nextPosition: number | null,\n onCancelRestore: (from: string, fromQuery: Record<string, string>) => void,\n ) => {\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(p, flat);\n\n let direction: NavigationDirection = \"none\";\n if (nextPosition != null) {\n if (nextPosition < _currentPosition) direction = \"back\";\n else if (nextPosition > _currentPosition) direction = \"forward\";\n }\n\n _runGuards(\n p,\n from,\n m?.route.beforeEnter,\n () => {\n if (nextPosition != null) _currentPosition = nextPosition;\n const animation = _pendingPopAnimation;\n _pendingPopAnimation = undefined;\n intent.value = { action: \"pop\", direction, animation };\n params.value = m?.params ?? {};\n query.value = newQuery;\n current.value = p;\n canGoBack.value = _currentPosition > 0;\n _applyScroll(p, from, savedPos);\n for (const hook of _afterHooks) {\n try { hook(p, from); } catch { /* ignore */ }\n }\n },\n () => onCancelRestore(from, fromQuery),\n );\n };\n\n if (_isHashMode) {\n const onHashChange = () => {\n if (_ignoreNextHashChange) {\n _ignoreNextHashChange = false;\n return;\n }\n const loc = readLocation();\n const nextQuery = parseQuery(loc.search);\n const savedPos = _hashScrollPositions.get(routeKey(loc.pathname, nextQuery)) ?? null;\n handleBrowserNav(\n loc.pathname,\n nextQuery,\n savedPos,\n null,\n (from, fromQuery) => {\n _ignoreNextHashChange = true;\n window.location.hash = buildUrl(from, fromQuery).slice(1);\n queueMicrotask(() => { _ignoreNextHashChange = false; });\n },\n );\n };\n window.addEventListener(\"hashchange\", onHashChange);\n _currentPopstateCleanup = () => window.removeEventListener(\"hashchange\", onHashChange);\n } else {\n const onPopstate = (ev: PopStateEvent) => {\n const loc = readLocation();\n const nextQuery = parseQuery(loc.search);\n const savedPos = readScrollPositionFromState(ev.state ?? history.state);\n const nextPos = readPositionFromState(ev.state ?? history.state);\n handleBrowserNav(\n loc.pathname,\n nextQuery,\n savedPos,\n nextPos,\n (from, fromQuery) => {\n history.pushState(\n buildHistoryState({}, getCurrentScrollPosition(), _currentPosition),\n \"\",\n buildUrl(from, fromQuery),\n );\n },\n );\n };\n window.addEventListener(\"popstate\", onPopstate);\n _currentPopstateCleanup = () => window.removeEventListener(\"popstate\", onPopstate);\n }\n\n // -------------------------------------------------------------------------\n // Internal commit (programmatic navigation)\n // -------------------------------------------------------------------------\n\n function _commit(\n pathname: string,\n stringQuery: Record<string, string>,\n from: string,\n fromQuery: Record<string, string>,\n m: ReturnType<typeof matchFlat>,\n nextIntent: NavigationIntent,\n useReplace: boolean,\n ): void {\n if (!useReplace) {\n _saveCurrentEntryScroll(from, fromQuery);\n _currentPosition += 1;\n }\n\n intent.value = nextIntent;\n params.value = m?.params ?? {};\n query.value = stringQuery;\n current.value = pathname;\n canGoBack.value = _currentPosition > 0;\n\n const url = buildUrl(pathname, stringQuery);\n\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(pathname, stringQuery), { left: 0, top: 0 });\n if (useReplace) {\n history.replaceState(history.state, \"\", url);\n } else {\n _ignoreNextHashChange = true;\n window.location.hash = url.slice(1);\n queueMicrotask(() => { _ignoreNextHashChange = false; });\n }\n } else {\n const nextState = buildHistoryState({}, { left: 0, top: 0 }, _currentPosition);\n if (useReplace) {\n history.replaceState(nextState, \"\", url);\n } else {\n history.pushState(nextState, \"\", url);\n }\n }\n\n _applyScroll(pathname, from, null);\n for (const hook of _afterHooks) {\n try { hook(pathname, from); } catch { /* ignore */ }\n }\n }\n\n // -------------------------------------------------------------------------\n // Public navigation API\n // -------------------------------------------------------------------------\n\n let _pendingPopAnimation: unknown = undefined;\n\n function navigate(location: RouteLocation, options?: NavigateOptions): void {\n _hasNavigated = true;\n const { pathname, stringQuery } = _resolveLocation(location, options);\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(pathname, flat);\n\n const nextIntent: NavigationIntent = {\n action: \"push\",\n direction: options?.direction ?? \"forward\",\n animation: options?.animation,\n };\n\n _runGuards(\n pathname,\n from,\n m?.route.beforeEnter,\n () => _commit(pathname, stringQuery, from, fromQuery, m, nextIntent, false),\n );\n }\n\n function replace(location: RouteLocation, options?: NavigateOptions): void {\n _hasNavigated = true;\n const { pathname, stringQuery } = _resolveLocation(location, options);\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(pathname, flat);\n\n const nextIntent: NavigationIntent = {\n action: \"replace\",\n direction: options?.direction ?? \"root\",\n animation: options?.animation,\n };\n\n _runGuards(\n pathname,\n from,\n m?.route.beforeEnter,\n () => _commit(pathname, stringQuery, from, fromQuery, m, nextIntent, true),\n );\n }\n\n function back(animation?: unknown): void {\n if (animation !== undefined) _pendingPopAnimation = animation;\n history.back();\n }\n\n function forward(animation?: unknown): void {\n if (animation !== undefined) _pendingPopAnimation = animation;\n history.forward();\n }\n\n function go(delta: number): void { history.go(delta); }\n\n function isActive(path: string, exact = true): boolean {\n const cur = current.value;\n if (exact) return cur === path;\n return cur === path || cur.startsWith(path.endsWith(\"/\") ? path : path + \"/\");\n }\n\n function resolve(path: string): ResolvedRoute {\n const m = matchFlat(path, flat);\n if (!m) return { matched: false, params: {}, route: undefined };\n return { matched: true, params: m.params, route: m.route.record };\n }\n\n function beforeEach(guard: NavigationGuard): () => void {\n _guards.push(guard);\n return () => {\n const idx = _guards.indexOf(guard);\n if (idx !== -1) _guards.splice(idx, 1);\n };\n }\n\n function afterEach(hook: AfterEachHook): () => void {\n _afterHooks.push(hook);\n return () => {\n const idx = _afterHooks.indexOf(hook);\n if (idx !== -1) _afterHooks.splice(idx, 1);\n };\n }\n\n const router: RouterInternal = {\n current, params, query, intent, canGoBack,\n base: _base || \"/\",\n navigate, replace, back, forward, go,\n isActive, resolve,\n beforeEach, afterEach, routes,\n _flat: flat, _guards, _base, _mode,\n };\n\n if (_currentRouter) {\n console.warn(\n \"[elur] A router already exists. The previous router is being replaced. \" +\n \"Only one router instance should be active at a time.\",\n );\n }\n _currentRouter = router;\n\n queueMicrotask(() => {\n if (_hasNavigated) return;\n\n const m = matchFlat(initialPath, flat);\n _runGuards(\n initialPath,\n \"\",\n m?.route.beforeEnter,\n () => { /* allowed */ },\n () => {\n const fallback = \"/\";\n const url = buildUrl(fallback, {});\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(fallback, {}), { left: 0, top: 0 });\n history.replaceState(history.state, \"\", url);\n } else {\n history.replaceState(\n buildHistoryState({}, { left: 0, top: 0 }, _currentPosition),\n \"\",\n url,\n );\n }\n const fm = matchFlat(fallback, flat);\n intent.value = { action: \"replace\", direction: \"root\" };\n current.value = fallback;\n params.value = fm?.params ?? {};\n query.value = {};\n canGoBack.value = _currentPosition > 0;\n _applyScroll(fallback, initialPath, null);\n },\n );\n });\n\n return router;\n}\n\nexport function elurRouter(): Router {\n const injected = inject(RouterKey);\n if (injected) return injected;\n return getRouter();\n}\n\n/** @internal */\nexport function _resetRouter(): void {\n if (_currentPopstateCleanup) {\n _currentPopstateCleanup();\n _currentPopstateCleanup = null;\n }\n _currentRouter = null;\n _mountedRouters.length = 0;\n}\n\nexport class RouterView extends ElurComponent {\n private _depth: number;\n private _router?: RouterInternal;\n\n constructor(depth = 0, router?: Router) {\n super();\n this._depth = depth;\n this._router = router as RouterInternal | undefined;\n }\n\n render(): ElurTemplate {\n const depth = this._depth;\n const explicitRouter = this._router;\n return html`<div class=\"router-view\">${() => {\n const router = explicitRouter ?? elurRouter() as RouterInternal;\n const matched = matchFlat(router.current.value, router._flat);\n if (!matched) {\n return html`\n <div style=\"color:#f87171;padding:16px 0\">\n 404 — Route not found: <strong>${router.current.value}</strong>\n </div>\n `;\n }\n if (depth >= matched.route.chain.length) {\n return html`\n <span></span>\n `;\n }\n const factory = matched.route.chain[depth];\n // chain entries can be undefined when the route was registered without\n // a `component` (typical when an outlet auto-bootstraps the router).\n // In that case there's nothing to render at this depth.\n if (!factory) return html`\n <span></span>\n `;\n return factory();\n }}</div>`;\n }\n}\n\nexport class Link extends ElurComponent {\n private _to: string;\n private _label: string;\n private _router?: RouterInternal;\n\n constructor(to: string, label: string, router?: Router) {\n super();\n this._to = to;\n this._label = label;\n this._router = router as RouterInternal | undefined;\n }\n\n render(): ElurTemplate {\n const to = this._to;\n const label = this._label;\n const router = this._router ?? elurRouter() as RouterInternal;\n const appPath = to.startsWith(\"/\") ? to : \"/\" + to;\n const fullPath = (router._base ? (router._base + appPath) : appPath).replace(/\\/+/g, \"/\");\n const href = router._mode === \"hash\" ? \"#\" + fullPath : fullPath;\n return html`\n <a href=${href} style=${() => router.current.value === to\n ? \"color:#38bdf8;font-weight:700;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px;background:#0c2a3a\"\n : \"color:#a3a3a3;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px\"} @click=${(e: Event) => { e.preventDefault(); router.navigate(to); }}>${label}</a>\n `;\n }\n}\n\nexport interface _RouterDebugInternal {\n mode: RouterMode;\n base: string;\n currentPath: string;\n params: Record<string, string>;\n query: Record<string, string>;\n matchedPath: string | null;\n activeGuards: { globalCount: number; hasRouteGuard: boolean; names: string[] };\n}\n\nexport function _debugGetRouterInternal(): _RouterDebugInternal | null {\n // Prefer the most recently mounted injected router over the global singleton\n // so devtools inspect the router actually active in the current UI.\n const router = _mountedRouters.length\n ? _mountedRouters[_mountedRouters.length - 1]\n : _currentRouter;\n if (!router) return null;\n const internal = router as RouterInternal;\n const currentPath = internal.current.value;\n const matched = matchFlat(currentPath, internal._flat);\n const routeGuard = matched?.route.beforeEnter;\n const names = internal._guards.map((g, idx) => g.name || `beforeEach#${idx + 1}`);\n if (routeGuard) names.push(routeGuard.name || \"beforeEnter\");\n return {\n mode: internal._mode,\n base: internal._base || \"/\",\n currentPath,\n params: { ...internal.params.value },\n query: { ...internal.query.value },\n matchedPath: matched?.route.fullPath ?? null,\n activeGuards: {\n globalCount: internal._guards.length,\n hasRouteGuard: Boolean(routeGuard),\n names,\n },\n };\n}"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`./types.cjs`),t=require(`./bindings.cjs`);function n(e,t){let n=new Uint8Array(e.length),r=``;for(let i=0;i<e.length;i++){let a=e[i];if(n[i]===1&&(a[0]===`"`||a[0]===`'`)&&(a=a.slice(1)),i<t.length){let e=t[i];if(e.type===`node`)r+=a+`<!--elur-${i}-->`;else if(e.type===`event`){let t=`@${e.modifiers.length?`${e.eventName}.${e.modifiers.join(`.`)}`:e.eventName}=`.length+ +!!e.hadOpenQuote;r+=a.slice(0,-t)+` data-elur-e-${i}="${e.eventName}"`,e.hadOpenQuote&&(n[i+1]=1)}else{let t=`${e.attrName}=`.length+ +!!e.hadOpenQuote;r+=a.slice(0,-t)+` data-elur-a-${i}="${e.attrName}"`,e.hadOpenQuote&&(n[i+1]=1)}}else r+=a}return r}var r=new WeakMap,i=new WeakMap;function a(a,...o){let s=r.get(a);if(!s){let e=[],n=``;for(let r=0;r<a.length-1;r++)n+=a[r],e.push(t.detectContext(n)),n+=`__elur__`;s={contexts:e},r.set(a,s)}let c=s.contexts,l={version:1,strings:a,values:o,contexts:c};function u(){let e=i.get(a);if(e)return e;if(typeof document>`u`)throw Error(`[elur] DOM rendering requires a document. Use
|
|
1
|
+
const e=require(`./types.cjs`),t=require(`./bindings.cjs`);function n(e,t){let n=new Uint8Array(e.length),r=``;for(let i=0;i<e.length;i++){let a=e[i];if(n[i]===1&&(a[0]===`"`||a[0]===`'`)&&(a=a.slice(1)),i<t.length){let e=t[i];if(e.type===`node`)r+=a+`<!--elur-${i}-->`;else if(e.type===`event`){let t=`@${e.modifiers.length?`${e.eventName}.${e.modifiers.join(`.`)}`:e.eventName}=`.length+ +!!e.hadOpenQuote;r+=a.slice(0,-t)+` data-elur-e-${i}="${e.eventName}"`,e.hadOpenQuote&&(n[i+1]=1)}else{let t=`${e.attrName}=`.length+ +!!e.hadOpenQuote;r+=a.slice(0,-t)+` data-elur-a-${i}="${e.attrName}"`,e.hadOpenQuote&&(n[i+1]=1)}}else r+=a}return r}var r=new WeakMap,i=new WeakMap;function a(a,...o){let s=r.get(a);if(!s){let e=[],n=``;for(let r=0;r<a.length-1;r++)n+=a[r],e.push(t.detectContext(n)),n+=`__elur__`;s={contexts:e},r.set(a,s)}let c=s.contexts,l={version:1,strings:a,values:o,contexts:c};function u(){let e=i.get(a);if(e)return e;if(typeof document>`u`)throw Error(`[elur] DOM rendering requires a document. Use @elurjs/core/server on the server.`);let t=document.createElement(`template`);t.innerHTML=n(a,c);let r=Array(c.length).fill(null),o=t.content,s=document.createTreeWalker(o,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_COMMENT),l=0,u;for(;u=s.nextNode();)if(l++,u.nodeType===8){let e=u.nodeValue;if(e&&e.startsWith(`elur-`)){let t=parseInt(e.slice(5),10);isNaN(t)||(r[t]={nodeIndex:l})}}else if(u.nodeType===1){let e=u,t=Array.from(e.attributes);for(let n=0;n<t.length;n++){let i=t[n],a=i.name;if(a.startsWith(`data-elur-e-`)){let t=parseInt(a.slice(12),10);isNaN(t)||(r[t]={nodeIndex:l,name:i.value}),e.removeAttribute(a);continue}if(a.startsWith(`data-elur-a-`)){let t=parseInt(a.slice(12),10);isNaN(t)||(r[t]={nodeIndex:l,name:i.value}),e.removeAttribute(a)}}}return e={tpl:t,pathMap:r},i.set(a,e),e}function d(e,n){let{tpl:r,pathMap:i}=u(),a=r.content.cloneNode(!0),{disposes:s,postMountHooks:l}=t.activateBindings(a,c,o,i),d=document.createTextNode(``),f=document.createTextNode(``);return e.insertBefore(d,n),e.insertBefore(a,n),e.insertBefore(f,n),l.forEach(e=>e()),()=>{for(let e=s.length-1;e>=0;e--)s[e]();let e=d.nextSibling;for(;e&&e!==f;){let t=e.nextSibling;e.parentNode?.removeChild(e),e=t}d.parentNode?.removeChild(d),f.parentNode?.removeChild(f)}}return{__isElurTemplate:!0,[e.ELUR_TEMPLATE_DESCRIPTOR]:l,_render:d,mount(e){let t=typeof e==`string`?document.querySelector(e):e;if(!t)throw Error(`[elur] mount: contenedor no encontrado: ${e}`);let n=d(t,null);return{unmount(){n()}}}}}exports.buildHTML=n,exports.html=a;
|
|
2
2
|
//# sourceMappingURL=html.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.cjs","names":[],"sources":["../../../../src/elur/template/html.ts"],"sourcesContent":["import { ELUR_TEMPLATE_DESCRIPTOR, type ElurTemplate, type ElurMountHandle, type TemplateDescriptor } from \"./types.js\";\nimport { detectContext, activateBindings } from \"./bindings.js\";\nimport type { BindingContext } from \"./bindings.js\";\n\n// =============================================================================\n// --- Static HTML construction with markers ---\n// =============================================================================\n\n/**\n * Builds the static HTML string, replacing each interpolated value with\n * a comment marker (node), data-elur-e-N (event), or data-elur-a-N (attribute).\n */\nexport function buildHTML(\n strings: readonly string[],\n contexts: BindingContext[]\n): string {\n const skipLeading = new Uint8Array(strings.length);\n let result = \"\";\n\n for (let i = 0; i < strings.length; i++) {\n let s = strings[i];\n\n if (skipLeading[i] === 1 && (s[0] === '\"' || s[0] === \"'\")) {\n s = s.slice(1);\n }\n\n if (i < contexts.length) {\n const ctx = contexts[i];\n\n if (ctx.type === \"node\") {\n result += s + `<!--elur-${i}-->`;\n } else if (ctx.type === \"event\") {\n const full = ctx.modifiers.length\n ? `${ctx.eventName}.${ctx.modifiers.join(\".\")}`\n : ctx.eventName;\n const cut = `@${full}=`.length + (ctx.hadOpenQuote ? 1 : 0);\n result += s.slice(0, -cut) + ` data-elur-e-${i}=\"${ctx.eventName}\"`;\n if (ctx.hadOpenQuote) skipLeading[i + 1] = 1;\n } else {\n const cut =\n `${ctx.attrName}=`.length + (ctx.hadOpenQuote ? 1 : 0);\n result += s.slice(0, -cut) + ` data-elur-a-${i}=\"${ctx.attrName}\"`;\n if (ctx.hadOpenQuote) skipLeading[i + 1] = 1;\n }\n } else {\n result += s;\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// --- Template cache ---\n// =============================================================================\n\ninterface DescriptorCache {\n contexts: BindingContext[];\n}\n\ninterface TemplateCache {\n tpl: HTMLTemplateElement;\n pathMap: Array<{ nodeIndex: number; name?: string } | null>;\n}\n\nconst _descriptorCache = new WeakMap<TemplateStringsArray, DescriptorCache>();\nconst _templateCache = new WeakMap<TemplateStringsArray, TemplateCache>();\n\n// =============================================================================\n// --- html`` tag function ---\n// =============================================================================\n\nexport function html(\n strings: TemplateStringsArray,\n ...values: unknown[]\n): ElurTemplate {\n // NOTE: Partial attribute interpolation (`class=\"btn ${size}\"`) is handled\n // at compile time by @elurjs/vite-plugin-elur. Without the plugin,\n // only full bindings (`class=${value}`) are supported.\n let descriptorCache = _descriptorCache.get(strings);\n if (!descriptorCache) {\n const contexts: BindingContext[] = [];\n let accumulated = \"\";\n for (let i = 0; i < strings.length - 1; i++) {\n accumulated += strings[i];\n contexts.push(detectContext(accumulated));\n accumulated += \"__elur__\";\n }\n descriptorCache = { contexts };\n _descriptorCache.set(strings, descriptorCache);\n }\n\n const contexts = descriptorCache.contexts;\n const descriptor: TemplateDescriptor = { version: 1, strings, values, contexts };\n\n function getTemplateCache(): TemplateCache {\n let cached = _templateCache.get(strings);\n if (cached) return cached;\n if (typeof document === \"undefined\") {\n throw new Error(\"[elur] DOM rendering requires a document. Use elur/server on the server.\");\n }\n\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = buildHTML(strings, contexts);\n const pathMap = new Array<{ nodeIndex: number; name?: string } | null>(contexts.length).fill(null);\n const root = tpl.content;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT);\n let nodeIndex = 0;\n let wNode: Node | null;\n\n while ((wNode = walker.nextNode())) {\n nodeIndex++;\n if (wNode.nodeType === 8) {\n const val = wNode.nodeValue;\n if (val && val.startsWith(\"elur-\")) {\n const idx = parseInt(val.slice(5), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex };\n }\n } else if (wNode.nodeType === 1) {\n const el = wNode as Element;\n const attrs = Array.from(el.attributes);\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i];\n const name = attr.name;\n if (name.startsWith(\"data-elur-e-\")) {\n const idx = parseInt(name.slice(12), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex, name: attr.value };\n el.removeAttribute(name);\n continue;\n }\n if (name.startsWith(\"data-elur-a-\")) {\n const idx = parseInt(name.slice(12), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex, name: attr.value };\n el.removeAttribute(name);\n }\n }\n }\n }\n\n cached = { tpl, pathMap };\n _templateCache.set(strings, cached);\n return cached;\n }\n\n function _render(parent: Node, before: Node | null): () => void {\n const { tpl, pathMap } = getTemplateCache();\n const fragment = tpl.content.cloneNode(true) as DocumentFragment;\n\n const { disposes, postMountHooks } = activateBindings(\n fragment, contexts, values, pathMap\n );\n\n const startMarker = document.createTextNode(\"\");\n const endMarker = document.createTextNode(\"\")\n\n parent.insertBefore(startMarker, before);\n parent.insertBefore(fragment, before);\n parent.insertBefore(endMarker, before);\n\n postMountHooks.forEach((cb) => cb());\n\n return () => {\n for (let i = disposes.length - 1; i >= 0; i--) {\n disposes[i]();\n }\n let node = startMarker.nextSibling;\n while (node && node !== endMarker) {\n const next = node.nextSibling;\n node.parentNode?.removeChild(node);\n node = next;\n }\n startMarker.parentNode?.removeChild(startMarker);\n endMarker.parentNode?.removeChild(endMarker);\n };\n }\n\n const elurTemplate: ElurTemplate = {\n __isElurTemplate: true,\n [ELUR_TEMPLATE_DESCRIPTOR]: descriptor,\n\n _render,\n\n mount(container: Element | string): ElurMountHandle {\n const el =\n typeof container === \"string\"\n ? (document.querySelector(container) as Element)\n : container;\n\n if (!el) {\n throw new Error(`[elur] mount: contenedor no encontrado: ${container}`);\n }\n\n const cleanup = _render(el, null);\n\n return {\n unmount() {\n cleanup();\n },\n };\n },\n };\n\n return elurTemplate;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"html.cjs","names":[],"sources":["../../../../src/elur/template/html.ts"],"sourcesContent":["import { ELUR_TEMPLATE_DESCRIPTOR, type ElurTemplate, type ElurMountHandle, type TemplateDescriptor } from \"./types.js\";\nimport { detectContext, activateBindings } from \"./bindings.js\";\nimport type { BindingContext } from \"./bindings.js\";\n\n// =============================================================================\n// --- Static HTML construction with markers ---\n// =============================================================================\n\n/**\n * Builds the static HTML string, replacing each interpolated value with\n * a comment marker (node), data-elur-e-N (event), or data-elur-a-N (attribute).\n */\nexport function buildHTML(\n strings: readonly string[],\n contexts: BindingContext[]\n): string {\n const skipLeading = new Uint8Array(strings.length);\n let result = \"\";\n\n for (let i = 0; i < strings.length; i++) {\n let s = strings[i];\n\n if (skipLeading[i] === 1 && (s[0] === '\"' || s[0] === \"'\")) {\n s = s.slice(1);\n }\n\n if (i < contexts.length) {\n const ctx = contexts[i];\n\n if (ctx.type === \"node\") {\n result += s + `<!--elur-${i}-->`;\n } else if (ctx.type === \"event\") {\n const full = ctx.modifiers.length\n ? `${ctx.eventName}.${ctx.modifiers.join(\".\")}`\n : ctx.eventName;\n const cut = `@${full}=`.length + (ctx.hadOpenQuote ? 1 : 0);\n result += s.slice(0, -cut) + ` data-elur-e-${i}=\"${ctx.eventName}\"`;\n if (ctx.hadOpenQuote) skipLeading[i + 1] = 1;\n } else {\n const cut =\n `${ctx.attrName}=`.length + (ctx.hadOpenQuote ? 1 : 0);\n result += s.slice(0, -cut) + ` data-elur-a-${i}=\"${ctx.attrName}\"`;\n if (ctx.hadOpenQuote) skipLeading[i + 1] = 1;\n }\n } else {\n result += s;\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// --- Template cache ---\n// =============================================================================\n\ninterface DescriptorCache {\n contexts: BindingContext[];\n}\n\ninterface TemplateCache {\n tpl: HTMLTemplateElement;\n pathMap: Array<{ nodeIndex: number; name?: string } | null>;\n}\n\nconst _descriptorCache = new WeakMap<TemplateStringsArray, DescriptorCache>();\nconst _templateCache = new WeakMap<TemplateStringsArray, TemplateCache>();\n\n// =============================================================================\n// --- html`` tag function ---\n// =============================================================================\n\nexport function html(\n strings: TemplateStringsArray,\n ...values: unknown[]\n): ElurTemplate {\n // NOTE: Partial attribute interpolation (`class=\"btn ${size}\"`) is handled\n // at compile time by @elurjs/vite-plugin-elur. Without the plugin,\n // only full bindings (`class=${value}`) are supported.\n let descriptorCache = _descriptorCache.get(strings);\n if (!descriptorCache) {\n const contexts: BindingContext[] = [];\n let accumulated = \"\";\n for (let i = 0; i < strings.length - 1; i++) {\n accumulated += strings[i];\n contexts.push(detectContext(accumulated));\n accumulated += \"__elur__\";\n }\n descriptorCache = { contexts };\n _descriptorCache.set(strings, descriptorCache);\n }\n\n const contexts = descriptorCache.contexts;\n const descriptor: TemplateDescriptor = { version: 1, strings, values, contexts };\n\n function getTemplateCache(): TemplateCache {\n let cached = _templateCache.get(strings);\n if (cached) return cached;\n if (typeof document === \"undefined\") {\n throw new Error(\"[elur] DOM rendering requires a document. Use @elurjs/core/server on the server.\");\n }\n\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = buildHTML(strings, contexts);\n const pathMap = new Array<{ nodeIndex: number; name?: string } | null>(contexts.length).fill(null);\n const root = tpl.content;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT);\n let nodeIndex = 0;\n let wNode: Node | null;\n\n while ((wNode = walker.nextNode())) {\n nodeIndex++;\n if (wNode.nodeType === 8) {\n const val = wNode.nodeValue;\n if (val && val.startsWith(\"elur-\")) {\n const idx = parseInt(val.slice(5), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex };\n }\n } else if (wNode.nodeType === 1) {\n const el = wNode as Element;\n const attrs = Array.from(el.attributes);\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i];\n const name = attr.name;\n if (name.startsWith(\"data-elur-e-\")) {\n const idx = parseInt(name.slice(12), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex, name: attr.value };\n el.removeAttribute(name);\n continue;\n }\n if (name.startsWith(\"data-elur-a-\")) {\n const idx = parseInt(name.slice(12), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex, name: attr.value };\n el.removeAttribute(name);\n }\n }\n }\n }\n\n cached = { tpl, pathMap };\n _templateCache.set(strings, cached);\n return cached;\n }\n\n function _render(parent: Node, before: Node | null): () => void {\n const { tpl, pathMap } = getTemplateCache();\n const fragment = tpl.content.cloneNode(true) as DocumentFragment;\n\n const { disposes, postMountHooks } = activateBindings(\n fragment, contexts, values, pathMap\n );\n\n const startMarker = document.createTextNode(\"\");\n const endMarker = document.createTextNode(\"\")\n\n parent.insertBefore(startMarker, before);\n parent.insertBefore(fragment, before);\n parent.insertBefore(endMarker, before);\n\n postMountHooks.forEach((cb) => cb());\n\n return () => {\n for (let i = disposes.length - 1; i >= 0; i--) {\n disposes[i]();\n }\n let node = startMarker.nextSibling;\n while (node && node !== endMarker) {\n const next = node.nextSibling;\n node.parentNode?.removeChild(node);\n node = next;\n }\n startMarker.parentNode?.removeChild(startMarker);\n endMarker.parentNode?.removeChild(endMarker);\n };\n }\n\n const elurTemplate: ElurTemplate = {\n __isElurTemplate: true,\n [ELUR_TEMPLATE_DESCRIPTOR]: descriptor,\n\n _render,\n\n mount(container: Element | string): ElurMountHandle {\n const el =\n typeof container === \"string\"\n ? (document.querySelector(container) as Element)\n : container;\n\n if (!el) {\n throw new Error(`[elur] mount: contenedor no encontrado: ${container}`);\n }\n\n const cleanup = _render(el, null);\n\n return {\n unmount() {\n cleanup();\n },\n };\n },\n };\n\n return elurTemplate;\n}\n"],"mappings":"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"}
|
|
@@ -36,7 +36,7 @@ function o(o, ...s) {
|
|
|
36
36
|
function d() {
|
|
37
37
|
let e = a.get(o);
|
|
38
38
|
if (e) return e;
|
|
39
|
-
if (typeof document > "u") throw Error("[elur] DOM rendering requires a document. Use
|
|
39
|
+
if (typeof document > "u") throw Error("[elur] DOM rendering requires a document. Use @elurjs/core/server on the server.");
|
|
40
40
|
let t = document.createElement("template");
|
|
41
41
|
t.innerHTML = r(o, l);
|
|
42
42
|
let n = Array(l.length).fill(null), i = t.content, s = document.createTreeWalker(i, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT), c = 0, u;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.js","names":[],"sources":["../../../../src/elur/template/html.ts"],"sourcesContent":["import { ELUR_TEMPLATE_DESCRIPTOR, type ElurTemplate, type ElurMountHandle, type TemplateDescriptor } from \"./types.js\";\nimport { detectContext, activateBindings } from \"./bindings.js\";\nimport type { BindingContext } from \"./bindings.js\";\n\n// =============================================================================\n// --- Static HTML construction with markers ---\n// =============================================================================\n\n/**\n * Builds the static HTML string, replacing each interpolated value with\n * a comment marker (node), data-elur-e-N (event), or data-elur-a-N (attribute).\n */\nexport function buildHTML(\n strings: readonly string[],\n contexts: BindingContext[]\n): string {\n const skipLeading = new Uint8Array(strings.length);\n let result = \"\";\n\n for (let i = 0; i < strings.length; i++) {\n let s = strings[i];\n\n if (skipLeading[i] === 1 && (s[0] === '\"' || s[0] === \"'\")) {\n s = s.slice(1);\n }\n\n if (i < contexts.length) {\n const ctx = contexts[i];\n\n if (ctx.type === \"node\") {\n result += s + `<!--elur-${i}-->`;\n } else if (ctx.type === \"event\") {\n const full = ctx.modifiers.length\n ? `${ctx.eventName}.${ctx.modifiers.join(\".\")}`\n : ctx.eventName;\n const cut = `@${full}=`.length + (ctx.hadOpenQuote ? 1 : 0);\n result += s.slice(0, -cut) + ` data-elur-e-${i}=\"${ctx.eventName}\"`;\n if (ctx.hadOpenQuote) skipLeading[i + 1] = 1;\n } else {\n const cut =\n `${ctx.attrName}=`.length + (ctx.hadOpenQuote ? 1 : 0);\n result += s.slice(0, -cut) + ` data-elur-a-${i}=\"${ctx.attrName}\"`;\n if (ctx.hadOpenQuote) skipLeading[i + 1] = 1;\n }\n } else {\n result += s;\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// --- Template cache ---\n// =============================================================================\n\ninterface DescriptorCache {\n contexts: BindingContext[];\n}\n\ninterface TemplateCache {\n tpl: HTMLTemplateElement;\n pathMap: Array<{ nodeIndex: number; name?: string } | null>;\n}\n\nconst _descriptorCache = new WeakMap<TemplateStringsArray, DescriptorCache>();\nconst _templateCache = new WeakMap<TemplateStringsArray, TemplateCache>();\n\n// =============================================================================\n// --- html`` tag function ---\n// =============================================================================\n\nexport function html(\n strings: TemplateStringsArray,\n ...values: unknown[]\n): ElurTemplate {\n // NOTE: Partial attribute interpolation (`class=\"btn ${size}\"`) is handled\n // at compile time by @elurjs/vite-plugin-elur. Without the plugin,\n // only full bindings (`class=${value}`) are supported.\n let descriptorCache = _descriptorCache.get(strings);\n if (!descriptorCache) {\n const contexts: BindingContext[] = [];\n let accumulated = \"\";\n for (let i = 0; i < strings.length - 1; i++) {\n accumulated += strings[i];\n contexts.push(detectContext(accumulated));\n accumulated += \"__elur__\";\n }\n descriptorCache = { contexts };\n _descriptorCache.set(strings, descriptorCache);\n }\n\n const contexts = descriptorCache.contexts;\n const descriptor: TemplateDescriptor = { version: 1, strings, values, contexts };\n\n function getTemplateCache(): TemplateCache {\n let cached = _templateCache.get(strings);\n if (cached) return cached;\n if (typeof document === \"undefined\") {\n throw new Error(\"[elur] DOM rendering requires a document. Use elur/server on the server.\");\n }\n\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = buildHTML(strings, contexts);\n const pathMap = new Array<{ nodeIndex: number; name?: string } | null>(contexts.length).fill(null);\n const root = tpl.content;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT);\n let nodeIndex = 0;\n let wNode: Node | null;\n\n while ((wNode = walker.nextNode())) {\n nodeIndex++;\n if (wNode.nodeType === 8) {\n const val = wNode.nodeValue;\n if (val && val.startsWith(\"elur-\")) {\n const idx = parseInt(val.slice(5), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex };\n }\n } else if (wNode.nodeType === 1) {\n const el = wNode as Element;\n const attrs = Array.from(el.attributes);\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i];\n const name = attr.name;\n if (name.startsWith(\"data-elur-e-\")) {\n const idx = parseInt(name.slice(12), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex, name: attr.value };\n el.removeAttribute(name);\n continue;\n }\n if (name.startsWith(\"data-elur-a-\")) {\n const idx = parseInt(name.slice(12), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex, name: attr.value };\n el.removeAttribute(name);\n }\n }\n }\n }\n\n cached = { tpl, pathMap };\n _templateCache.set(strings, cached);\n return cached;\n }\n\n function _render(parent: Node, before: Node | null): () => void {\n const { tpl, pathMap } = getTemplateCache();\n const fragment = tpl.content.cloneNode(true) as DocumentFragment;\n\n const { disposes, postMountHooks } = activateBindings(\n fragment, contexts, values, pathMap\n );\n\n const startMarker = document.createTextNode(\"\");\n const endMarker = document.createTextNode(\"\")\n\n parent.insertBefore(startMarker, before);\n parent.insertBefore(fragment, before);\n parent.insertBefore(endMarker, before);\n\n postMountHooks.forEach((cb) => cb());\n\n return () => {\n for (let i = disposes.length - 1; i >= 0; i--) {\n disposes[i]();\n }\n let node = startMarker.nextSibling;\n while (node && node !== endMarker) {\n const next = node.nextSibling;\n node.parentNode?.removeChild(node);\n node = next;\n }\n startMarker.parentNode?.removeChild(startMarker);\n endMarker.parentNode?.removeChild(endMarker);\n };\n }\n\n const elurTemplate: ElurTemplate = {\n __isElurTemplate: true,\n [ELUR_TEMPLATE_DESCRIPTOR]: descriptor,\n\n _render,\n\n mount(container: Element | string): ElurMountHandle {\n const el =\n typeof container === \"string\"\n ? (document.querySelector(container) as Element)\n : container;\n\n if (!el) {\n throw new Error(`[elur] mount: contenedor no encontrado: ${container}`);\n }\n\n const cleanup = _render(el, null);\n\n return {\n unmount() {\n cleanup();\n },\n };\n },\n };\n\n return elurTemplate;\n}\n"],"mappings":";;;AAYA,SAAgB,EACZ,GACA,GACM;CACN,IAAM,IAAc,IAAI,WAAW,EAAQ,OAAO,EAC9C,IAAS;AAEb,MAAK,IAAI,IAAI,GAAG,IAAI,EAAQ,QAAQ,KAAK;EACrC,IAAI,IAAI,EAAQ;AAMhB,MAJI,EAAY,OAAO,MAAM,EAAE,OAAO,QAAO,EAAE,OAAO,SAClD,IAAI,EAAE,MAAM,EAAE,GAGd,IAAI,EAAS,QAAQ;GACrB,IAAM,IAAM,EAAS;AAErB,OAAI,EAAI,SAAS,OACb,MAAU,IAAI,YAAY,EAAE;YACrB,EAAI,SAAS,SAAS;IAI7B,IAAM,IAAM,IAHC,EAAI,UAAU,SACrB,GAAG,EAAI,UAAU,GAAG,EAAI,UAAU,KAAK,IAAI,KAC3C,EAAI,UACW,GAAG,SAAU,KAAI;AAEtC,IADA,KAAU,EAAE,MAAM,GAAG,CAAC,EAAI,GAAG,gBAAgB,EAAE,IAAI,EAAI,UAAU,IAC7D,EAAI,iBAAc,EAAY,IAAI,KAAK;UACxC;IACH,IAAM,IACF,GAAG,EAAI,SAAS,GAAG,SAAU,KAAI;AAErC,IADA,KAAU,EAAE,MAAM,GAAG,CAAC,EAAI,GAAG,gBAAgB,EAAE,IAAI,EAAI,SAAS,IAC5D,EAAI,iBAAc,EAAY,IAAI,KAAK;;QAG/C,MAAU;;AAIlB,QAAO;;AAgBX,IAAM,oBAAmB,IAAI,SAAgD,EACvE,oBAAiB,IAAI,SAA8C;AAMzE,SAAgB,EACZ,GACA,GAAG,GACS;CAIZ,IAAI,IAAkB,EAAiB,IAAI,EAAQ;AACnD,KAAI,CAAC,GAAiB;EAClB,IAAM,IAA6B,EAAE,EACjC,IAAc;AAClB,OAAK,IAAI,IAAI,GAAG,IAAI,EAAQ,SAAS,GAAG,IAGpC,CAFA,KAAe,EAAQ,IACvB,EAAS,KAAK,EAAc,EAAY,CAAC,EACzC,KAAe;AAGnB,EADA,IAAkB,EAAE,aAAU,EAC9B,EAAiB,IAAI,GAAS,EAAgB;;CAGlD,IAAM,IAAW,EAAgB,UAC3B,IAAiC;EAAE,SAAS;EAAG;EAAS;EAAQ;EAAU;CAEhF,SAAS,IAAkC;EACvC,IAAI,IAAS,EAAe,IAAI,EAAQ;AACxC,MAAI,EAAQ,QAAO;AACnB,MAAI,OAAO,WAAa,IACpB,OAAU,MAAM,2EAA2E;EAG/F,IAAM,IAAM,SAAS,cAAc,WAAW;AAC9C,IAAI,YAAY,EAAU,GAAS,EAAS;EAC5C,IAAM,IAAc,MAAmD,EAAS,OAAO,CAAC,KAAK,KAAK,EAC5F,IAAO,EAAI,SACX,IAAS,SAAS,iBAAiB,GAAM,WAAW,eAAe,WAAW,aAAa,EAC7F,IAAY,GACZ;AAEJ,SAAQ,IAAQ,EAAO,UAAU,EAE7B,KADA,KACI,EAAM,aAAa,GAAG;GACtB,IAAM,IAAM,EAAM;AAClB,OAAI,KAAO,EAAI,WAAW,QAAQ,EAAE;IAChC,IAAM,IAAM,SAAS,EAAI,MAAM,EAAE,EAAE,GAAG;AACtC,IAAK,MAAM,EAAI,KAAE,EAAQ,KAAO,EAAE,cAAW;;aAE1C,EAAM,aAAa,GAAG;GAC7B,IAAM,IAAK,GACL,IAAQ,MAAM,KAAK,EAAG,WAAW;AACvC,QAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAAK;IACnC,IAAM,IAAO,EAAM,IACb,IAAO,EAAK;AAClB,QAAI,EAAK,WAAW,eAAe,EAAE;KACjC,IAAM,IAAM,SAAS,EAAK,MAAM,GAAG,EAAE,GAAG;AAExC,KADK,MAAM,EAAI,KAAE,EAAQ,KAAO;MAAE;MAAW,MAAM,EAAK;MAAO,GAC/D,EAAG,gBAAgB,EAAK;AACxB;;AAEJ,QAAI,EAAK,WAAW,eAAe,EAAE;KACjC,IAAM,IAAM,SAAS,EAAK,MAAM,GAAG,EAAE,GAAG;AAExC,KADK,MAAM,EAAI,KAAE,EAAQ,KAAO;MAAE;MAAW,MAAM,EAAK;MAAO,GAC/D,EAAG,gBAAgB,EAAK;;;;AAQxC,SAFA,IAAS;GAAE;GAAK;GAAS,EACzB,EAAe,IAAI,GAAS,EAAO,EAC5B;;CAGX,SAAS,EAAQ,GAAc,GAAiC;EAC5D,IAAM,EAAE,QAAK,eAAY,GAAkB,EACrC,IAAW,EAAI,QAAQ,UAAU,GAAK,EAEtC,EAAE,aAAU,sBAAmB,EACjC,GAAU,GAAU,GAAQ,EAC/B,EAEK,IAAc,SAAS,eAAe,GAAG,EACzC,IAAY,SAAS,eAAe,GAAG;AAQ7C,SANA,EAAO,aAAa,GAAa,EAAO,EACxC,EAAO,aAAa,GAAU,EAAO,EACrC,EAAO,aAAa,GAAW,EAAO,EAEtC,EAAe,SAAS,MAAO,GAAI,CAAC,QAEvB;AACT,QAAK,IAAI,IAAI,EAAS,SAAS,GAAG,KAAK,GAAG,IACtC,GAAS,IAAI;GAEjB,IAAI,IAAO,EAAY;AACvB,UAAO,KAAQ,MAAS,IAAW;IAC/B,IAAM,IAAO,EAAK;AAElB,IADA,EAAK,YAAY,YAAY,EAAK,EAClC,IAAO;;AAGX,GADA,EAAY,YAAY,YAAY,EAAY,EAChD,EAAU,YAAY,YAAY,EAAU;;;AA8BpD,QA1BmC;EAC/B,kBAAkB;GACjB,IAA2B;EAE5B;EAEA,MAAM,GAA8C;GAChD,IAAM,IACF,OAAO,KAAc,WACd,SAAS,cAAc,EAAU,GAClC;AAEV,OAAI,CAAC,EACD,OAAU,MAAM,2CAA2C,IAAY;GAG3E,IAAM,IAAU,EAAQ,GAAI,KAAK;AAEjC,UAAO,EACH,UAAU;AACN,OAAS;MAEhB;;EAER"}
|
|
1
|
+
{"version":3,"file":"html.js","names":[],"sources":["../../../../src/elur/template/html.ts"],"sourcesContent":["import { ELUR_TEMPLATE_DESCRIPTOR, type ElurTemplate, type ElurMountHandle, type TemplateDescriptor } from \"./types.js\";\nimport { detectContext, activateBindings } from \"./bindings.js\";\nimport type { BindingContext } from \"./bindings.js\";\n\n// =============================================================================\n// --- Static HTML construction with markers ---\n// =============================================================================\n\n/**\n * Builds the static HTML string, replacing each interpolated value with\n * a comment marker (node), data-elur-e-N (event), or data-elur-a-N (attribute).\n */\nexport function buildHTML(\n strings: readonly string[],\n contexts: BindingContext[]\n): string {\n const skipLeading = new Uint8Array(strings.length);\n let result = \"\";\n\n for (let i = 0; i < strings.length; i++) {\n let s = strings[i];\n\n if (skipLeading[i] === 1 && (s[0] === '\"' || s[0] === \"'\")) {\n s = s.slice(1);\n }\n\n if (i < contexts.length) {\n const ctx = contexts[i];\n\n if (ctx.type === \"node\") {\n result += s + `<!--elur-${i}-->`;\n } else if (ctx.type === \"event\") {\n const full = ctx.modifiers.length\n ? `${ctx.eventName}.${ctx.modifiers.join(\".\")}`\n : ctx.eventName;\n const cut = `@${full}=`.length + (ctx.hadOpenQuote ? 1 : 0);\n result += s.slice(0, -cut) + ` data-elur-e-${i}=\"${ctx.eventName}\"`;\n if (ctx.hadOpenQuote) skipLeading[i + 1] = 1;\n } else {\n const cut =\n `${ctx.attrName}=`.length + (ctx.hadOpenQuote ? 1 : 0);\n result += s.slice(0, -cut) + ` data-elur-a-${i}=\"${ctx.attrName}\"`;\n if (ctx.hadOpenQuote) skipLeading[i + 1] = 1;\n }\n } else {\n result += s;\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// --- Template cache ---\n// =============================================================================\n\ninterface DescriptorCache {\n contexts: BindingContext[];\n}\n\ninterface TemplateCache {\n tpl: HTMLTemplateElement;\n pathMap: Array<{ nodeIndex: number; name?: string } | null>;\n}\n\nconst _descriptorCache = new WeakMap<TemplateStringsArray, DescriptorCache>();\nconst _templateCache = new WeakMap<TemplateStringsArray, TemplateCache>();\n\n// =============================================================================\n// --- html`` tag function ---\n// =============================================================================\n\nexport function html(\n strings: TemplateStringsArray,\n ...values: unknown[]\n): ElurTemplate {\n // NOTE: Partial attribute interpolation (`class=\"btn ${size}\"`) is handled\n // at compile time by @elurjs/vite-plugin-elur. Without the plugin,\n // only full bindings (`class=${value}`) are supported.\n let descriptorCache = _descriptorCache.get(strings);\n if (!descriptorCache) {\n const contexts: BindingContext[] = [];\n let accumulated = \"\";\n for (let i = 0; i < strings.length - 1; i++) {\n accumulated += strings[i];\n contexts.push(detectContext(accumulated));\n accumulated += \"__elur__\";\n }\n descriptorCache = { contexts };\n _descriptorCache.set(strings, descriptorCache);\n }\n\n const contexts = descriptorCache.contexts;\n const descriptor: TemplateDescriptor = { version: 1, strings, values, contexts };\n\n function getTemplateCache(): TemplateCache {\n let cached = _templateCache.get(strings);\n if (cached) return cached;\n if (typeof document === \"undefined\") {\n throw new Error(\"[elur] DOM rendering requires a document. Use @elurjs/core/server on the server.\");\n }\n\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = buildHTML(strings, contexts);\n const pathMap = new Array<{ nodeIndex: number; name?: string } | null>(contexts.length).fill(null);\n const root = tpl.content;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT);\n let nodeIndex = 0;\n let wNode: Node | null;\n\n while ((wNode = walker.nextNode())) {\n nodeIndex++;\n if (wNode.nodeType === 8) {\n const val = wNode.nodeValue;\n if (val && val.startsWith(\"elur-\")) {\n const idx = parseInt(val.slice(5), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex };\n }\n } else if (wNode.nodeType === 1) {\n const el = wNode as Element;\n const attrs = Array.from(el.attributes);\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i];\n const name = attr.name;\n if (name.startsWith(\"data-elur-e-\")) {\n const idx = parseInt(name.slice(12), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex, name: attr.value };\n el.removeAttribute(name);\n continue;\n }\n if (name.startsWith(\"data-elur-a-\")) {\n const idx = parseInt(name.slice(12), 10);\n if (!isNaN(idx)) pathMap[idx] = { nodeIndex, name: attr.value };\n el.removeAttribute(name);\n }\n }\n }\n }\n\n cached = { tpl, pathMap };\n _templateCache.set(strings, cached);\n return cached;\n }\n\n function _render(parent: Node, before: Node | null): () => void {\n const { tpl, pathMap } = getTemplateCache();\n const fragment = tpl.content.cloneNode(true) as DocumentFragment;\n\n const { disposes, postMountHooks } = activateBindings(\n fragment, contexts, values, pathMap\n );\n\n const startMarker = document.createTextNode(\"\");\n const endMarker = document.createTextNode(\"\")\n\n parent.insertBefore(startMarker, before);\n parent.insertBefore(fragment, before);\n parent.insertBefore(endMarker, before);\n\n postMountHooks.forEach((cb) => cb());\n\n return () => {\n for (let i = disposes.length - 1; i >= 0; i--) {\n disposes[i]();\n }\n let node = startMarker.nextSibling;\n while (node && node !== endMarker) {\n const next = node.nextSibling;\n node.parentNode?.removeChild(node);\n node = next;\n }\n startMarker.parentNode?.removeChild(startMarker);\n endMarker.parentNode?.removeChild(endMarker);\n };\n }\n\n const elurTemplate: ElurTemplate = {\n __isElurTemplate: true,\n [ELUR_TEMPLATE_DESCRIPTOR]: descriptor,\n\n _render,\n\n mount(container: Element | string): ElurMountHandle {\n const el =\n typeof container === \"string\"\n ? (document.querySelector(container) as Element)\n : container;\n\n if (!el) {\n throw new Error(`[elur] mount: contenedor no encontrado: ${container}`);\n }\n\n const cleanup = _render(el, null);\n\n return {\n unmount() {\n cleanup();\n },\n };\n },\n };\n\n return elurTemplate;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=Symbol.for(
|
|
1
|
+
var e=Symbol.for(`@elurjs/core/template-descriptor`),t=Symbol.for(`@elurjs/core/render-protocol`),n={partialAttributeInterpolation:!1};function r(){return{el:null}}var i={SCOPE:`elur-scope`,ERROR_BOUNDARY:`elur-eb`,TRANSITION:`elur-t`,KEYED_START:`elur-ks`,KEYED_END:`elur-ke`,KEYED_ZONE:`elur-kz`};function a(e){return typeof e==`object`&&!!e&&e.__isElurTemplate===!0}function o(e){return typeof e==`object`&&!!e&&e.__isKeyedList===!0}exports.COMMENT=i,exports.ELUR_RENDER_PROTOCOL=t,exports.ELUR_TEMPLATE_DESCRIPTOR=e,exports.isElurTemplate=a,exports.isKeyedList=o,exports.ref=r,exports.templateFeatures=n;
|
|
2
2
|
//# sourceMappingURL=types.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs","names":[],"sources":["../../../../src/elur/template/types.ts"],"sourcesContent":["// =============================================================================\n// --- Public types ---\n// =============================================================================\n\nexport type TemplateBindingContext =\n | { type: \"node\" }\n | { type: \"event\"; eventName: string; modifiers: string[]; hadOpenQuote: boolean }\n | { type: \"attr\"; attrName: string; hadOpenQuote: boolean; url?: boolean; executable?: boolean };\n\nexport interface TemplateDescriptor {\n readonly version: 1;\n readonly strings: readonly string[];\n readonly values: readonly unknown[];\n readonly contexts: readonly TemplateBindingContext[];\n}\n\nexport interface ServerRenderProtocolContext {\n readonly markers: boolean;\n readonly signal?: AbortSignal;\n readonly context?: unknown;\n render(value: unknown, options?: { markers?: boolean }): Promise<string>;\n}\n\n/** Context passed to a custom value's `mountDom` protocol during client-side mount. */\nexport interface DomProtocolContext {\n readonly parent: Node;\n readonly before: Node | null;\n readonly context?: unknown;\n}\n\n/** Context passed to a custom value's `hydrateDom` protocol during hydration. */\nexport interface HydrationProtocolContext {\n readonly parent: Node;\n readonly bounds: { start: Comment; end: Comment } | null;\n readonly context?: unknown;\n /** Remounts a value inside the current bounds (used as fallback). */\n render(value: unknown): unknown;\n}\n\nexport interface ElurRenderProtocol {\n renderServer?(context: ServerRenderProtocolContext): string | Promise<string>;\n mountDom?(context: DomProtocolContext): (() => void) | void;\n hydrateDom?(context: HydrationProtocolContext): (() => void) | void;\n}\n\nexport const ELUR_TEMPLATE_DESCRIPTOR = Symbol.for(\"
|
|
1
|
+
{"version":3,"file":"types.cjs","names":[],"sources":["../../../../src/elur/template/types.ts"],"sourcesContent":["// =============================================================================\n// --- Public types ---\n// =============================================================================\n\nexport type TemplateBindingContext =\n | { type: \"node\" }\n | { type: \"event\"; eventName: string; modifiers: string[]; hadOpenQuote: boolean }\n | { type: \"attr\"; attrName: string; hadOpenQuote: boolean; url?: boolean; executable?: boolean };\n\nexport interface TemplateDescriptor {\n readonly version: 1;\n readonly strings: readonly string[];\n readonly values: readonly unknown[];\n readonly contexts: readonly TemplateBindingContext[];\n}\n\nexport interface ServerRenderProtocolContext {\n readonly markers: boolean;\n readonly signal?: AbortSignal;\n readonly context?: unknown;\n render(value: unknown, options?: { markers?: boolean }): Promise<string>;\n}\n\n/** Context passed to a custom value's `mountDom` protocol during client-side mount. */\nexport interface DomProtocolContext {\n readonly parent: Node;\n readonly before: Node | null;\n readonly context?: unknown;\n}\n\n/** Context passed to a custom value's `hydrateDom` protocol during hydration. */\nexport interface HydrationProtocolContext {\n readonly parent: Node;\n readonly bounds: { start: Comment; end: Comment } | null;\n readonly context?: unknown;\n /** Remounts a value inside the current bounds (used as fallback). */\n render(value: unknown): unknown;\n}\n\nexport interface ElurRenderProtocol {\n renderServer?(context: ServerRenderProtocolContext): string | Promise<string>;\n mountDom?(context: DomProtocolContext): (() => void) | void;\n hydrateDom?(context: HydrationProtocolContext): (() => void) | void;\n}\n\nexport const ELUR_TEMPLATE_DESCRIPTOR = Symbol.for(\"@elurjs/core/template-descriptor\");\nexport const ELUR_RENDER_PROTOCOL = Symbol.for(\"@elurjs/core/render-protocol\");\n\n/**\n * Runtime feature capabilities, for tooling to detect support without\n * inferring it from version strings.\n *\n * NOTE: Partial attribute interpolation is now handled at compile time by\n * @elurjs/vite-plugin-elur. The core runtime does not support it natively.\n * Use the Vite plugin for partial interpolation support.\n */\nexport const templateFeatures = {\n /** Partial attribute interpolation (`class=\"btn ${size}\"`) is supported. */\n partialAttributeInterpolation: false,\n} as const;\n\nexport interface ElurTemplate {\n readonly __isElurTemplate: true;\n readonly [ELUR_TEMPLATE_DESCRIPTOR]?: TemplateDescriptor;\n readonly [ELUR_RENDER_PROTOCOL]?: ElurRenderProtocol;\n /** Mounts the template into a container element (public / root API). */\n mount(container: Element | string): ElurMountHandle;\n /** @internal Renders before `before` node (or appends to `parent`). Returns cleanup. */\n _render(parent: Node, before: Node | null): () => void;\n}\n\nexport interface ElurMountHandle {\n unmount(): void;\n}\n\n/** Direct reference to a DOM element, assigned on mount and cleared on unmount. */\nexport interface ElurRef<T extends Element = Element> {\n el: T | null;\n}\n\n/** Creates an empty `ElurRef`. Use as `ref` attribute value in templates. */\nexport function ref<T extends Element = Element>(): ElurRef<T> {\n return { el: null };\n}\n\n/** Keyed list result for efficient DOM diffing via `repeat()`. */\nexport interface KeyedList<T = unknown> {\n readonly __isKeyedList: true;\n readonly items: T[];\n readonly keyFn: (item: T, index: number) => string | number;\n readonly renderFn: (item: T, index: number) => ElurTemplate | import(\"../lifecycle.js\").ElurComponent;\n}\n\nexport interface KEntry {\n start: Comment;\n end: Comment;\n cleanup: () => void;\n}\n\n/** Opaque token for a named portal target. */\nexport interface PortalOutlet {\n readonly __isPortalOutlet: true;\n /** @internal */\n _container: Element | null;\n}\n\n/** Fallback: a static template/component, or a factory receiving the error. */\nexport type ErrorFallback =\n | ElurTemplate\n | import(\"../lifecycle.js\").ElurComponent\n | ((err: unknown) => ElurTemplate | import(\"../lifecycle.js\").ElurComponent);\n\n/** Content that can be wrapped with `transition()`. */\nexport type TransitionContent =\n | ElurTemplate\n | import(\"../lifecycle.js\").ElurComponent\n | (() => ElurTemplate | import(\"../lifecycle.js\").ElurComponent | null);\n\nexport const COMMENT = {\n SCOPE: \"elur-scope\",\n ERROR_BOUNDARY: \"elur-eb\",\n TRANSITION: \"elur-t\",\n KEYED_START: \"elur-ks\",\n KEYED_END: \"elur-ke\",\n KEYED_ZONE: \"elur-kz\",\n} as const;\n\n// =============================================================================\n// --- DOM inspection helpers ---\n// =============================================================================\n\nexport function isElurTemplate(v: unknown): v is ElurTemplate {\n return (\n v != null &&\n typeof v === \"object\" &&\n (v as Record<string, unknown>).__isElurTemplate === true\n );\n}\n\nexport function isKeyedList(v: unknown): v is KeyedList {\n return (\n v != null &&\n typeof v === \"object\" &&\n (v as Record<string, unknown>).__isKeyedList === true\n );\n}\n"],"mappings":"AA6CA,IAAa,EAA2B,OAAO,IAAI,mCAAmC,CACzE,EAAuB,OAAO,IAAI,+BAA+B,CAUjE,EAAmB,CAE5B,8BAA+B,GAClC,CAsBD,SAAgB,GAA+C,CAC3D,MAAO,CAAE,GAAI,KAAM,CAoCvB,IAAa,EAAU,CACnB,MAAO,aACP,eAAgB,UAChB,WAAY,SACZ,YAAa,UACb,UAAW,UACX,WAAY,UACf,CAMD,SAAgB,EAAe,EAA+B,CAC1D,OAEI,OAAO,GAAM,YADb,GAEC,EAA8B,mBAAqB,GAI5D,SAAgB,EAAY,EAA4B,CACpD,OAEI,OAAO,GAAM,YADb,GAEC,EAA8B,gBAAkB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/elur/template/types.ts
|
|
2
|
-
var e = Symbol.for("
|
|
2
|
+
var e = Symbol.for("@elurjs/core/template-descriptor"), t = Symbol.for("@elurjs/core/render-protocol"), n = { partialAttributeInterpolation: !1 };
|
|
3
3
|
function r() {
|
|
4
4
|
return { el: null };
|
|
5
5
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/elur/template/types.ts"],"sourcesContent":["// =============================================================================\n// --- Public types ---\n// =============================================================================\n\nexport type TemplateBindingContext =\n | { type: \"node\" }\n | { type: \"event\"; eventName: string; modifiers: string[]; hadOpenQuote: boolean }\n | { type: \"attr\"; attrName: string; hadOpenQuote: boolean; url?: boolean; executable?: boolean };\n\nexport interface TemplateDescriptor {\n readonly version: 1;\n readonly strings: readonly string[];\n readonly values: readonly unknown[];\n readonly contexts: readonly TemplateBindingContext[];\n}\n\nexport interface ServerRenderProtocolContext {\n readonly markers: boolean;\n readonly signal?: AbortSignal;\n readonly context?: unknown;\n render(value: unknown, options?: { markers?: boolean }): Promise<string>;\n}\n\n/** Context passed to a custom value's `mountDom` protocol during client-side mount. */\nexport interface DomProtocolContext {\n readonly parent: Node;\n readonly before: Node | null;\n readonly context?: unknown;\n}\n\n/** Context passed to a custom value's `hydrateDom` protocol during hydration. */\nexport interface HydrationProtocolContext {\n readonly parent: Node;\n readonly bounds: { start: Comment; end: Comment } | null;\n readonly context?: unknown;\n /** Remounts a value inside the current bounds (used as fallback). */\n render(value: unknown): unknown;\n}\n\nexport interface ElurRenderProtocol {\n renderServer?(context: ServerRenderProtocolContext): string | Promise<string>;\n mountDom?(context: DomProtocolContext): (() => void) | void;\n hydrateDom?(context: HydrationProtocolContext): (() => void) | void;\n}\n\nexport const ELUR_TEMPLATE_DESCRIPTOR = Symbol.for(\"
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/elur/template/types.ts"],"sourcesContent":["// =============================================================================\n// --- Public types ---\n// =============================================================================\n\nexport type TemplateBindingContext =\n | { type: \"node\" }\n | { type: \"event\"; eventName: string; modifiers: string[]; hadOpenQuote: boolean }\n | { type: \"attr\"; attrName: string; hadOpenQuote: boolean; url?: boolean; executable?: boolean };\n\nexport interface TemplateDescriptor {\n readonly version: 1;\n readonly strings: readonly string[];\n readonly values: readonly unknown[];\n readonly contexts: readonly TemplateBindingContext[];\n}\n\nexport interface ServerRenderProtocolContext {\n readonly markers: boolean;\n readonly signal?: AbortSignal;\n readonly context?: unknown;\n render(value: unknown, options?: { markers?: boolean }): Promise<string>;\n}\n\n/** Context passed to a custom value's `mountDom` protocol during client-side mount. */\nexport interface DomProtocolContext {\n readonly parent: Node;\n readonly before: Node | null;\n readonly context?: unknown;\n}\n\n/** Context passed to a custom value's `hydrateDom` protocol during hydration. */\nexport interface HydrationProtocolContext {\n readonly parent: Node;\n readonly bounds: { start: Comment; end: Comment } | null;\n readonly context?: unknown;\n /** Remounts a value inside the current bounds (used as fallback). */\n render(value: unknown): unknown;\n}\n\nexport interface ElurRenderProtocol {\n renderServer?(context: ServerRenderProtocolContext): string | Promise<string>;\n mountDom?(context: DomProtocolContext): (() => void) | void;\n hydrateDom?(context: HydrationProtocolContext): (() => void) | void;\n}\n\nexport const ELUR_TEMPLATE_DESCRIPTOR = Symbol.for(\"@elurjs/core/template-descriptor\");\nexport const ELUR_RENDER_PROTOCOL = Symbol.for(\"@elurjs/core/render-protocol\");\n\n/**\n * Runtime feature capabilities, for tooling to detect support without\n * inferring it from version strings.\n *\n * NOTE: Partial attribute interpolation is now handled at compile time by\n * @elurjs/vite-plugin-elur. The core runtime does not support it natively.\n * Use the Vite plugin for partial interpolation support.\n */\nexport const templateFeatures = {\n /** Partial attribute interpolation (`class=\"btn ${size}\"`) is supported. */\n partialAttributeInterpolation: false,\n} as const;\n\nexport interface ElurTemplate {\n readonly __isElurTemplate: true;\n readonly [ELUR_TEMPLATE_DESCRIPTOR]?: TemplateDescriptor;\n readonly [ELUR_RENDER_PROTOCOL]?: ElurRenderProtocol;\n /** Mounts the template into a container element (public / root API). */\n mount(container: Element | string): ElurMountHandle;\n /** @internal Renders before `before` node (or appends to `parent`). Returns cleanup. */\n _render(parent: Node, before: Node | null): () => void;\n}\n\nexport interface ElurMountHandle {\n unmount(): void;\n}\n\n/** Direct reference to a DOM element, assigned on mount and cleared on unmount. */\nexport interface ElurRef<T extends Element = Element> {\n el: T | null;\n}\n\n/** Creates an empty `ElurRef`. Use as `ref` attribute value in templates. */\nexport function ref<T extends Element = Element>(): ElurRef<T> {\n return { el: null };\n}\n\n/** Keyed list result for efficient DOM diffing via `repeat()`. */\nexport interface KeyedList<T = unknown> {\n readonly __isKeyedList: true;\n readonly items: T[];\n readonly keyFn: (item: T, index: number) => string | number;\n readonly renderFn: (item: T, index: number) => ElurTemplate | import(\"../lifecycle.js\").ElurComponent;\n}\n\nexport interface KEntry {\n start: Comment;\n end: Comment;\n cleanup: () => void;\n}\n\n/** Opaque token for a named portal target. */\nexport interface PortalOutlet {\n readonly __isPortalOutlet: true;\n /** @internal */\n _container: Element | null;\n}\n\n/** Fallback: a static template/component, or a factory receiving the error. */\nexport type ErrorFallback =\n | ElurTemplate\n | import(\"../lifecycle.js\").ElurComponent\n | ((err: unknown) => ElurTemplate | import(\"../lifecycle.js\").ElurComponent);\n\n/** Content that can be wrapped with `transition()`. */\nexport type TransitionContent =\n | ElurTemplate\n | import(\"../lifecycle.js\").ElurComponent\n | (() => ElurTemplate | import(\"../lifecycle.js\").ElurComponent | null);\n\nexport const COMMENT = {\n SCOPE: \"elur-scope\",\n ERROR_BOUNDARY: \"elur-eb\",\n TRANSITION: \"elur-t\",\n KEYED_START: \"elur-ks\",\n KEYED_END: \"elur-ke\",\n KEYED_ZONE: \"elur-kz\",\n} as const;\n\n// =============================================================================\n// --- DOM inspection helpers ---\n// =============================================================================\n\nexport function isElurTemplate(v: unknown): v is ElurTemplate {\n return (\n v != null &&\n typeof v === \"object\" &&\n (v as Record<string, unknown>).__isElurTemplate === true\n );\n}\n\nexport function isKeyedList(v: unknown): v is KeyedList {\n return (\n v != null &&\n typeof v === \"object\" &&\n (v as Record<string, unknown>).__isKeyedList === true\n );\n}\n"],"mappings":";AA6CA,IAAa,IAA2B,OAAO,IAAI,mCAAmC,EACzE,IAAuB,OAAO,IAAI,+BAA+B,EAUjE,IAAmB,EAE5B,+BAA+B,IAClC;AAsBD,SAAgB,IAA+C;AAC3D,QAAO,EAAE,IAAI,MAAM;;AAoCvB,IAAa,IAAU;CACnB,OAAO;CACP,gBAAgB;CAChB,YAAY;CACZ,aAAa;CACb,WAAW;CACX,YAAY;CACf;AAMD,SAAgB,EAAe,GAA+B;AAC1D,QAEI,OAAO,KAAM,cADb,KAEC,EAA8B,qBAAqB;;AAI5D,SAAgB,EAAY,GAA4B;AACpD,QAEI,OAAO,KAAM,cADb,KAEC,EAA8B,kBAAkB"}
|
package/dist/lib/elur.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./signals.cjs`),t=require(`./elur/template/types.cjs`),n=require(`./lifecycle.cjs`),r=require(`./context.cjs`),i=require(`./elur/template/keyed.cjs`),a=require(`./elur/template/bindings.cjs`),o=require(`./elur/template/html.cjs`),s=require(`./elur/template/raw.cjs`),c=require(`./elur/template/transitions.cjs`),l=require(`./elur/template/portal.cjs`),u=require(`./elur/template/error-boundary.cjs`),d=require(`./router.cjs`),f=require(`./component.cjs`),p=require(`./store.cjs`),m=require(`./plugins.cjs`),h=require(`./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./signals.cjs`),t=require(`./elur/template/types.cjs`),n=require(`./lifecycle.cjs`),r=require(`./context.cjs`),i=require(`./elur/template/keyed.cjs`),a=require(`./elur/template/bindings.cjs`),o=require(`./elur/template/html.cjs`),s=require(`./elur/template/raw.cjs`),c=require(`./elur/template/transitions.cjs`),l=require(`./elur/template/portal.cjs`),u=require(`./elur/template/error-boundary.cjs`),d=require(`./elur/router-registry.cjs`),f=require(`./component.cjs`),p=require(`./store.cjs`),m=require(`./plugins.cjs`),h=require(`./router.cjs`),g=require(`./async.cjs`),_=require(`./form.cjs`);exports.ELUR_RENDER_PROTOCOL=t.ELUR_RENDER_PROTOCOL,exports.ELUR_TEMPLATE_DESCRIPTOR=t.ELUR_TEMPLATE_DESCRIPTOR,exports.ElurComponent=n.ElurComponent,exports.Link=h.Link,exports.RouterKey=d.RouterKey,exports.RouterView=h.RouterView,exports.Signal=e.Signal,exports._hasActiveRouter=h._hasActiveRouter,exports.batch=e.batch,exports.bridgePlugin=m.bridgePlugin,exports.computed=e.computed,exports.createErrorBoundary=u.createErrorBoundary,exports.createForm=_.createForm,exports.createInjectionKey=r.createInjectionKey,exports.createPortalOutlet=l.createPortalOutlet,exports.createRouter=h.createRouter,exports.createStore=p.createStore,exports.createValidator=_.createValidator,exports.effect=e.effect,exports.elurField=_.elurField,exports.elurFieldArray=_.elurFieldArray,exports.elurRouter=h.elurRouter,exports.email=_.email,exports.extendValidators=_.extendValidators,exports.guardPlugin=m.guardPlugin,exports.html=o.html,exports.inject=r.inject,exports.injectOutlet=l.injectOutlet,exports.lazy=g.lazy,exports.loggerPlugin=m.loggerPlugin,exports.max=_.max,exports.maxLength=_.maxLength,exports.min=_.min,exports.minLength=_.minLength,exports.mount=f.mount,exports.nextTick=e.nextTick,exports.pattern=_.pattern,exports.persistPlugin=m.persistPlugin,exports.portal=l.portal,exports.portalOutlet=l.portalOutlet,exports.provide=r.provide,exports.provideOutlet=l.provideOutlet,exports.raw=s.raw,exports.ref=t.ref,exports.repeat=i.repeat,exports.required=_.required,exports.showWhen=a.showWhen,exports.signal=e.signal,exports.suspend=g.suspend,exports.templateFeatures=t.templateFeatures,exports.transition=c.transition,exports.untrack=e.untrack,exports.validators=_.validators,exports.watch=e.watch;
|
package/dist/lib/elur.js
CHANGED
|
@@ -9,10 +9,11 @@ import { raw as y } from "./elur/template/raw.js";
|
|
|
9
9
|
import { transition as b } from "./elur/template/transitions.js";
|
|
10
10
|
import { createPortalOutlet as x, injectOutlet as S, portal as C, portalOutlet as w, provideOutlet as T } from "./elur/template/portal.js";
|
|
11
11
|
import { createErrorBoundary as E } from "./elur/template/error-boundary.js";
|
|
12
|
-
import {
|
|
13
|
-
import { mount as
|
|
14
|
-
import { createStore as
|
|
15
|
-
import { bridgePlugin as
|
|
12
|
+
import { RouterKey as D } from "./elur/router-registry.js";
|
|
13
|
+
import { mount as O } from "./component.js";
|
|
14
|
+
import { createStore as k } from "./store.js";
|
|
15
|
+
import { bridgePlugin as A, guardPlugin as j, loggerPlugin as M, persistPlugin as N } from "./plugins.js";
|
|
16
|
+
import { Link as P, RouterView as F, _hasActiveRouter as I, createRouter as L, elurRouter as R } from "./router.js";
|
|
16
17
|
import { lazy as z, suspend as B } from "./async.js";
|
|
17
18
|
import { createForm as V, createValidator as H, elurField as U, elurFieldArray as W, email as G, extendValidators as K, max as q, maxLength as J, min as Y, minLength as X, pattern as Z, required as Q, validators as $ } from "./form.js";
|
|
18
|
-
export { c as ELUR_RENDER_PROTOCOL, l as ELUR_TEMPLATE_DESCRIPTOR, f as ElurComponent,
|
|
19
|
+
export { c as ELUR_RENDER_PROTOCOL, l as ELUR_TEMPLATE_DESCRIPTOR, f as ElurComponent, P as Link, D as RouterKey, F as RouterView, e as Signal, I as _hasActiveRouter, t as batch, A as bridgePlugin, n as computed, E as createErrorBoundary, V as createForm, p as createInjectionKey, x as createPortalOutlet, L as createRouter, k as createStore, H as createValidator, r as effect, U as elurField, W as elurFieldArray, R as elurRouter, G as email, K as extendValidators, j as guardPlugin, v as html, m as inject, S as injectOutlet, z as lazy, M as loggerPlugin, q as max, J as maxLength, Y as min, X as minLength, O as mount, i as nextTick, Z as pattern, N as persistPlugin, C as portal, w as portalOutlet, h as provide, T as provideOutlet, y as raw, u as ref, g as repeat, Q as required, _ as showWhen, a as signal, B as suspend, d as templateFeatures, b as transition, o as untrack, $ as validators, s as watch };
|
package/dist/lib/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./signals.cjs`),t=require(`./elur/template/types.cjs`),n=require(`./lifecycle.cjs`),r=require(`./context.cjs`),i=require(`./elur/template/keyed.cjs`),a=require(`./elur/template/bindings.cjs`),o=require(`./elur/template/html.cjs`),s=require(`./elur/template/raw.cjs`),c=require(`./elur/template/transitions.cjs`),l=require(`./elur/template/portal.cjs`),u=require(`./elur/template/error-boundary.cjs`),d=require(`./router.cjs`),f=require(`./component.cjs`),p=require(`./store.cjs`),m=require(`./plugins.cjs`),h=require(`./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./signals.cjs`),t=require(`./elur/template/types.cjs`),n=require(`./lifecycle.cjs`),r=require(`./context.cjs`),i=require(`./elur/template/keyed.cjs`),a=require(`./elur/template/bindings.cjs`),o=require(`./elur/template/html.cjs`),s=require(`./elur/template/raw.cjs`),c=require(`./elur/template/transitions.cjs`),l=require(`./elur/template/portal.cjs`),u=require(`./elur/template/error-boundary.cjs`),d=require(`./elur/router-registry.cjs`),f=require(`./component.cjs`),p=require(`./store.cjs`),m=require(`./plugins.cjs`),h=require(`./router.cjs`),g=require(`./async.cjs`),_=require(`./form.cjs`);exports.ELUR_RENDER_PROTOCOL=t.ELUR_RENDER_PROTOCOL,exports.ELUR_TEMPLATE_DESCRIPTOR=t.ELUR_TEMPLATE_DESCRIPTOR,exports.ElurComponent=n.ElurComponent,exports.Link=h.Link,exports.RouterKey=d.RouterKey,exports.RouterView=h.RouterView,exports.Signal=e.Signal,exports._hasActiveRouter=h._hasActiveRouter,exports.batch=e.batch,exports.bridgePlugin=m.bridgePlugin,exports.computed=e.computed,exports.createErrorBoundary=u.createErrorBoundary,exports.createForm=_.createForm,exports.createInjectionKey=r.createInjectionKey,exports.createPortalOutlet=l.createPortalOutlet,exports.createRouter=h.createRouter,exports.createStore=p.createStore,exports.createValidator=_.createValidator,exports.effect=e.effect,exports.elurField=_.elurField,exports.elurFieldArray=_.elurFieldArray,exports.elurRouter=h.elurRouter,exports.email=_.email,exports.extendValidators=_.extendValidators,exports.guardPlugin=m.guardPlugin,exports.html=o.html,exports.inject=r.inject,exports.injectOutlet=l.injectOutlet,exports.lazy=g.lazy,exports.loggerPlugin=m.loggerPlugin,exports.max=_.max,exports.maxLength=_.maxLength,exports.min=_.min,exports.minLength=_.minLength,exports.mount=f.mount,exports.nextTick=e.nextTick,exports.pattern=_.pattern,exports.persistPlugin=m.persistPlugin,exports.portal=l.portal,exports.portalOutlet=l.portalOutlet,exports.provide=r.provide,exports.provideOutlet=l.provideOutlet,exports.raw=s.raw,exports.ref=t.ref,exports.repeat=i.repeat,exports.required=_.required,exports.showWhen=a.showWhen,exports.signal=e.signal,exports.suspend=g.suspend,exports.templateFeatures=t.templateFeatures,exports.transition=c.transition,exports.untrack=e.untrack,exports.validators=_.validators,exports.watch=e.watch;
|
package/dist/lib/index.js
CHANGED
|
@@ -9,10 +9,11 @@ import { raw as y } from "./elur/template/raw.js";
|
|
|
9
9
|
import { transition as b } from "./elur/template/transitions.js";
|
|
10
10
|
import { createPortalOutlet as x, injectOutlet as S, portal as C, portalOutlet as w, provideOutlet as T } from "./elur/template/portal.js";
|
|
11
11
|
import { createErrorBoundary as E } from "./elur/template/error-boundary.js";
|
|
12
|
-
import {
|
|
13
|
-
import { mount as
|
|
14
|
-
import { createStore as
|
|
15
|
-
import { bridgePlugin as
|
|
12
|
+
import { RouterKey as D } from "./elur/router-registry.js";
|
|
13
|
+
import { mount as O } from "./component.js";
|
|
14
|
+
import { createStore as k } from "./store.js";
|
|
15
|
+
import { bridgePlugin as A, guardPlugin as j, loggerPlugin as M, persistPlugin as N } from "./plugins.js";
|
|
16
|
+
import { Link as P, RouterView as F, _hasActiveRouter as I, createRouter as L, elurRouter as R } from "./router.js";
|
|
16
17
|
import { lazy as z, suspend as B } from "./async.js";
|
|
17
18
|
import { createForm as V, createValidator as H, elurField as U, elurFieldArray as W, email as G, extendValidators as K, max as q, maxLength as J, min as Y, minLength as X, pattern as Z, required as Q, validators as $ } from "./form.js";
|
|
18
|
-
export { c as ELUR_RENDER_PROTOCOL, l as ELUR_TEMPLATE_DESCRIPTOR, f as ElurComponent,
|
|
19
|
+
export { c as ELUR_RENDER_PROTOCOL, l as ELUR_TEMPLATE_DESCRIPTOR, f as ElurComponent, P as Link, D as RouterKey, F as RouterView, e as Signal, I as _hasActiveRouter, t as batch, A as bridgePlugin, n as computed, E as createErrorBoundary, V as createForm, p as createInjectionKey, x as createPortalOutlet, L as createRouter, k as createStore, H as createValidator, r as effect, U as elurField, W as elurFieldArray, R as elurRouter, G as email, K as extendValidators, j as guardPlugin, v as html, m as inject, S as injectOutlet, z as lazy, M as loggerPlugin, q as max, J as maxLength, Y as min, X as minLength, O as mount, i as nextTick, Z as pattern, N as persistPlugin, C as portal, w as portalOutlet, h as provide, T as provideOutlet, y as raw, u as ref, g as repeat, Q as required, _ as showWhen, a as signal, B as suspend, d as templateFeatures, b as transition, o as untrack, $ as validators, s as watch };
|
package/dist/lib/lifecycle.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class{__isElurComponent=!0;children;_debugName;_slots=new Map;setChildren(e){return this.children=e,this}setSlot(e,t){return this._slots.set(e,t),this}slot(e){return this._slots.get(e)}setDebugName(e){return this._debugName=e,this}};function t(e){return typeof e==`object`&&!!e&&e.__isElurComponent===!0}var n=
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class{__isElurComponent=!0;children;_debugName;_slots=new Map;setChildren(e){return this.children=e,this}setSlot(e,t){return this._slots.set(e,t),this}slot(e){return this._slots.get(e)}setDebugName(e){return this._debugName=e,this}};function t(e){return typeof e==`object`&&!!e&&e.__isElurComponent===!0}var n=Symbol.for(`@elurjs/core/reactivity-state`);function r(){let e=globalThis,t=e[n];return t||(t={},e[n]=t),t}function i(e){let t=e.componentDebugHookSet;if(!t||t.size===0){e.componentDebugHooks=null;return}if(t.size===1){e.componentDebugHooks=t.values().next().value??null;return}e.componentDebugHooks={onMountStart(e){for(let n of t)n.onMountStart?.(e)},onMountEnd(e){for(let n of t)n.onMountEnd?.(e)},onUnmount(e){for(let n of t)n.onUnmount?.(e)}}}function a(e){let t=r(),n=new Set;e&&n.add(e),t.componentDebugHookSet=n,i(t)}function o(e){let t=r(),n=t.componentDebugHookSet;return n||(n=new Set,t.componentDebugHooks&&n.add(t.componentDebugHooks),t.componentDebugHookSet=n),n.add(e),i(t),()=>{n.delete(e),i(t)}}function s(e){r().componentDebugHooks?.onMountStart?.(e)}function c(e){r().componentDebugHooks?.onMountEnd?.(e)}function l(e){r().componentDebugHooks?.onUnmount?.(e)}exports.ElurComponent=e,exports._addComponentDebugHooks=o,exports._debugComponentMountEnd=c,exports._debugComponentMountStart=s,exports._debugComponentUnmount=l,exports._setComponentDebugHooks=a,exports.isElurComponent=t;
|
|
2
2
|
//# sourceMappingURL=lifecycle.cjs.map
|