@manyducks.co/dolla 3.0.0 β 3.2.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/dist/core/context.d.ts +9 -0
- package/dist/core/index.d.ts +3 -3
- package/dist/core/signals.d.ts +33 -1
- package/dist/core-2CFW0uRa.js +1047 -0
- package/dist/core-2CFW0uRa.js.map +1 -0
- package/dist/index.js +2 -4
- package/dist/jsx-dev-runtime.js +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/dist/router/types.d.ts +4 -3
- package/dist/router/utils.d.ts +1 -1
- package/dist/router.js +141 -143
- package/dist/router.js.map +1 -1
- package/dist/translate.js +24 -24
- package/package.json +1 -1
- package/dist/core-BLkJ-xuh.js +0 -242
- package/dist/core-BLkJ-xuh.js.map +0 -1
- package/dist/signals-CMJPGr_M.js +0 -354
- package/dist/signals-CMJPGr_M.js.map +0 -1
- package/dist/view-cBN-hn_T.js +0 -360
- package/dist/view-cBN-hn_T.js.map +0 -1
package/dist/router.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","names":[],"sources":["../src/router/utils.ts","../src/router/store.ts","../src/router/router.ts","../src/router/index.ts"],"sourcesContent":["import { View } from \"../types.js\";\nimport { assert, isArray, isFunction, isObject, isString, uniqueId } from \"../utils.js\";\nimport type { JourneyStep, Route, RouteLayer, Stringable } from \"./types.js\";\n\nexport interface Match {\n /**\n * The path string that triggered this match.\n */\n path: string;\n\n /**\n * The pattern satisfied by `path`.\n */\n pattern: string;\n\n /**\n * Named params as parsed from `path`.\n */\n params: Record<string, string>;\n\n /**\n * Query params as parsed from `path`.\n */\n query: Record<string, string>;\n\n /**\n * Freeform data you wish to store with this route.\n * Merged `data` from all matched layers are available on the router's `match.meta`.\n */\n meta: Record<any, any>;\n}\n\nexport interface RouteMatch extends Match {\n layers: RouteLayer[];\n redirect?: string | ((match: Match) => string) | ((match: Match) => Promise<string>);\n}\n\nexport type RoutePayload = {\n pattern: string;\n meta: Record<any, any>;\n layers?: RouteLayer[];\n redirect?: string | ((match: Match) => string) | ((match: Match) => Promise<string>);\n};\n\nexport type RouteMatchOptions = {\n willMatch?: (route: RoutePayload) => boolean;\n};\n\n/**\n * Separates a URL path into multiple fragments.\n *\n * @param path - A path string (e.g. `\"/api/users/5\"`)\n * @returns an array of fragments (e.g. `[\"api\", \"users\", \"5\"]`)\n */\nexport function splitPath(path: string): string[] {\n return path\n .split(\"/\")\n .map((f) => f.trim())\n .filter(Boolean);\n}\n\n/**\n * Joins multiple URL path fragments into a single string.\n *\n * @param parts - One or more URL fragments (e.g. `[\"api\", \"users\", 5]`)\n * @returns a joined path (e.g. `\"api/users/5\"`)\n */\n\nexport function joinPath(parts: { toString(): string }[]): string {\n const joined = parts\n .map((p) => p.toString())\n .filter(Boolean)\n .join(\"/\");\n if (!joined) return \"\";\n\n const isAbsolute = joined.startsWith(\"/\");\n const segments = joined.split(\"/\");\n const resolved: string[] = [];\n\n for (const segment of segments) {\n if (segment === \"\" || segment === \".\") continue;\n if (segment === \"..\") {\n // Pop the previous segment unless we're at the root, or already backing up\n if (resolved.length > 0 && resolved[resolved.length - 1] !== \"..\") {\n resolved.pop();\n } else if (!isAbsolute) {\n resolved.push(\"..\");\n }\n } else {\n resolved.push(segment);\n }\n }\n\n let result = resolved.join(\"/\");\n if (isAbsolute) result = \"/\" + result;\n\n return result || (isAbsolute ? \"/\" : \"\");\n}\n\nexport function resolvePath(base: string, part: string | null = null): string {\n if (part == null) {\n part = base;\n base = \"\";\n }\n\n // If the target is absolute, it replaces the base entirely\n if (part.startsWith(\"/\")) return joinPath([part]);\n\n // Otherwise, join them and let joinPath resolve the '.' and '..'\n return joinPath([base, part]);\n}\n\nexport function parseQueryParams(query: string): Record<string, string> {\n return Object.fromEntries(new URLSearchParams(query));\n}\n\nexport function mergeQueryParams(\n previous: Record<string, string>,\n current: Record<string, Stringable>,\n preserve?: boolean | string[],\n) {\n const merged: Record<string, any> = {};\n\n if (preserve === true) {\n Object.assign(merged, previous);\n } else if (isArray(preserve)) {\n for (const key of preserve) {\n if (key in previous) merged[key] = previous[key];\n }\n }\n\n Object.assign(merged, current);\n return new URLSearchParams(merged as Record<string, string>);\n}\n\nexport class RouteNode {\n staticChildren = new Map<string, RouteNode>();\n numericChild: RouteNode | null = null;\n paramChild: RouteNode | null = null;\n wildcardChild: RouteNode | null = null;\n\n // Set if this node represents the end of a valid path\n route?: RoutePayload;\n paramName?: string;\n numericName?: string;\n}\n\nexport function buildRouteTree(routes: Route[]): RouteNode {\n const root = new RouteNode();\n const redirectsToValidate: RoutePayload[] = [];\n\n function insertIntoTree(pattern: string, payload: RoutePayload) {\n const parts = splitPath(pattern);\n let current = root;\n\n for (const part of parts) {\n if (part === \"*\") {\n current = current.wildcardChild ??= new RouteNode();\n } else if (part.charCodeAt(0) === 123) {\n // {\n if (part.charCodeAt(1) === 35) {\n // #\n current = current.numericChild ??= new RouteNode();\n current.numericName = part.slice(2, -1);\n } else {\n current = current.paramChild ??= new RouteNode();\n current.paramName = part.slice(1, -1);\n }\n } else {\n const key = part.toLowerCase();\n let next = current.staticChildren.get(key);\n if (!next) current.staticChildren.set(key, (next = new RouteNode()));\n current = next;\n }\n }\n current.route = payload;\n }\n\n function parse(route: Route, parents: Route[] = [], layers: RouteLayer[] = []) {\n assert(isObject<Route>(route) && isString(route.path), \"Invalid route object\");\n\n const parentPaths = parents.map((p) => p.path);\n const parent = parents.at(-1);\n const meta = parent && route.meta ? { ...parent.meta, ...route.meta } : route.meta || {};\n\n const rawPattern = parentPaths.length ? joinPath([...parentPaths, route.path]) : route.path;\n const patterns = expandOptionalPaths(rawPattern);\n\n if (route.redirect) {\n assert(!route.routes && !route.view, \"Route cannot mix redirect with view/routes\");\n let redirect = route.redirect;\n\n if (isString(redirect)) {\n redirect = resolvePath(joinPath(parentPaths), redirect);\n if (!redirect.startsWith(\"/\")) redirect = \"/\" + redirect;\n }\n\n for (const pattern of patterns) {\n const payload: RoutePayload = { pattern, meta, redirect };\n insertIntoTree(pattern, payload);\n if (isString(redirect)) redirectsToValidate.push(payload);\n }\n\n return;\n }\n\n assert(route.view || route.routes, \"Route must have view, redirect, or routes\");\n\n let view = (route.view || ((props: any) => props.children)) as View<any>;\n if (!isFunction(view) && !(view as any)._lazy) {\n throw new TypeError(`Expected view function for ${route.path}`);\n }\n\n if (route.routes) {\n // For parent nodes, create the layer using the raw pattern and recurse\n const layer: RouteLayer = {\n id: uniqueId(),\n pattern: rawPattern,\n view,\n preload: route.preload,\n errorView: route.errorView,\n };\n for (const subroute of route.routes) parse(subroute, [...parents, route], [...layers, layer]);\n } else {\n // For leaf nodes, register every permutation as a valid endpoint\n for (const pattern of patterns) {\n const layer: RouteLayer = {\n id: uniqueId(),\n pattern, // Use the specific expanded pattern for this layer\n view,\n preload: route.preload,\n errorView: route.errorView,\n };\n insertIntoTree(pattern, { pattern, meta, layers: [...layers, layer] });\n }\n }\n }\n\n for (const route of routes) parse(route);\n\n for (const payload of redirectsToValidate) {\n assert(\n matchRoute(root, payload.redirect as string, { willMatch: (r) => r !== payload }),\n `Dead redirect: ${payload.pattern} -> ${payload.redirect}`,\n );\n }\n\n return root;\n}\n\nexport function matchRoute(rootNode: RouteNode, url: string, options: RouteMatchOptions = {}): RouteMatch | undefined {\n const [path, query] = url.split(\"?\");\n const parts = splitPath(path);\n const paramState: Record<string, string> = {}; // Reused across branches\n\n function search(node: RouteNode, index: number): RouteMatch | undefined {\n // if we've consumed all URL parts\n if (index === parts.length) {\n if (node.route && (!options.willMatch || options.willMatch(node.route))) {\n return {\n path: path || \"/\",\n pattern: node.route.pattern,\n params: { ...paramState },\n query: parseQueryParams(query || \"\"),\n meta: node.route.meta,\n layers: node.route.layers ?? [],\n redirect: node.route.redirect,\n };\n }\n\n // Allow wildcards to match zero remaining segments\n if (node.wildcardChild && node.wildcardChild.route) {\n if (!options.willMatch || options.willMatch(node.wildcardChild.route)) {\n return {\n path: path || \"/\",\n pattern: node.wildcardChild.route.pattern,\n params: { ...paramState, wildcard: \"/\" },\n query: parseQueryParams(query || \"\"),\n meta: node.wildcardChild.route.meta,\n layers: node.wildcardChild.route.layers ?? [],\n redirect: node.wildcardChild.route.redirect,\n };\n }\n }\n\n return undefined;\n }\n\n const part = parts[index];\n const lowerPart = part.toLowerCase();\n\n const staticNode = node.staticChildren.get(lowerPart);\n if (staticNode) {\n const result = search(staticNode, index + 1);\n if (result) return result;\n }\n\n if (node.numericChild && !isNaN(Number(part))) {\n paramState[node.numericChild.numericName!] = part;\n const result = search(node.numericChild, index + 1);\n if (result) return result;\n delete paramState[node.numericChild.numericName!];\n }\n\n if (node.paramChild) {\n paramState[node.paramChild.paramName!] = decodeURIComponent(part);\n const result = search(node.paramChild, index + 1);\n if (result) return result;\n delete paramState[node.paramChild.paramName!];\n }\n\n if (node.wildcardChild && node.wildcardChild.route) {\n if (!options.willMatch || options.willMatch(node.wildcardChild.route)) {\n return {\n path: path || \"/\",\n pattern: node.wildcardChild.route.pattern,\n params: { ...paramState, wildcard: \"/\" + parts.slice(index).map(decodeURIComponent).join(\"/\") },\n query: parseQueryParams(query || \"\"),\n meta: node.wildcardChild.route.meta,\n layers: node.wildcardChild.route.layers ?? [],\n redirect: node.wildcardChild.route.redirect,\n };\n }\n }\n\n return undefined;\n }\n\n return search(rootNode, 0);\n}\n\nexport interface ResolvedRoute {\n match?: RouteMatch;\n journey: JourneyStep[];\n}\n\n/**\n * Takes a URL and finds a match, following redirects.\n */\nexport async function resolveRoute(\n rootNode: RouteNode,\n path: string,\n journey: JourneyStep[] = [],\n): Promise<ResolvedRoute> {\n const match = matchRoute(rootNode, path);\n\n if (!match) return { journey: [...journey, { kind: \"miss\", message: `no match for '${path}'` }] };\n\n if (match.redirect != null) {\n let target = match.redirect;\n\n if (isString(target)) {\n target = replaceParams(target, match.params);\n } else {\n target = await target(match);\n assert(isString(target), \"Redirect function must return a path.\");\n if (!target.startsWith(\"/\")) target = resolvePath(match.path, target);\n }\n\n return resolveRoute(rootNode, target, [\n ...journey,\n { kind: \"redirect\", message: `redirecting '${match.path}' -> '${target}'` },\n ]);\n }\n\n // TODO: Data preload\n\n return { match, journey: [...journey, { kind: \"match\", message: `matched route '${match.path}'` }] };\n}\n\n/**\n * Intercepts links within the root node.\n *\n * This is adapted from https://github.com/choojs/nanohref/blob/master/index.js\n *\n * @param root - Element under which to intercept link clicks\n * @param callback - Function to call when a click event is intercepted\n * @param _window - (optional) Override for global window object\n */\nexport function catchLinks(\n root: Element,\n callback: (href: string, anchor: HTMLAnchorElement) => void,\n _window = window,\n) {\n function handler(e: MouseEvent) {\n if ((e.button && e.button !== 0) || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.defaultPrevented) return;\n\n const anchor = (e.target as Element).closest(\"a\");\n if (!anchor || !root.contains(anchor)) return;\n\n const href = anchor.getAttribute(\"href\");\n if (!href) return;\n\n if (\n _window.location.protocol !== anchor.protocol ||\n _window.location.hostname !== anchor.hostname ||\n _window.location.port !== anchor.port ||\n anchor.hasAttribute(\"data-router-ignore\") ||\n anchor.hasAttribute(\"download\") ||\n anchor.getAttribute(\"target\") === \"_blank\" ||\n /^[\\w-_]+:/.test(href)\n ) {\n return;\n }\n\n e.preventDefault();\n callback(href, anchor);\n }\n\n root.addEventListener(\"click\", handler as any);\n return () => root.removeEventListener(\"click\", handler as any);\n}\n\nexport function expandOptionalPaths(path: string): string[] {\n const parts = splitPath(path);\n const permutations: string[][] = [[]];\n\n for (const part of parts) {\n // Strictly enforces the inside style: {param?} or {#param?}\n const isOptional = part.endsWith(\"?}\");\n const cleanPart = isOptional ? part.replace(\"?\", \"\") : part;\n\n if (isOptional) {\n const withPart = permutations.map((p) => [...p, cleanPart]);\n permutations.push(...withPart);\n } else {\n for (const p of permutations) {\n p.push(cleanPart);\n }\n }\n }\n\n return permutations.map((p) => \"/\" + p.join(\"/\")).map((p) => (p === \"/\" ? p : p.replace(/\\/$/, \"\")));\n}\n\n/**\n * Replace route pattern param placeholders with real matched values.\n */\nexport function replaceParams(path: string, params: Record<string, string | number>) {\n for (const key in params) {\n const value = String(params[key]);\n path = path\n .replace(`{${key}}`, value)\n .replace(`{#${key}}`, value)\n .replace(`{${key}?}`, value) // Handle optional string param\n .replace(`{#${key}?}`, value); // Handle optional numeric param\n }\n\n // Remove any remaining unmatched optional parameters\n path = path.replace(/\\{#?[a-zA-Z0-9_]+\\?\\}/g, \"\");\n\n // Clean up any double slashes created by the removal\n path = path.replace(/\\/+/g, \"/\");\n\n // Strip trailing slash unless the entire path is just \"/\"\n if (path.length > 1 && path.endsWith(\"/\")) {\n path = path.slice(0, -1);\n }\n\n return path;\n}\n\nexport interface HistoryAdapter {\n getPath(): string;\n getSearch(): string;\n getKey(): string;\n getIndex(): number;\n push(url: string): void;\n replace(url: string): void;\n}\n\nexport function createHistoryAdapter(useHash: boolean): HistoryAdapter {\n let currentIndex = window.history.state?.index || 0;\n\n if (window.history.state?.index === undefined) {\n window.history.replaceState({ ...window.history.state, key: Date.now().toString(), index: currentIndex }, \"\");\n }\n\n const getPath = useHash ? () => window.location.hash.slice(1).split(\"?\")[0] || \"/\" : () => window.location.pathname;\n const getSearch = useHash\n ? () => {\n const hash = window.location.hash;\n const searchIndex = hash.indexOf(\"?\");\n return searchIndex !== -1 ? hash.slice(searchIndex) : \"\";\n }\n : () => window.location.search;\n\n const getKey = () => window.history.state?.key || \"root\";\n const getIndex = () => window.history.state?.index || 0;\n\n return {\n getPath,\n getSearch,\n getKey,\n getIndex,\n push: (url) => {\n currentIndex++;\n const prefix = useHash ? \"/#\" : \"\";\n window.history.pushState({ key: uniqueId(), index: currentIndex }, \"\", prefix + url);\n },\n replace: (url) => {\n const prefix = useHash ? \"/#\" : \"\";\n window.history.replaceState({ key: getKey(), index: currentIndex }, \"\", prefix + url);\n },\n };\n}\n","import { compose, getDebug, peek, type Context, type Getter, type Setter } from \"../core\";\nimport type { Router } from \"./types\";\nimport { mergeQueryParams, resolvePath, type HistoryAdapter, type Match } from \"./utils\";\n\nexport interface RouterStoreProps {\n currentMatch: Getter<Match>;\n setCurrentMatch: Setter<Match>;\n progress: Getter<number>;\n history: HistoryAdapter;\n updateRoute: () => void;\n guards: Set<() => boolean | Promise<boolean>>;\n}\n\nexport function RouterStore(\n this: Context,\n { currentMatch, setCurrentMatch, progress, history, updateRoute, guards }: RouterStoreProps,\n): Router {\n this.name = \"dolla:router\";\n const debug = getDebug(this);\n\n async function navigate(path: string, replace: boolean) {\n for (const guard of guards) {\n if (await guard()) return;\n }\n\n debug.info(`πΊοΈ navigating to '${path}'${replace ? \" (replace)\" : \"\"}`);\n\n const resolved = resolvePath(history.getPath(), path);\n replace ? history.replace(resolved) : history.push(resolved);\n updateRoute();\n }\n\n return {\n path: compose(() => currentMatch().path),\n pattern: compose(() => currentMatch().pattern),\n params: compose(() => currentMatch().params),\n query: compose(() => currentMatch().query),\n meta: compose(() => currentMatch().meta),\n progress: progress,\n\n setQuery(params) {\n const m = peek(currentMatch);\n const merged = mergeQueryParams(m.query, params, true);\n const query = Object.fromEntries(merged);\n\n setCurrentMatch({ ...m, query });\n\n const queryString = merged.size ? \"?\" + merged.toString() : \"\";\n history.replace(m.path + queryString);\n\n return query;\n },\n\n back: (steps = 1) => window.history.go(-steps),\n forward: (steps = 1) => window.history.go(steps),\n\n push: (path) => navigate(path, false),\n replace: (path) => navigate(path, true),\n\n block: (guard) => {\n guards.add(guard);\n return () => guards.delete(guard);\n },\n\n isActive(path: string, exact = false) {\n const target = path === \"/\" ? \"/\" : path.replace(/\\/$/, \"\");\n const targetSlash = target === \"/\" ? \"/\" : target + \"/\";\n\n return compose(() => {\n const current = currentMatch().path;\n const normalized = current === \"/\" ? \"/\" : current.replace(/\\/$/, \"\");\n\n if (exact) return normalized === target;\n\n // Ensure segment boundaries match (prevents /app matching /apple)\n return normalized === target || normalized.startsWith(targetSlash);\n });\n },\n };\n}\n","import { addStore, Context, createContext, onCleanup, onMount } from \"../core/context.js\";\nimport { DollaPlugin, getDebug } from \"../core/index.js\";\nimport { DynamicNode } from \"../core/markup/nodes/dynamic.js\";\nimport { ViewNode } from \"../core/markup/nodes/view.js\";\nimport type { MarkupNode } from \"../core/markup/types.js\";\nimport { addListener, createMarkup } from \"../core/markup/utils.js\";\nimport { batch, peek, createAtom } from \"../core/signals.js\";\nimport { DEBUG, PARENT_ELEMENT } from \"../core/symbols.js\";\nimport type { View } from \"../types.js\";\nimport { assert } from \"../utils.js\";\nimport { RouterStore } from \"./store.js\";\nimport type { ActiveLayer, LazyLoader, LazyView, RouterOptions } from \"./types.js\";\nimport {\n buildRouteTree,\n catchLinks,\n createHistoryAdapter,\n type Match,\n mergeQueryParams,\n replaceParams,\n resolveRoute,\n} from \"./utils.js\";\n\nconst ROUTER_ROOT_SLOT = Symbol();\n\n/**\n * Lazy loads a view when its route is first matched.\n *\n * @example\n * {\n * path: \"/users\",\n * view: lazy(() => import(\"./views/users.js\"))\n * }\n */\nexport function lazy(load: LazyLoader): LazyView {\n return { _lazy: true, load };\n}\n\nexport function createRouterPlugin(options: RouterOptions): DollaPlugin {\n return function (context) {\n if (\"scrollRestoration\" in window.history) {\n window.history.scrollRestoration = \"manual\";\n }\n\n const history = createHistoryAdapter(!!options.hash);\n const scrollCache = new Map<string, number>();\n let currentKey = history.getKey();\n\n const [currentMatch, setCurrentMatch] = createAtom<Match>({\n path: history.getPath(),\n pattern: \"\",\n params: {},\n query: Object.fromEntries(new URLSearchParams(history.getSearch())),\n meta: {},\n });\n\n const [progress, setProgress] = createAtom(0);\n const routeTree = buildRouteTree(options.routes);\n\n const guards = new Set<() => boolean | Promise<boolean>>();\n\n const routerContext = createContext(context);\n routerContext.name = \"dolla:router\";\n\n const console = getDebug(routerContext);\n const [rootSlot, setRootSlot] = createAtom<MarkupNode>();\n\n context[ROUTER_ROOT_SLOT] = rootSlot;\n\n const rootLayer: Partial<ActiveLayer> = {\n context,\n slot: rootSlot,\n setSlot: setRootSlot,\n };\n\n const activeLayers: ActiveLayer[] = [];\n\n /**\n * Run when the location changes. Diffs and mounts new routes and updates\n * the signals accordingly.\n */\n async function updateRoute(href?: string) {\n scrollCache.set(currentKey, window.scrollY);\n\n const path = href ?? history.getPath();\n const { match, journey } = await resolveRoute(routeTree, path);\n\n if (context[DEBUG]) {\n for (let i = 0; i < journey.length; i++) {\n const step = journey[i];\n const tag = `(update ${i + 1}/${journey.length})`;\n if (step.kind === \"match\") {\n console.info(`π ${tag} ${step.message}`);\n } else if (step.kind === \"redirect\") {\n console.info(`β©οΈ ${tag} ${step.message}`);\n } else {\n console.info(`π ${tag} ${step.message}`);\n }\n }\n }\n\n if (!match) throw new Error(`Failed to match route '${path}'`);\n\n const { layers, params } = match;\n const targetKeys: string[] = [];\n let branchIndex = 0;\n\n // Compute keys and find out where mounted layers diverge from matched layers\n for (let i = 0; i < layers.length; i++) {\n const key = `${layers[i].id}:${replaceParams(layers[i].pattern, params)}`;\n targetKeys.push(key);\n if (branchIndex === i && activeLayers[i]?.key === key) branchIndex++;\n }\n\n const tasks: Promise<void>[] = [];\n const preloadedData: any[] = []; // Offsets match loop index minus divIndex\n\n // Execute preloads and lazy component fetches\n for (let i = branchIndex; i < layers.length; i++) {\n const layer = layers[i];\n\n if (layer.preload) {\n tasks.push(\n Promise.resolve(layer.preload(match)).then((data) => {\n preloadedData[i - branchIndex] = data;\n }),\n );\n }\n\n const view = layer.view as LazyView;\n if (view._lazy) {\n tasks.push(\n view.load().then((mod) => {\n layer.view = (mod as any).default ?? mod; // Overwrite with loaded module\n }),\n );\n }\n }\n\n let caughtError: Error | null = null;\n let errorIndex = -1;\n\n // Track loading progress if there are async tasks\n if (tasks.length > 0) {\n setProgress(0.1);\n let completed = 0;\n const increment = 0.8 / tasks.length;\n\n tasks.forEach((p) => p.then(() => setProgress(0.1 + ++completed * increment)).catch(() => {}));\n\n try {\n await Promise.all(tasks);\n } catch (error) {\n setProgress(0);\n if (error instanceof RedirectError) return api.replace(error.redirectPath);\n\n caughtError = error instanceof Error ? error : new Error(String(error));\n errorIndex = branchIndex;\n }\n }\n\n // Merge query params and sync URL if redirect occurred\n const query = mergeQueryParams(peek(currentMatch).query, match.query, options.preserveQuery);\n const queryString = query.toString();\n const newUrl = match.path + (queryString ? `?${queryString}` : \"\");\n\n if (newUrl !== history.getPath() + history.getSearch()) {\n history.replace(newUrl);\n }\n\n // Batch state updates and DOM mutations\n batch(() => {\n setCurrentMatch({ ...match, query: Object.fromEntries(query) });\n\n if (branchIndex === layers.length && activeLayers.length === layers.length) return;\n\n // Fast truncate arrays and drop old DOM branches\n if (activeLayers[branchIndex]) {\n activeLayers[branchIndex].node.unmount();\n activeLayers.length = branchIndex;\n }\n\n // Mount new layers\n for (let i = branchIndex; i < layers.length; i++) {\n const layer = layers[i];\n const parent = activeLayers[i - 1] ?? rootLayer;\n const [slot, setSlot] = createAtom<MarkupNode>();\n\n let viewToMount = layer.view as View<any>;\n let propsToPass: any = {\n data: preloadedData[i - branchIndex],\n children: createMarkup(DynamicNode, { args: [slot] }),\n };\n\n // Handle Error Boundaries\n if (caughtError && i === errorIndex) {\n if (!layer.errorView) throw caughtError;\n viewToMount = layer.errorView;\n propsToPass = { error: caughtError };\n }\n\n const node = new ViewNode(parent.context!, viewToMount, propsToPass);\n parent.setSlot(node);\n\n activeLayers.push({\n id: layer.id,\n key: targetKeys[i],\n node,\n context: node.context,\n slot,\n setSlot,\n });\n\n if (caughtError && i === errorIndex) break;\n }\n });\n\n setProgress(0);\n\n requestAnimationFrame(() => {\n window.scrollTo(0, scrollCache.get(history.getKey()) ?? 0);\n currentKey = history.getKey();\n });\n }\n\n const api = addStore(context, RouterStore, {\n currentMatch,\n setCurrentMatch,\n progress,\n history,\n updateRoute,\n guards,\n });\n\n onMount(context, () => {\n let isReverting = false;\n let isReplaying = false;\n let lastIndex = history.getIndex();\n\n const removePop = addListener(window, \"popstate\", async () => {\n // If this popstate is the result of us reverting the URL, ignore it.\n if (isReverting) {\n isReverting = false;\n return;\n }\n\n // If this popstate is the result of us replaying an allowed navigation, accept it.\n if (isReplaying) {\n isReplaying = false;\n lastIndex = history.getIndex();\n updateRoute();\n return;\n }\n\n const newIndex = history.getIndex();\n const delta = lastIndex - newIndex; // Positive if user clicked Back\n\n // If guards exist, revert synchronously first\n if (guards.size > 0) {\n isReverting = true;\n window.history.go(delta); // Restores the URL immediately\n\n // Run guards while the URL is back in its original state\n let blocked = false;\n for (const guard of guards) {\n if (await guard()) {\n blocked = true;\n break;\n }\n }\n\n // If guards passed, replay the intended navigation\n if (!blocked) {\n isReplaying = true;\n window.history.go(-delta);\n }\n return;\n }\n\n // Normal flow (no guards)\n lastIndex = newIndex;\n updateRoute();\n });\n\n // Block tab closure/reload if guards exist\n const removeUnload = addListener(window, \"beforeunload\", (e: BeforeUnloadEvent) => {\n if (guards.size > 0) {\n e.preventDefault();\n e.returnValue = \"\"; // Triggers the native browser warning dialog\n }\n });\n\n const removeClick = catchLinks(context[PARENT_ELEMENT] as Element, api.push);\n\n onCleanup(context, () => {\n removePop();\n removeUnload();\n removeClick();\n });\n\n updateRoute();\n });\n };\n}\n\n/**\n * Displays the router's content.\n */\nexport function Outlet(this: Context) {\n this.name = \"dolla:router\";\n\n const rootSlot = this[ROUTER_ROOT_SLOT];\n assert(rootSlot != null, \"Router plugin not found on root.\");\n\n return new DynamicNode(this, rootSlot);\n}\n\nexport class RedirectError extends Error {\n constructor(public redirectPath: string) {\n super(`Redirecting to ${redirectPath}`);\n this.name = \"RedirectError\";\n }\n}\n","import { Context, getStore } from \"../core\";\nimport { RouterStore } from \"./store\";\n\nexport { createRouterPlugin, lazy, Outlet, RedirectError } from \"./router\";\nexport type { RouterOptions } from \"./types\";\n\nexport function getRouter(context: Context) {\n return getStore(context, RouterStore);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.js","names":[],"sources":["../src/router/utils.ts","../src/router/store.ts","../src/router/router.ts","../src/router/index.ts"],"sourcesContent":["import { View } from \"../types.js\";\nimport { assert, isArray, isFunction, isObject, isString, uniqueId } from \"../utils.js\";\nimport type { JourneyStep, Route, RouteLayer, Stringable } from \"./types.js\";\n\nexport interface Match {\n /**\n * The path string that triggered this match.\n */\n path: string;\n\n /**\n * The pattern satisfied by `path`.\n */\n pattern: string;\n\n /**\n * Named params as parsed from `path`.\n */\n params: Record<string, string>;\n\n /**\n * Query params as parsed from `path`.\n */\n query: Record<string, string>;\n\n /**\n * Freeform data you wish to store with this route.\n * Merged `data` from all matched layers are available on the router's `match.meta`.\n */\n meta: Record<any, any>;\n}\n\nexport interface RouteMatch extends Match {\n layers: RouteLayer[];\n redirect?: string | ((match: Match) => string) | ((match: Match) => Promise<string>);\n}\n\nexport type RoutePayload = {\n pattern: string;\n meta: Record<any, any>;\n layers?: RouteLayer[];\n redirect?: string | ((match: Match) => string) | ((match: Match) => Promise<string>);\n};\n\nexport type RouteMatchOptions = {\n willMatch?: (route: RoutePayload) => boolean;\n};\n\n/**\n * Separates a URL path into multiple fragments.\n *\n * @param path - A path string (e.g. `\"/api/users/5\"`)\n * @returns an array of fragments (e.g. `[\"api\", \"users\", \"5\"]`)\n */\nexport function splitPath(path: string): string[] {\n return path\n .split(\"/\")\n .map((f) => f.trim())\n .filter(Boolean);\n}\n\n/**\n * Joins multiple URL path fragments into a single string.\n *\n * @param parts - One or more URL fragments (e.g. `[\"api\", \"users\", 5]`)\n * @returns a joined path (e.g. `\"api/users/5\"`)\n */\n\nexport function joinPath(parts: { toString(): string }[]): string {\n const joined = parts\n .map((p) => p.toString())\n .filter(Boolean)\n .join(\"/\");\n if (!joined) return \"\";\n\n const isAbsolute = joined.startsWith(\"/\");\n const segments = joined.split(\"/\");\n const resolved: string[] = [];\n\n for (const segment of segments) {\n if (segment === \"\" || segment === \".\") continue;\n if (segment === \"..\") {\n // Pop the previous segment unless we're at the root, or already backing up\n if (resolved.length > 0 && resolved[resolved.length - 1] !== \"..\") {\n resolved.pop();\n } else if (!isAbsolute) {\n resolved.push(\"..\");\n }\n } else {\n resolved.push(segment);\n }\n }\n\n let result = resolved.join(\"/\");\n if (isAbsolute) result = \"/\" + result;\n\n return result || (isAbsolute ? \"/\" : \"\");\n}\n\nexport function resolvePath(base: string, part: string | null = null): string {\n if (part == null) {\n part = base;\n base = \"\";\n }\n\n // If the target is absolute, it replaces the base entirely\n if (part.startsWith(\"/\")) return joinPath([part]);\n\n // Otherwise, join them and let joinPath resolve the '.' and '..'\n return joinPath([base, part]);\n}\n\nexport function parseQueryParams(query: string): Record<string, string> {\n return Object.fromEntries(new URLSearchParams(query));\n}\n\nexport function mergeQueryParams(\n previous: Record<string, string>,\n current: Record<string, Stringable | null>,\n preserve?: boolean | string[],\n) {\n const merged: Record<string, any> = {};\n\n if (preserve === true) {\n Object.assign(merged, previous);\n } else if (isArray(preserve)) {\n for (const key of preserve) {\n if (key in previous) merged[key] = previous[key];\n }\n }\n\n for (const [key, value] of Object.entries(current)) {\n if (value === null) {\n delete merged[key]; // Explicit nulls act as 'delete'\n } else {\n merged[key] = value;\n }\n }\n\n return new URLSearchParams(merged as Record<string, string>);\n}\n\nexport class RouteNode {\n staticChildren = new Map<string, RouteNode>();\n numericChild: RouteNode | null = null;\n paramChild: RouteNode | null = null;\n wildcardChild: RouteNode | null = null;\n\n // Set if this node represents the end of a valid path\n route?: RoutePayload;\n paramName?: string;\n numericName?: string;\n}\n\nexport function buildRouteTree(routes: Route[]): RouteNode {\n const root = new RouteNode();\n const redirectsToValidate: RoutePayload[] = [];\n\n function insertIntoTree(pattern: string, payload: RoutePayload) {\n const parts = splitPath(pattern);\n let current = root;\n\n for (const part of parts) {\n if (part === \"*\") {\n current = current.wildcardChild ??= new RouteNode();\n } else if (part.charCodeAt(0) === 123) {\n // {\n if (part.charCodeAt(1) === 35) {\n // #\n current = current.numericChild ??= new RouteNode();\n current.numericName = part.slice(2, -1);\n } else {\n current = current.paramChild ??= new RouteNode();\n current.paramName = part.slice(1, -1);\n }\n } else {\n const key = part.toLowerCase();\n let next = current.staticChildren.get(key);\n if (!next) current.staticChildren.set(key, (next = new RouteNode()));\n current = next;\n }\n }\n current.route = payload;\n }\n\n function parse(route: Route, parents: Route[] = [], layers: RouteLayer[] = []) {\n assert(isObject<Route>(route) && isString(route.path), \"Invalid route object\");\n\n const parentPaths = parents.map((p) => p.path);\n const parent = parents.at(-1);\n const meta = parent && route.meta ? { ...parent.meta, ...route.meta } : route.meta || {};\n\n const rawPattern = parentPaths.length ? joinPath([...parentPaths, route.path]) : route.path;\n const patterns = expandOptionalPaths(rawPattern);\n\n if (route.redirect) {\n assert(!route.routes && !route.view, \"Route cannot mix redirect with view/routes\");\n let redirect = route.redirect;\n\n if (isString(redirect)) {\n redirect = resolvePath(joinPath(parentPaths), redirect);\n if (!redirect.startsWith(\"/\")) redirect = \"/\" + redirect;\n }\n\n for (const pattern of patterns) {\n const payload: RoutePayload = { pattern, meta, redirect };\n insertIntoTree(pattern, payload);\n if (isString(redirect)) redirectsToValidate.push(payload);\n }\n\n return;\n }\n\n assert(route.view || route.routes, \"Route must have view, redirect, or routes\");\n\n let view = (route.view || ((props: any) => props.children)) as View<any>;\n if (!isFunction(view) && !(view as any)._lazy) {\n throw new TypeError(`Expected view function for ${route.path}`);\n }\n\n if (route.routes) {\n // For parent nodes, create the layer using the raw pattern and recurse\n const layer: RouteLayer = {\n id: uniqueId(),\n pattern: rawPattern,\n view,\n preload: route.preload,\n errorView: route.errorView,\n };\n for (const subroute of route.routes) parse(subroute, [...parents, route], [...layers, layer]);\n } else {\n // For leaf nodes, register every permutation as a valid endpoint\n for (const pattern of patterns) {\n const layer: RouteLayer = {\n id: uniqueId(),\n pattern, // Use the specific expanded pattern for this layer\n view,\n preload: route.preload,\n errorView: route.errorView,\n };\n insertIntoTree(pattern, { pattern, meta, layers: [...layers, layer] });\n }\n }\n }\n\n for (const route of routes) parse(route);\n\n for (const payload of redirectsToValidate) {\n assert(\n matchRoute(root, payload.redirect as string, { willMatch: (r) => r !== payload }),\n `Dead redirect: ${payload.pattern} -> ${payload.redirect}`,\n );\n }\n\n return root;\n}\n\nexport function matchRoute(rootNode: RouteNode, url: string, options: RouteMatchOptions = {}): RouteMatch | undefined {\n const [path, query] = url.split(\"?\");\n const parts = splitPath(path);\n const paramState: Record<string, string> = {}; // Reused across branches\n\n function search(node: RouteNode, index: number): RouteMatch | undefined {\n // if we've consumed all URL parts\n if (index === parts.length) {\n if (node.route && (!options.willMatch || options.willMatch(node.route))) {\n return {\n path: path || \"/\",\n pattern: node.route.pattern,\n params: { ...paramState },\n query: parseQueryParams(query || \"\"),\n meta: node.route.meta,\n layers: node.route.layers ?? [],\n redirect: node.route.redirect,\n };\n }\n\n // Allow wildcards to match zero remaining segments\n if (node.wildcardChild && node.wildcardChild.route) {\n if (!options.willMatch || options.willMatch(node.wildcardChild.route)) {\n return {\n path: path || \"/\",\n pattern: node.wildcardChild.route.pattern,\n params: { ...paramState, wildcard: \"/\" },\n query: parseQueryParams(query || \"\"),\n meta: node.wildcardChild.route.meta,\n layers: node.wildcardChild.route.layers ?? [],\n redirect: node.wildcardChild.route.redirect,\n };\n }\n }\n\n return undefined;\n }\n\n const part = parts[index];\n const lowerPart = part.toLowerCase();\n\n const staticNode = node.staticChildren.get(lowerPart);\n if (staticNode) {\n const result = search(staticNode, index + 1);\n if (result) return result;\n }\n\n if (node.numericChild && !isNaN(Number(part))) {\n paramState[node.numericChild.numericName!] = part;\n const result = search(node.numericChild, index + 1);\n if (result) return result;\n delete paramState[node.numericChild.numericName!];\n }\n\n if (node.paramChild) {\n paramState[node.paramChild.paramName!] = decodeURIComponent(part);\n const result = search(node.paramChild, index + 1);\n if (result) return result;\n delete paramState[node.paramChild.paramName!];\n }\n\n if (node.wildcardChild && node.wildcardChild.route) {\n if (!options.willMatch || options.willMatch(node.wildcardChild.route)) {\n return {\n path: path || \"/\",\n pattern: node.wildcardChild.route.pattern,\n params: { ...paramState, wildcard: \"/\" + parts.slice(index).map(decodeURIComponent).join(\"/\") },\n query: parseQueryParams(query || \"\"),\n meta: node.wildcardChild.route.meta,\n layers: node.wildcardChild.route.layers ?? [],\n redirect: node.wildcardChild.route.redirect,\n };\n }\n }\n\n return undefined;\n }\n\n return search(rootNode, 0);\n}\n\nexport interface ResolvedRoute {\n match?: RouteMatch;\n journey: JourneyStep[];\n}\n\n/**\n * Takes a URL and finds a match, following redirects.\n */\nexport async function resolveRoute(\n rootNode: RouteNode,\n path: string,\n journey: JourneyStep[] = [],\n): Promise<ResolvedRoute> {\n const match = matchRoute(rootNode, path);\n\n if (!match) return { journey: [...journey, { kind: \"miss\", message: `no match for '${path}'` }] };\n\n if (match.redirect != null) {\n let target = match.redirect;\n\n if (isString(target)) {\n target = replaceParams(target, match.params);\n } else {\n target = await target(match);\n assert(isString(target), \"Redirect function must return a path.\");\n if (!target.startsWith(\"/\")) target = resolvePath(match.path, target);\n }\n\n return resolveRoute(rootNode, target, [\n ...journey,\n { kind: \"redirect\", message: `redirecting '${match.path}' -> '${target}'` },\n ]);\n }\n\n // TODO: Data preload\n\n return { match, journey: [...journey, { kind: \"match\", message: `matched route '${match.path}'` }] };\n}\n\n/**\n * Intercepts links within the root node.\n *\n * This is adapted from https://github.com/choojs/nanohref/blob/master/index.js\n *\n * @param root - Element under which to intercept link clicks\n * @param callback - Function to call when a click event is intercepted\n * @param _window - (optional) Override for global window object\n */\nexport function catchLinks(\n root: Element,\n callback: (href: string, anchor: HTMLAnchorElement) => void,\n _window = window,\n) {\n function handler(e: MouseEvent) {\n if ((e.button && e.button !== 0) || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.defaultPrevented) return;\n\n const anchor = (e.target as Element).closest(\"a\");\n if (!anchor || !root.contains(anchor)) return;\n\n const href = anchor.getAttribute(\"href\");\n if (!href) return;\n\n if (\n _window.location.protocol !== anchor.protocol ||\n _window.location.hostname !== anchor.hostname ||\n _window.location.port !== anchor.port ||\n anchor.hasAttribute(\"data-router-ignore\") ||\n anchor.hasAttribute(\"download\") ||\n anchor.getAttribute(\"target\") === \"_blank\" ||\n /^[\\w-_]+:/.test(href)\n ) {\n return;\n }\n\n e.preventDefault();\n callback(href, anchor);\n }\n\n root.addEventListener(\"click\", handler as any);\n return () => root.removeEventListener(\"click\", handler as any);\n}\n\nexport function expandOptionalPaths(path: string): string[] {\n const parts = splitPath(path);\n const permutations: string[][] = [[]];\n\n for (const part of parts) {\n // Strictly enforces the inside style: {param?} or {#param?}\n const isOptional = part.endsWith(\"?}\");\n const cleanPart = isOptional ? part.replace(\"?\", \"\") : part;\n\n if (isOptional) {\n const withPart = permutations.map((p) => [...p, cleanPart]);\n permutations.push(...withPart);\n } else {\n for (const p of permutations) {\n p.push(cleanPart);\n }\n }\n }\n\n return permutations.map((p) => \"/\" + p.join(\"/\")).map((p) => (p === \"/\" ? p : p.replace(/\\/$/, \"\")));\n}\n\n/**\n * Replace route pattern param placeholders with real matched values.\n */\nexport function replaceParams(path: string, params: Record<string, string | number>) {\n for (const key in params) {\n const value = String(params[key]);\n path = path\n .replace(`{${key}}`, value)\n .replace(`{#${key}}`, value)\n .replace(`{${key}?}`, value) // Handle optional string param\n .replace(`{#${key}?}`, value); // Handle optional numeric param\n }\n\n // Remove any remaining unmatched optional parameters\n path = path.replace(/\\{#?[a-zA-Z0-9_]+\\?\\}/g, \"\");\n\n // Clean up any double slashes created by the removal\n path = path.replace(/\\/+/g, \"/\");\n\n // Strip trailing slash unless the entire path is just \"/\"\n if (path.length > 1 && path.endsWith(\"/\")) {\n path = path.slice(0, -1);\n }\n\n return path;\n}\n\nexport interface HistoryAdapter {\n getPath(): string;\n getSearch(): string;\n getKey(): string;\n getIndex(): number;\n push(url: string): void;\n replace(url: string): void;\n}\n\nexport function createHistoryAdapter(useHash: boolean): HistoryAdapter {\n let currentIndex = window.history.state?.index || 0;\n\n if (window.history.state?.index === undefined) {\n window.history.replaceState({ ...window.history.state, key: Date.now().toString(), index: currentIndex }, \"\");\n }\n\n const getPath = useHash ? () => window.location.hash.slice(1).split(\"?\")[0] || \"/\" : () => window.location.pathname;\n const getSearch = useHash\n ? () => {\n const hash = window.location.hash;\n const searchIndex = hash.indexOf(\"?\");\n return searchIndex !== -1 ? hash.slice(searchIndex) : \"\";\n }\n : () => window.location.search;\n\n const getKey = () => window.history.state?.key || \"root\";\n const getIndex = () => window.history.state?.index || 0;\n\n return {\n getPath,\n getSearch,\n getKey,\n getIndex,\n push: (url) => {\n currentIndex++;\n const prefix = useHash ? \"/#\" : \"\";\n window.history.pushState({ key: uniqueId(), index: currentIndex }, \"\", prefix + url);\n },\n replace: (url) => {\n const prefix = useHash ? \"/#\" : \"\";\n window.history.replaceState({ key: getKey(), index: currentIndex }, \"\", prefix + url);\n },\n };\n}\n","import { compose, getDebug, peek, unwrap, type Context, type Getter, type MaybeGetter, type Setter } from \"../core\";\nimport type { Router } from \"./types\";\nimport { mergeQueryParams, resolvePath, type HistoryAdapter, type Match } from \"./utils\";\n\nexport interface RouterStoreProps {\n currentMatch: Getter<Match>;\n setCurrentMatch: Setter<Match>;\n progress: Getter<number>;\n history: HistoryAdapter;\n updateRoute: () => void;\n guards: Set<() => boolean | Promise<boolean>>;\n}\n\nexport function RouterStore(\n this: Context,\n { currentMatch, setCurrentMatch, progress, history, updateRoute, guards }: RouterStoreProps,\n): Router {\n this.name = \"dolla:router\";\n const debug = getDebug(this);\n\n async function navigate(path: string, replace: boolean) {\n for (const guard of guards) {\n if (await guard()) return;\n }\n\n debug.info(`πΊοΈ navigating to '${path}'${replace ? \" (replace)\" : \"\"}`);\n\n const resolved = resolvePath(history.getPath(), path);\n replace ? history.replace(resolved) : history.push(resolved);\n updateRoute();\n }\n\n return {\n path: compose(() => currentMatch().path),\n pattern: compose(() => currentMatch().pattern),\n params: compose(() => currentMatch().params),\n query: compose(() => currentMatch().query),\n meta: compose(() => currentMatch().meta),\n progress: progress,\n\n setQuery(params) {\n const m = peek(currentMatch);\n const merged = mergeQueryParams(m.query, params, true);\n const query = Object.fromEntries(merged);\n\n setCurrentMatch({ ...m, query });\n\n const queryString = merged.size ? \"?\" + merged.toString() : \"\";\n history.replace(m.path + queryString);\n\n return query;\n },\n\n back: (steps = 1) => window.history.go(-steps),\n forward: (steps = 1) => window.history.go(steps),\n\n push: (path) => navigate(path, false),\n replace: (path) => navigate(path, true),\n\n block: (guard) => {\n guards.add(guard);\n return () => guards.delete(guard);\n },\n\n isActive(path: MaybeGetter<string>, exact = false) {\n return compose(() => {\n const _path = unwrap(path);\n const target = _path === \"/\" ? \"/\" : _path.replace(/\\/$/, \"\");\n const targetSlash = target === \"/\" ? \"/\" : target + \"/\";\n\n const current = currentMatch().path;\n const normalized = current === \"/\" ? \"/\" : current.replace(/\\/$/, \"\");\n\n if (exact) return normalized === target;\n\n // Ensure segment boundaries match (prevents /app matching /apple)\n return normalized === target || normalized.startsWith(targetSlash);\n });\n },\n };\n}\n","import { addStore, Context, createContext, onCleanup, onMount } from \"../core/context.js\";\nimport { DollaPlugin, getDebug, getRootElement } from \"../core/index.js\";\nimport { DynamicNode } from \"../core/markup/nodes/dynamic.js\";\nimport { ViewNode } from \"../core/markup/nodes/view.js\";\nimport type { MarkupNode } from \"../core/markup/types.js\";\nimport { addListener, createMarkup } from \"../core/markup/utils.js\";\nimport { batch, peek, createAtom } from \"../core/signals.js\";\nimport { DEBUG } from \"../core/symbols.js\";\nimport type { View } from \"../types.js\";\nimport { assert } from \"../utils.js\";\nimport { RouterStore } from \"./store.js\";\nimport type { ActiveLayer, LazyLoader, LazyView, RouterOptions } from \"./types.js\";\nimport {\n buildRouteTree,\n catchLinks,\n createHistoryAdapter,\n type Match,\n mergeQueryParams,\n replaceParams,\n resolveRoute,\n} from \"./utils.js\";\n\nconst ROUTER_ROOT_SLOT = Symbol();\n\n/**\n * Lazy loads a view when its route is first matched.\n *\n * @example\n * {\n * path: \"/users\",\n * view: lazy(() => import(\"./views/users.js\"))\n * }\n */\nexport function lazy(load: LazyLoader): LazyView {\n return { _lazy: true, load };\n}\n\nexport function createRouterPlugin(options: RouterOptions): DollaPlugin {\n return function (context) {\n if (\"scrollRestoration\" in window.history) {\n window.history.scrollRestoration = \"manual\";\n }\n\n const history = createHistoryAdapter(!!options.hash);\n const scrollCache = new Map<string, number>();\n let currentKey = history.getKey();\n\n const [currentMatch, setCurrentMatch] = createAtom<Match>({\n path: history.getPath(),\n pattern: \"\",\n params: {},\n query: Object.fromEntries(new URLSearchParams(history.getSearch())),\n meta: {},\n });\n\n const [progress, setProgress] = createAtom(0);\n const routeTree = buildRouteTree(options.routes);\n\n const guards = new Set<() => boolean | Promise<boolean>>();\n\n const routerContext = createContext(context);\n routerContext.name = \"dolla:router\";\n\n const console = getDebug(routerContext);\n const [rootSlot, setRootSlot] = createAtom<MarkupNode>();\n\n context[ROUTER_ROOT_SLOT] = rootSlot;\n\n const rootLayer: Partial<ActiveLayer> = {\n context,\n slot: rootSlot,\n setSlot: setRootSlot,\n };\n\n const activeLayers: ActiveLayer[] = [];\n\n /**\n * Run when the location changes. Diffs and mounts new routes and updates\n * the signals accordingly.\n */\n async function updateRoute(href?: string) {\n scrollCache.set(currentKey, window.scrollY);\n\n const path = href ?? history.getPath();\n const { match, journey } = await resolveRoute(routeTree, path);\n\n if (context[DEBUG]) {\n for (let i = 0; i < journey.length; i++) {\n const step = journey[i];\n const tag = `(update ${i + 1}/${journey.length})`;\n if (step.kind === \"match\") {\n console.info(`π ${tag} ${step.message}`);\n } else if (step.kind === \"redirect\") {\n console.info(`β©οΈ ${tag} ${step.message}`);\n } else {\n console.info(`π ${tag} ${step.message}`);\n }\n }\n }\n\n if (!match) throw new Error(`Failed to match route '${path}'`);\n\n const { layers, params } = match;\n const targetKeys: string[] = [];\n let branchIndex = 0;\n\n // Compute keys and find out where mounted layers diverge from matched layers\n for (let i = 0; i < layers.length; i++) {\n const key = `${layers[i].id}:${replaceParams(layers[i].pattern, params)}`;\n targetKeys.push(key);\n if (branchIndex === i && activeLayers[i]?.key === key) branchIndex++;\n }\n\n const tasks: Promise<void>[] = [];\n const preloadedData: any[] = []; // Offsets match loop index minus divIndex\n\n // Execute preloads and lazy component fetches\n for (let i = branchIndex; i < layers.length; i++) {\n const layer = layers[i];\n\n if (layer.preload) {\n tasks.push(\n Promise.resolve(layer.preload(match)).then((data) => {\n preloadedData[i - branchIndex] = data;\n }),\n );\n }\n\n const view = layer.view as LazyView;\n if (view._lazy) {\n tasks.push(\n view.load().then((mod) => {\n layer.view = (mod as any).default ?? mod; // Overwrite with loaded module\n }),\n );\n }\n }\n\n let caughtError: Error | null = null;\n let errorIndex = -1;\n\n // Track loading progress if there are async tasks\n if (tasks.length > 0) {\n setProgress(0.1);\n let completed = 0;\n const increment = 0.8 / tasks.length;\n\n tasks.forEach((p) => p.then(() => setProgress(0.1 + ++completed * increment)).catch(() => {}));\n\n try {\n await Promise.all(tasks);\n } catch (error) {\n setProgress(0);\n if (error instanceof RedirectError) return api.replace(error.redirectPath);\n\n caughtError = error instanceof Error ? error : new Error(String(error));\n errorIndex = branchIndex;\n }\n }\n\n // Merge query params and sync URL if redirect occurred\n const query = mergeQueryParams(peek(currentMatch).query, match.query, options.preserveQuery);\n const queryString = query.toString();\n const newUrl = match.path + (queryString ? `?${queryString}` : \"\");\n\n if (newUrl !== history.getPath() + history.getSearch()) {\n history.replace(newUrl);\n }\n\n // Batch state updates and DOM mutations\n batch(() => {\n setCurrentMatch({ ...match, query: Object.fromEntries(query) });\n\n if (branchIndex === layers.length && activeLayers.length === layers.length) return;\n\n // Fast truncate arrays and drop old DOM branches\n if (activeLayers[branchIndex]) {\n activeLayers[branchIndex].node.unmount();\n activeLayers.length = branchIndex;\n }\n\n // Mount new layers\n for (let i = branchIndex; i < layers.length; i++) {\n const layer = layers[i];\n const parent = activeLayers[i - 1] ?? rootLayer;\n const [slot, setSlot] = createAtom<MarkupNode>();\n\n let viewToMount = layer.view as View<any>;\n let propsToPass: any = {\n data: preloadedData[i - branchIndex],\n children: createMarkup(DynamicNode, { args: [slot] }),\n };\n\n // Handle Error Boundaries\n if (caughtError && i === errorIndex) {\n if (!layer.errorView) throw caughtError;\n viewToMount = layer.errorView;\n propsToPass = { error: caughtError };\n }\n\n const node = new ViewNode(parent.context!, viewToMount, propsToPass);\n parent.setSlot(node);\n\n activeLayers.push({\n id: layer.id,\n key: targetKeys[i],\n node,\n context: node.context,\n slot,\n setSlot,\n });\n\n if (caughtError && i === errorIndex) break;\n }\n });\n\n setProgress(0);\n\n requestAnimationFrame(() => {\n window.scrollTo(0, scrollCache.get(history.getKey()) ?? 0);\n currentKey = history.getKey();\n });\n }\n\n const api = addStore(context, RouterStore, {\n currentMatch,\n setCurrentMatch,\n progress,\n history,\n updateRoute,\n guards,\n });\n\n onMount(context, () => {\n let isReverting = false;\n let isReplaying = false;\n let lastIndex = history.getIndex();\n\n const removePop = addListener(window, \"popstate\", async () => {\n // If this popstate is the result of us reverting the URL, ignore it.\n if (isReverting) {\n isReverting = false;\n return;\n }\n\n // If this popstate is the result of us replaying an allowed navigation, accept it.\n if (isReplaying) {\n isReplaying = false;\n lastIndex = history.getIndex();\n updateRoute();\n return;\n }\n\n const newIndex = history.getIndex();\n const delta = lastIndex - newIndex; // Positive if user clicked Back\n\n // If guards exist, revert synchronously first\n if (guards.size > 0) {\n isReverting = true;\n window.history.go(delta); // Restores the URL immediately\n\n // Run guards while the URL is back in its original state\n let blocked = false;\n for (const guard of guards) {\n if (await guard()) {\n blocked = true;\n break;\n }\n }\n\n // If guards passed, replay the intended navigation\n if (!blocked) {\n isReplaying = true;\n window.history.go(-delta);\n }\n return;\n }\n\n // Normal flow (no guards)\n lastIndex = newIndex;\n updateRoute();\n });\n\n // Block tab closure/reload if guards exist\n const removeUnload = addListener(window, \"beforeunload\", (e: BeforeUnloadEvent) => {\n if (guards.size > 0) {\n e.preventDefault();\n e.returnValue = \"\"; // Triggers the native browser warning dialog\n }\n });\n\n const removeClick = catchLinks(getRootElement(context), api.push);\n\n onCleanup(context, () => {\n removePop();\n removeUnload();\n removeClick();\n });\n\n updateRoute();\n });\n };\n}\n\n/**\n * Displays the router's content.\n */\nexport function Outlet(this: Context) {\n this.name = \"dolla:router\";\n\n const rootSlot = this[ROUTER_ROOT_SLOT];\n assert(rootSlot != null, \"Router plugin not found on root.\");\n\n return new DynamicNode(this, rootSlot);\n}\n\nexport class RedirectError extends Error {\n constructor(public redirectPath: string) {\n super(`Redirecting to ${redirectPath}`);\n this.name = \"RedirectError\";\n }\n}\n","import { Context, getStore } from \"../core\";\nimport { RouterStore } from \"./store\";\n\nexport { createRouterPlugin, lazy, Outlet, RedirectError } from \"./router\";\nexport type { RouterOptions } from \"./types\";\n\nexport function getRouter(context: Context) {\n return getStore(context, RouterStore);\n}\n"],"mappings":"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"}
|
package/dist/translate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as e, I as t, N as n, O as r } from "./core-2CFW0uRa.js";
|
|
2
2
|
//#region src/translate/index.ts
|
|
3
3
|
var i = Symbol("Dolla.Translator");
|
|
4
4
|
function a(e) {
|
|
@@ -11,29 +11,29 @@ function o(e) {
|
|
|
11
11
|
if (!e[i]) throw Error("Translate plugin isn't loaded.");
|
|
12
12
|
return e[i];
|
|
13
13
|
}
|
|
14
|
-
function s(
|
|
14
|
+
function s(t) {
|
|
15
15
|
let i = /* @__PURE__ */ new Map();
|
|
16
|
-
if (i.set("number", (e, t, n) => new Intl.NumberFormat(e, n).format(Number(t))), i.set("datetime", (e, t, n) => new Intl.DateTimeFormat(e, n).format(new Date(t))), i.set("list", (e, t, n) => new Intl.ListFormat(e, n).format(t)),
|
|
17
|
-
let a, [o, s] =
|
|
16
|
+
if (i.set("number", (e, t, n) => new Intl.NumberFormat(e, n).format(Number(t))), i.set("datetime", (e, t, n) => new Intl.DateTimeFormat(e, n).format(new Date(t))), i.set("list", (e, t, n) => new Intl.ListFormat(e, n).format(t)), t.formatters) for (let e in t.formatters) i.set(e, t.formatters[e]);
|
|
17
|
+
let a, [o, s] = r("en"), l = [...Object.keys(t.translations)];
|
|
18
18
|
async function u(e) {
|
|
19
|
-
let
|
|
19
|
+
let n;
|
|
20
20
|
if (e === void 0) {
|
|
21
21
|
let e = [];
|
|
22
22
|
if (typeof navigator < "u") {
|
|
23
23
|
let t = navigator;
|
|
24
24
|
t.languages?.length > 0 ? e.push(...t.languages) : t.language ? e.push(t.language) : t.browserLanguage ? e.push(t.browserLanguage) : t.userLanguage && e.push(t.userLanguage);
|
|
25
25
|
}
|
|
26
|
-
for (let
|
|
27
|
-
|
|
26
|
+
for (let r of e) if (r in t.translations) {
|
|
27
|
+
n = r;
|
|
28
28
|
break;
|
|
29
29
|
}
|
|
30
|
-
} else e in
|
|
31
|
-
if (
|
|
32
|
-
let e = Object.keys(
|
|
33
|
-
e && (
|
|
30
|
+
} else e in t.translations && (n = e);
|
|
31
|
+
if (n == null) {
|
|
32
|
+
let e = Object.keys(t.translations).at(0);
|
|
33
|
+
e && (n = e);
|
|
34
34
|
}
|
|
35
|
-
if (!
|
|
36
|
-
a = await c(
|
|
35
|
+
if (!n || !(n in t.translations)) throw Error(`Locale '${e}' has no translation.`);
|
|
36
|
+
a = await c(n, i, t.translations[n]), s(n);
|
|
37
37
|
}
|
|
38
38
|
function d(t, n) {
|
|
39
39
|
return e(() => (o(), a?.(t, n) ?? t));
|
|
@@ -51,20 +51,20 @@ function s(r) {
|
|
|
51
51
|
format: f
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
async function c(e,
|
|
55
|
-
let a = l(
|
|
56
|
-
return function(
|
|
57
|
-
if (i && (i.context != null && (
|
|
58
|
-
let
|
|
59
|
-
o.has(
|
|
54
|
+
async function c(e, r, i) {
|
|
55
|
+
let a = l(t(i) ? await i() : i), o = new Map(a);
|
|
56
|
+
return function(t, i) {
|
|
57
|
+
if (i && (i.context != null && (t += "_" + i.context), i.count != null)) if (i.ordinal) {
|
|
58
|
+
let r = `${t}_ordinal_(=${i.count})`;
|
|
59
|
+
o.has(r) ? t = r : t += "_ordinal_" + new Intl.PluralRules(e, { type: "ordinal" }).select(n(i.count));
|
|
60
60
|
} else {
|
|
61
|
-
let
|
|
62
|
-
o.has(
|
|
61
|
+
let r = `${t}_(=${i.count})`;
|
|
62
|
+
o.has(r) ? t = r : t += "_" + new Intl.PluralRules(e).select(n(i.count));
|
|
63
63
|
}
|
|
64
|
-
let a = o.get(
|
|
65
|
-
if (!a) return
|
|
64
|
+
let a = o.get(t);
|
|
65
|
+
if (!a) return t;
|
|
66
66
|
let s = "";
|
|
67
|
-
for (let
|
|
67
|
+
for (let t = 0; t < a.length; t++) s += a[t](i, r, e);
|
|
68
68
|
return s;
|
|
69
69
|
};
|
|
70
70
|
}
|
package/package.json
CHANGED
package/dist/core-BLkJ-xuh.js
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import { c as e, n as t, o as n, p as r, r as i, t as a, u as o } from "./signals-CMJPGr_M.js";
|
|
2
|
-
import { a as s, b as c, c as l, d as u, f as d, g as f, i as p, l as m, m as h, n as g, o as _, s as v, t as y, u as b } from "./view-cBN-hn_T.js";
|
|
3
|
-
//#region src/core/root.ts
|
|
4
|
-
function x(t, n) {
|
|
5
|
-
let i = r(t) ? document.querySelector(t) : t;
|
|
6
|
-
e(i, "Element cannot be null.");
|
|
7
|
-
let a = h();
|
|
8
|
-
a.name = "dolla:root";
|
|
9
|
-
let s = [], u = [];
|
|
10
|
-
a[m] = i, a[l] = !!n?.debug;
|
|
11
|
-
let d = null, p = {
|
|
12
|
-
plugin: g,
|
|
13
|
-
mount: _,
|
|
14
|
-
unmount: b
|
|
15
|
-
};
|
|
16
|
-
function g(e) {
|
|
17
|
-
return s.push(e), p;
|
|
18
|
-
}
|
|
19
|
-
async function _(e) {
|
|
20
|
-
if (a.isMounted) return;
|
|
21
|
-
let t = await Promise.all(s.map((e) => e(a)));
|
|
22
|
-
for (let e of t) o(e) && u.push(e);
|
|
23
|
-
d = o(e) ? new y(a, e, {}) : v(e, a), d?.mount(i), f(a);
|
|
24
|
-
}
|
|
25
|
-
async function b() {
|
|
26
|
-
a.isMounted && (d?.unmount(!1), d = null, c(a), await Promise.all(u.map((e) => e())), u.length = 0);
|
|
27
|
-
}
|
|
28
|
-
return p;
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
//#region src/core/debug.ts
|
|
32
|
-
var S = () => {}, C = {
|
|
33
|
-
trace: 1,
|
|
34
|
-
info: 1,
|
|
35
|
-
log: 2,
|
|
36
|
-
warn: 3,
|
|
37
|
-
error: 4,
|
|
38
|
-
silent: 5
|
|
39
|
-
}, w = 1, T = (e) => !e.startsWith("dolla:"), E = (e) => {
|
|
40
|
-
w = C[e] || 1;
|
|
41
|
-
}, D = (e) => {
|
|
42
|
-
T = e;
|
|
43
|
-
}, O = globalThis.console || {};
|
|
44
|
-
function k(e, ...t) {
|
|
45
|
-
let n = e.name, r, i = (e, i) => {
|
|
46
|
-
if (i < w || !T(n) || !O[e]) return S;
|
|
47
|
-
if (!r) {
|
|
48
|
-
let e = "%c" + n, i = [`color:${j(n)};font-weight:bold`];
|
|
49
|
-
for (let [n, r] of t) e += `%c[${n}: %c${r}%c]`, i.push("color:#777", "color:#aaa", "color:#777");
|
|
50
|
-
r = [e, ...i];
|
|
51
|
-
}
|
|
52
|
-
return O[e].bind(O, ...r);
|
|
53
|
-
};
|
|
54
|
-
return {
|
|
55
|
-
get info() {
|
|
56
|
-
return i("info", 1);
|
|
57
|
-
},
|
|
58
|
-
get trace() {
|
|
59
|
-
return i("trace", 1);
|
|
60
|
-
},
|
|
61
|
-
get log() {
|
|
62
|
-
return i("log", 2);
|
|
63
|
-
},
|
|
64
|
-
get warn() {
|
|
65
|
-
return i("warn", 3);
|
|
66
|
-
},
|
|
67
|
-
get error() {
|
|
68
|
-
return i("error", 4);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
function A(e, ...t) {
|
|
73
|
-
let n = h();
|
|
74
|
-
return n.name = e, k(n, ...t);
|
|
75
|
-
}
|
|
76
|
-
function j(e) {
|
|
77
|
-
let t = 0;
|
|
78
|
-
for (let n = 0; n < e.length; n++) t = (t + e.charCodeAt(n) * 10) % 360;
|
|
79
|
-
return `oklch(0.68 0.15 ${t}deg)`;
|
|
80
|
-
}
|
|
81
|
-
//#endregion
|
|
82
|
-
//#region src/core/markup/nodes/portal.ts
|
|
83
|
-
var M = class extends d {
|
|
84
|
-
#e = s("");
|
|
85
|
-
#t;
|
|
86
|
-
#n;
|
|
87
|
-
#r;
|
|
88
|
-
#i;
|
|
89
|
-
constructor(e, t, n) {
|
|
90
|
-
super(), this.#t = e, this.#n = t, this.#r = n;
|
|
91
|
-
}
|
|
92
|
-
getRoot() {
|
|
93
|
-
return this.#e;
|
|
94
|
-
}
|
|
95
|
-
isMounted() {
|
|
96
|
-
return this.#e.parentNode != null;
|
|
97
|
-
}
|
|
98
|
-
mount(e, t) {
|
|
99
|
-
this.isMounted() || (g(e, this.#e, t), this.#i ||= v(this.#n, this.#t), this.#i.mount(this.#r));
|
|
100
|
-
}
|
|
101
|
-
unmount(e = !1) {
|
|
102
|
-
this.isMounted() && (e || this.#e.parentNode?.removeChild(this.#e), this.#i?.isMounted() && this.#i.unmount(!1));
|
|
103
|
-
}
|
|
104
|
-
move(e, t) {
|
|
105
|
-
_(e, this.#e, t);
|
|
106
|
-
}
|
|
107
|
-
}, N = class extends d {
|
|
108
|
-
#e = s("");
|
|
109
|
-
#t;
|
|
110
|
-
#n;
|
|
111
|
-
#r;
|
|
112
|
-
#i;
|
|
113
|
-
#a = null;
|
|
114
|
-
#o = /* @__PURE__ */ new Map();
|
|
115
|
-
constructor(e, t, n, r) {
|
|
116
|
-
super(), this.#t = e, this.#n = t, this.#r = n, this.#i = r;
|
|
117
|
-
}
|
|
118
|
-
getRoot() {
|
|
119
|
-
return this.#e;
|
|
120
|
-
}
|
|
121
|
-
isMounted() {
|
|
122
|
-
return this.#e.parentNode != null;
|
|
123
|
-
}
|
|
124
|
-
mount(e, t) {
|
|
125
|
-
this.isMounted() || (g(e, this.#e, t), this.#a = n(this.#n, (e) => {
|
|
126
|
-
u(() => {
|
|
127
|
-
this._update(Array.from(e));
|
|
128
|
-
});
|
|
129
|
-
}));
|
|
130
|
-
}
|
|
131
|
-
unmount(e = !1) {
|
|
132
|
-
this.#a &&= (this.#a(), null), !e && this.isMounted() && this.#e.parentNode?.removeChild(this.#e), this._cleanup(e);
|
|
133
|
-
}
|
|
134
|
-
move(e, t) {
|
|
135
|
-
return this.mount(e, t);
|
|
136
|
-
}
|
|
137
|
-
_cleanup(e) {
|
|
138
|
-
for (let t of this.#o.values()) t._node.unmount(e);
|
|
139
|
-
this.#o.clear();
|
|
140
|
-
}
|
|
141
|
-
_update(e) {
|
|
142
|
-
if (!this.isMounted()) return;
|
|
143
|
-
if (e.length === 0) return this._cleanup(!1);
|
|
144
|
-
let t = /* @__PURE__ */ new Map();
|
|
145
|
-
a(() => {
|
|
146
|
-
let n = new Set(e.map((e, t) => this.#r(e, t)));
|
|
147
|
-
for (let [e, t] of this.#o.entries()) n.has(e) || t._node.unmount(!1);
|
|
148
|
-
for (let r = 0; r < e.length; r++) {
|
|
149
|
-
let a = e[r], o = this.#r(a, r), s = this.#o.get(o);
|
|
150
|
-
if (s && n.has(o)) s._setItem(a), s._setIndex(r);
|
|
151
|
-
else {
|
|
152
|
-
let [e, t] = i(a), [n, c] = i(r);
|
|
153
|
-
s = {
|
|
154
|
-
_key: o,
|
|
155
|
-
_node: v(this.#i(() => e(), () => n()), this.#t),
|
|
156
|
-
_item: e,
|
|
157
|
-
_setItem: t,
|
|
158
|
-
_index: n,
|
|
159
|
-
_setIndex: c
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
t.set(o, s);
|
|
163
|
-
}
|
|
164
|
-
}), this.#o = t;
|
|
165
|
-
let n = this.#e.parentElement, r = this.#e;
|
|
166
|
-
for (let e of this.#o.values()) {
|
|
167
|
-
let t = r.nextSibling;
|
|
168
|
-
e._node.isMounted() ? e._node.getRoot() !== t && e._node.move(n, r) : e._node.mount(n, r), r = e._node.getRoot();
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
//#endregion
|
|
173
|
-
//#region src/core/markup/helpers.ts
|
|
174
|
-
function P(e, t, n) {
|
|
175
|
-
return o(e) ? p(N, { args: [
|
|
176
|
-
e,
|
|
177
|
-
t,
|
|
178
|
-
n
|
|
179
|
-
] }) : Array.from(e).map((e, t) => n(() => e, () => t));
|
|
180
|
-
}
|
|
181
|
-
function F(e, n, r) {
|
|
182
|
-
return o(e) ? p(b, { args: [t(() => e() ? n : r)] }) : e ? n : r;
|
|
183
|
-
}
|
|
184
|
-
function I(e, t, n) {
|
|
185
|
-
return F(e, t, n);
|
|
186
|
-
}
|
|
187
|
-
function L(e, t, n) {
|
|
188
|
-
return F(e, n, t);
|
|
189
|
-
}
|
|
190
|
-
function R(e, t) {
|
|
191
|
-
return p(M, { args: [t, e] });
|
|
192
|
-
}
|
|
193
|
-
//#endregion
|
|
194
|
-
//#region src/core/markup/html.ts
|
|
195
|
-
var z = /* @__PURE__ */ function(e) {
|
|
196
|
-
return e[e.Slash = 0] = "Slash", e[e.Text = 1] = "Text", e[e.Whitespace = 2] = "Whitespace", e[e.TagName = 3] = "TagName", e[e.Comment = 4] = "Comment", e[e.PropSet = 5] = "PropSet", e[e.PropAppend = 6] = "PropAppend", e;
|
|
197
|
-
}(z || {});
|
|
198
|
-
function B(e, ...t) {
|
|
199
|
-
let n = [e, ...t], r = z.Text, i = "", a = "", o = [0], s = "", c = (e) => {
|
|
200
|
-
r === z.Text && (e || (i = i.replace(/^\s*\n\s*|\s*\n\s*$/g, ""))) ? o.push(e ? n[e] : i) : r === z.TagName && (e || i) ? (o[1] = e ? n[e] : i, r = z.Whitespace) : r === z.Whitespace && i === "..." && e ? o[2] = Object.assign(o[2] || {}, n[e]) : r === z.Whitespace && i && !e ? (o[2] = o[2] || {})[i] = !0 : r >= z.PropSet && (r === z.PropSet ? ((o[2] = o[2] || {})[s] = e ? i ? i + n[e] : n[e] : i, r = z.PropAppend) : (e || i) && (o[2][s] += e ? i + n[e] : i)), i = "";
|
|
201
|
-
};
|
|
202
|
-
for (let t = 0; t < e.length; t++) {
|
|
203
|
-
t && (r === z.Text && c(), c(t));
|
|
204
|
-
for (let n = 0; n < e[t].length; n++) {
|
|
205
|
-
let l = e[t][n];
|
|
206
|
-
if (r === z.Text) l === "<" ? (c(), o = [
|
|
207
|
-
o,
|
|
208
|
-
"",
|
|
209
|
-
null
|
|
210
|
-
], r = z.TagName) : i += l;
|
|
211
|
-
else if (r === z.Comment) i === "--" && l === ">" ? (r = z.Text, i = "") : i = l + i[0];
|
|
212
|
-
else if (a) l === a ? a = "" : i += l;
|
|
213
|
-
else if (l === "\"" || l === "'") a = l;
|
|
214
|
-
else if (l === ">") c(), r = z.Text;
|
|
215
|
-
else if (r) if (l === "=") r = z.PropSet, s = i, i = "";
|
|
216
|
-
else if (l === "/" && (r < z.PropSet || e[t][n + 1] === ">")) {
|
|
217
|
-
c(), r === z.TagName && (o = o[0]);
|
|
218
|
-
let e = o;
|
|
219
|
-
o = o[0];
|
|
220
|
-
let t = e[1], n = e[2] || {}, i = e.slice(3);
|
|
221
|
-
o.push(p(t, {
|
|
222
|
-
...n,
|
|
223
|
-
children: i
|
|
224
|
-
})), r = z.Slash;
|
|
225
|
-
} else l === " " || l === " " || l === "\n" || l === "\r" ? (c(), r = z.Whitespace) : i += l;
|
|
226
|
-
r === z.TagName && i === "!--" && (r = z.Comment, o = o[0]);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return c(), o.length > 2 ? o.slice(1) : o[1];
|
|
230
|
-
}
|
|
231
|
-
//#endregion
|
|
232
|
-
//#region src/core/ref.ts
|
|
233
|
-
function V() {
|
|
234
|
-
let e;
|
|
235
|
-
return ((...t) => t.length ? (e = t[0], () => {
|
|
236
|
-
e = void 0;
|
|
237
|
-
}) : e);
|
|
238
|
-
}
|
|
239
|
-
//#endregion
|
|
240
|
-
export { L as a, k as c, x as d, P as i, D as l, B as n, I as o, R as r, A as s, V as t, E as u };
|
|
241
|
-
|
|
242
|
-
//# sourceMappingURL=core-BLkJ-xuh.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"core-BLkJ-xuh.js","names":["#context","#value","#parent","#anchor","#childNode","#context","#items","#key","#render","#root","#unsubscribe","#connectedItems"],"sources":["../src/core/root.ts","../src/core/debug.ts","../src/core/markup/nodes/portal.ts","../src/core/markup/nodes/repeat.ts","../src/core/markup/helpers.ts","../src/core/markup/html.ts","../src/core/ref.ts"],"sourcesContent":["import type { Renderable, View } from \"../types.js\";\nimport { assert, isFunction, isString } from \"../utils.js\";\nimport { type Context, createContext, mountContext, unmountContext } from \"./context.js\";\nimport { ViewNode } from \"./markup/nodes/view.js\";\nimport { type MarkupNode } from \"./markup/types.js\";\nimport { render } from \"./markup/utils.js\";\nimport { DEBUG, PARENT_ELEMENT } from \"./symbols.js\";\n\nexport type CleanupCallback = () => void | Promise<void>;\n\n/**\n * Plugins run before the app is mounted. If they return a promise, mounting will be delayed until the promise resolves.\n * If a cleanup function is returned, it will be called before the app is unmounted. The cleanup function's promise will delay unmounting.\n *\n * Hooks can be used inside plugins.\n */\nexport type DollaPlugin = (context: Context) => Promise<CleanupCallback | void> | CleanupCallback | void;\n\nexport interface DollaRootOptions {\n /**\n * Adds additional view info to the DOM to help with debugging.\n */\n debug?: boolean;\n}\n\nexport interface DollaRoot {\n /**\n * Registers a plugin to be added before `mount`.\n */\n plugin(plugin: DollaPlugin): DollaRoot;\n\n /**\n * Mounts a `view` to this root.\n */\n mount(view: View<{}>): Promise<void>;\n\n /**\n * Mounts any renderable content to this root.\n */\n mount(content: Renderable): Promise<void>;\n\n /**\n * Unmounts the currently mounted content.\n */\n unmount(): void;\n}\n\nexport function createRoot(selector: string, options?: DollaRootOptions): DollaRoot;\nexport function createRoot(element: Element, options?: DollaRootOptions): DollaRoot;\nexport function createRoot(target: string | Element, options?: DollaRootOptions) {\n const element = isString(target) ? document.querySelector(target) : target;\n assert(element, \"Element cannot be null.\");\n\n const context = createContext();\n context.name = \"dolla:root\";\n\n const plugins: DollaPlugin[] = [];\n const cleanup: CleanupCallback[] = [];\n\n context[PARENT_ELEMENT] = element;\n context[DEBUG] = Boolean(options?.debug);\n\n let rootNode: MarkupNode | null = null;\n\n const self: DollaRoot = { plugin, mount, unmount };\n\n function plugin(plugin: DollaPlugin) {\n plugins.push(plugin);\n return self;\n }\n\n async function mount(content: View<{}> | Renderable) {\n if (context.isMounted) return;\n\n const results = await Promise.all(plugins.map((fn) => fn(context)));\n for (const result of results) {\n if (isFunction<CleanupCallback>(result)) {\n cleanup.push(result);\n }\n }\n\n rootNode = isFunction<View<{}>>(content) ? new ViewNode(context, content, {}) : render(content, context);\n rootNode?.mount(element!);\n\n mountContext(context);\n }\n\n async function unmount() {\n if (!context.isMounted) return;\n\n rootNode?.unmount(false);\n rootNode = null;\n\n unmountContext(context);\n\n await Promise.all(cleanup.map((callback) => callback()));\n cleanup.length = 0;\n }\n\n return self;\n}\n","import { Context, createContext } from \"./context.js\";\n\nexport const noOp = () => {};\n\nexport type LogLevel = \"info\" | \"log\" | \"warn\" | \"error\" | \"silent\";\nconst LEVELS: Record<string, number> = { trace: 1, info: 1, log: 2, warn: 3, error: 4, silent: 5 };\n\nlet logLevel = 1;\nlet logFilter = (name: string) => !name.startsWith(\"dolla:\");\n\n// 3. Extracted configuration setters (replacing static class methods)\nexport const setLogLevel = (level: LogLevel) => {\n logLevel = LEVELS[level] || 1;\n};\nexport const setLogFilter = (filter: (name: string) => boolean) => {\n logFilter = filter;\n};\n\nconst c: any = globalThis.console || {};\n\nexport function getDebug(context: Context, ...tags: [string, any][]) {\n const name = context.name;\n let args: any[];\n\n const make = (method: string, level: number): ((...args: any[]) => void) => {\n if (level < logLevel || !logFilter(name) || !c[method]) return noOp;\n\n // Build and cache the console arguments on the first valid log\n if (!args) {\n let p = \"%c\" + name;\n let s = [`color:${okhash(name)};font-weight:bold`];\n for (const [k, v] of tags) {\n p += `%c[${k}: %c${v}%c]`;\n s.push(\"color:#777\", \"color:#aaa\", \"color:#777\");\n }\n args = [p, ...s];\n }\n\n return c[method].bind(c, ...args);\n };\n\n return {\n get info() {\n return make(\"info\", 1);\n },\n get trace() {\n return make(\"trace\", 1);\n },\n get log() {\n return make(\"log\", 2);\n },\n get warn() {\n return make(\"warn\", 3);\n },\n get error() {\n return make(\"error\", 4);\n },\n };\n}\n\nexport function createDebug(name: string, ...tags: [string, any][]) {\n const context = createContext();\n context.name = name;\n return getDebug(context, ...tags);\n}\n\n/**\n * Takes any string and returns an OKLCH color.\n */\nfunction okhash(value: string) {\n let hue = 0;\n for (let i = 0; i < value.length; i++) {\n hue = (hue + value.charCodeAt(i) * 10) % 360;\n }\n return `oklch(0.68 0.15 ${hue}deg)`;\n}\n","import type { Renderable } from \"../../../types.js\";\nimport { Context } from \"../../context.js\";\nimport { MarkupNode, MountTarget } from \"../types.js\";\nimport { addChild, createTextNode, moveAfter, render } from \"../utils.js\";\n\n/**\n * Renders content into a specified parent node.\n */\nexport class PortalNode extends MarkupNode {\n // Acts as a physical placeholder in the logical DOM tree\n #anchor = createTextNode(\"\");\n\n #context: Context;\n #value: Renderable;\n #parent: MountTarget;\n #childNode?: MarkupNode;\n\n constructor(context: Context, value: Renderable, parent: MountTarget) {\n super();\n this.#context = context;\n this.#value = value;\n this.#parent = parent;\n }\n\n override getRoot() {\n // Return the anchor, allowing siblings to mount correctly around it\n return this.#anchor;\n }\n\n override isMounted() {\n return this.#anchor.parentNode != null;\n }\n\n override mount(logicalParent: MountTarget, after?: Node) {\n if (!this.isMounted()) {\n // Mount the anchor in the standard document flow\n addChild(logicalParent, this.#anchor, after);\n\n // Render the content and mount it to the portal target\n if (!this.#childNode) {\n this.#childNode = render(this.#value, this.#context);\n }\n this.#childNode.mount(this.#parent);\n }\n }\n\n override unmount(skipDOM = false) {\n if (this.isMounted()) {\n if (!skipDOM) {\n this.#anchor.parentNode?.removeChild(this.#anchor);\n }\n\n // Portals always force unmount the DOM of their children\n if (this.#childNode?.isMounted()) {\n this.#childNode.unmount(false);\n }\n }\n }\n\n override move(logicalParent: MountTarget, after?: Node) {\n moveAfter(logicalParent, this.#anchor, after);\n }\n}\n","import type { Renderable } from \"../../../types.js\";\nimport type { Context } from \"../../context.js\";\nimport { batch, type Getter, createAtom, subscribe, type Setter } from \"../../signals.js\";\nimport { scheduleUpdate } from \"../scheduler.js\";\nimport { MarkupNode } from \"../types.js\";\nimport { addChild, createTextNode, render } from \"../utils.js\";\n\n// ----- Types ----- //\n\nexport type Key = any;\n\nexport type KeyFn<T> = (item: T, index: number) => Key;\nexport type RenderFn<T> = (item: Getter<T>, index: Getter<number>) => Renderable;\n\ntype ConnectedItem<T> = {\n _key: Key;\n _item: Getter<T>;\n _setItem: Setter<T>;\n _index: Getter<number>;\n _setIndex: Setter<number>;\n _node: MarkupNode;\n};\n\n// ----- Code ----- //\n\n/**\n * Renders a list of items.\n */\nexport class RepeatNode<T> extends MarkupNode {\n #root = createTextNode(\"\");\n\n #context;\n\n #items: Getter<Iterable<T>>;\n #key: KeyFn<T>;\n #render: RenderFn<T>;\n\n #unsubscribe: (() => void) | null = null;\n #connectedItems: Map<Key, ConnectedItem<T>> = new Map();\n\n constructor(context: Context, items: Getter<Iterable<T>>, key: KeyFn<T>, render: RenderFn<T>) {\n super();\n this.#context = context;\n\n this.#items = items;\n this.#key = key;\n this.#render = render;\n }\n\n override getRoot() {\n return this.#root;\n }\n\n override isMounted() {\n return this.#root.parentNode != null;\n }\n\n override mount(parent: Element, after?: Node) {\n if (!this.isMounted()) {\n addChild(parent, this.#root, after);\n\n this.#unsubscribe = subscribe(this.#items, (items) => {\n scheduleUpdate(() => {\n this._update(Array.from(items));\n });\n });\n }\n }\n\n override unmount(skipDOM = false) {\n if (this.#unsubscribe) {\n this.#unsubscribe();\n this.#unsubscribe = null;\n }\n\n if (!skipDOM && this.isMounted()) {\n this.#root.parentNode?.removeChild(this.#root);\n }\n\n this._cleanup(skipDOM);\n }\n\n override move(parent: Element, after?: Node) {\n // TODO: Implement move\n return this.mount(parent, after);\n }\n\n private _cleanup(skipDOM: boolean) {\n for (const item of this.#connectedItems.values()) {\n item._node.unmount(skipDOM);\n }\n this.#connectedItems.clear();\n }\n\n private _update(value: T[]) {\n if (!this.isMounted()) return;\n\n if (value.length === 0) {\n return this._cleanup(false);\n }\n\n const nextItems = new Map<Key, ConnectedItem<T>>();\n\n batch(() => {\n // Track keys for the incoming list\n const nextKeys = new Set(value.map((item, index) => this.#key(item, index)));\n\n // Unmount deleted items immediately.\n // This collapses the DOM tree so surviving items sit adjacent to each other.\n for (const [key, connected] of this.#connectedItems.entries()) {\n if (!nextKeys.has(key)) {\n connected._node.unmount(false);\n }\n }\n\n // Prepare state and allocate new nodes.\n for (let i = 0; i < value.length; i++) {\n const itemVal = value[i];\n const key = this.#key(itemVal, i);\n let connected = this.#connectedItems.get(key);\n\n if (connected && nextKeys.has(key)) {\n connected._setItem(itemVal);\n connected._setIndex(i);\n } else {\n const [_item, _setItem] = createAtom(itemVal);\n const [_index, _setIndex] = createAtom(i);\n\n const renderContent = this.#render(\n () => _item(),\n () => _index(),\n );\n const _node = render(renderContent, this.#context);\n\n connected = {\n _key: key,\n _node,\n _item,\n _setItem,\n _index,\n _setIndex,\n };\n }\n nextItems.set(key, connected);\n }\n });\n\n this.#connectedItems = nextItems;\n\n // Forward pass to insert or move nodes.\n const parent = this.#root.parentElement!;\n let referenceNode: Node = this.#root;\n\n for (const connected of this.#connectedItems.values()) {\n const expectedNext = referenceNode.nextSibling;\n\n if (!connected._node.isMounted()) {\n // Node is new. Mount it exactly at the current cursor.\n connected._node.mount(parent, referenceNode);\n } else if (connected._node.getRoot() !== expectedNext) {\n // Node is out of order. Move it.\n connected._node.move(parent, referenceNode);\n }\n\n // Advance the cursor.\n referenceNode = connected._node.getRoot()!;\n }\n }\n}\n","import { createMarkup, Renderable } from \"..\";\nimport { isFunction } from \"../../utils\";\nimport { type Getter, compose } from \"../signals\";\nimport { DynamicNode } from \"./nodes/dynamic\";\nimport { PortalNode } from \"./nodes/portal\";\nimport { KeyFn, RenderFn, RepeatNode } from \"./nodes/repeat\";\n\n/**\n * Displays a dynamic list. Items will be added and removed as the list is updated.\n *\n * @param items - List items. Can be reactive.\n * @param keyFn - Takes (item, index) as plain values and returns a unique key to identify that item (usually the item's ID).\n * @param renderFn - Takes (item, index) as Reactive values and returns content to display for that item.\n */\nexport function forEach<T>(\n items: Getter<Iterable<T>> | Iterable<T>,\n keyFn: KeyFn<T>,\n renderFn: RenderFn<T>,\n): Renderable {\n if (isFunction(items)) {\n return createMarkup(RepeatNode<T>, { args: [items, keyFn, renderFn] });\n } else {\n return Array.from(items).map((item, index) =>\n renderFn(\n () => item,\n () => index,\n ),\n );\n }\n}\n\n/**\n * Displays content conditionally. When `condition` is truthy, display `content`. When `condition` is falsy, display `fallback`.\n *\n * @param condition - Condition to hide or show content on. Can be reactive.\n * @param whenTruthy - Content to display when condition is truthy.\n * @param whenFalsy - Content to display when condition is falsy.\n */\nfunction _conditional(condition: any, whenTruthy?: Renderable, whenFalsy?: Renderable): Renderable {\n if (isFunction(condition)) {\n return createMarkup(DynamicNode, {\n args: [compose(() => (condition() ? whenTruthy : whenFalsy))],\n });\n } else if (condition) {\n return whenTruthy;\n } else {\n return whenFalsy;\n }\n}\n\n/**\n * Shows content only when `condition` is truthy.\n * It can be a plain value or a Getter to track dynamically.\n *\n * @param condition - Condition to hide or show content on.\n * @param content - Content to display when condition is truthy.\n * @param fallback - Content to display when condition is falsy.\n */\nexport function showIf(condition: any, content: Renderable, fallback?: Renderable): Renderable {\n return _conditional(condition, content, fallback);\n}\n\n/**\n * Shows content only when `condition` is falsy.\n * It can be a plain value or a Getter to track dynamically.\n *\n * @param condition - Condition to hide or show content on.\n * @param content - Content to display when condition is falsy.\n * @param fallback - Content to display when condition is truthy.\n */\nexport function hideIf(condition: any, content: Renderable, fallback?: Renderable): Renderable {\n return _conditional(condition, fallback, content);\n}\n\n/**\n * Creates a portal that renders `content` into another element on the page.\n **/\nexport function createPortal(parent: Element, content: Renderable) {\n return createMarkup(PortalNode, { args: [content, parent] });\n}\n","// This file is a heavily modified version of the 'htm' library.\n// Original source: https://github.com/developit/htm\n//\n// --- Original htm License ---\n// Copyright 2018 Jason Miller\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport type { Markup } from \"./types.js\";\nimport { createMarkup } from \"./utils.js\";\n\nexport type Template = Markup | Markup[];\n\nconst enum Mode {\n Slash = 0,\n Text = 1,\n Whitespace = 2,\n TagName = 3,\n Comment = 4,\n PropSet = 5,\n PropAppend = 6,\n}\n\nexport function html(statics: TemplateStringsArray, ...args: any[]): Template {\n const fields = [statics, ...args];\n\n let mode = Mode.Text;\n let buffer = \"\";\n let quote = \"\";\n let current: any[] = [0];\n let propName = \"\";\n\n const commit = (field?: number) => {\n if (mode === Mode.Text && (field || (buffer = buffer.replace(/^\\s*\\n\\s*|\\s*\\n\\s*$/g, \"\")))) {\n current.push(field ? fields[field] : buffer);\n } else if (mode === Mode.TagName && (field || buffer)) {\n current[1] = field ? fields[field] : buffer;\n mode = Mode.Whitespace;\n } else if (mode === Mode.Whitespace && buffer === \"...\" && field) {\n current[2] = Object.assign(current[2] || {}, fields[field]);\n } else if (mode === Mode.Whitespace && buffer && !field) {\n (current[2] = current[2] || {})[buffer] = true;\n } else if (mode >= Mode.PropSet) {\n if (mode === Mode.PropSet) {\n (current[2] = current[2] || {})[propName] = field ? (buffer ? buffer + fields[field] : fields[field]) : buffer;\n mode = Mode.PropAppend;\n } else if (field || buffer) {\n current[2][propName] += field ? buffer + fields[field] : buffer;\n }\n }\n\n buffer = \"\";\n };\n\n for (let i = 0; i < statics.length; i++) {\n if (i) {\n if (mode === Mode.Text) commit();\n commit(i);\n }\n\n for (let j = 0; j < statics[i].length; j++) {\n const char = statics[i][j];\n\n if (mode === Mode.Text) {\n if (char === \"<\") {\n // commit buffer\n commit();\n current = [current, \"\", null];\n mode = Mode.TagName;\n } else {\n buffer += char;\n }\n } else if (mode === Mode.Comment) {\n // Ignore everything until the last three characters are '-', '-' and '>'\n if (buffer === \"--\" && char === \">\") {\n mode = Mode.Text;\n buffer = \"\";\n } else {\n buffer = char + buffer[0];\n }\n } else if (quote) {\n if (char === quote) {\n quote = \"\";\n } else {\n buffer += char;\n }\n } else if (char === '\"' || char === \"'\") {\n quote = char;\n } else if (char === \">\") {\n commit();\n mode = Mode.Text;\n } else if (!mode) {\n // Ignore everything until the tag ends\n } else if (char === \"=\") {\n mode = Mode.PropSet;\n propName = buffer;\n buffer = \"\";\n } else if (char === \"/\" && (mode < Mode.PropSet || statics[i][j + 1] === \">\")) {\n commit();\n if (mode === Mode.TagName) {\n current = current[0];\n }\n const node = current;\n current = current[0];\n\n const type = node[1];\n const props = node[2] || {};\n const children = node.slice(3);\n\n current.push(createMarkup(type, { ...props, children }));\n\n mode = Mode.Slash;\n } else if (char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\") {\n // <a disabled>\n commit();\n mode = Mode.Whitespace;\n } else {\n buffer += char;\n }\n\n if (mode === Mode.TagName && buffer === \"!--\") {\n mode = Mode.Comment;\n current = current[0];\n }\n }\n }\n commit();\n\n return current.length > 2 ? current.slice(1) : current[1];\n}\n","import { assert } from \"../utils\";\n\nexport interface Ref<T> {\n /**\n * Call with no arguments to get the stored value.\n * Throws an error if the ref is empty (this means you forgot to pass it or it was called after teardown).\n */\n (): T;\n\n /**\n * Call with an argument to initialize the value. Returns a cleanup function.\n * The cleanup function should clear any references to the DOM node.\n */\n (value: T): () => void;\n}\n\nexport function createRef<T = HTMLElement>(): Ref<T> {\n let currentValue: T | undefined;\n\n return ((...args: [T]) => {\n if (args.length) {\n currentValue = args[0];\n return () => {\n currentValue = undefined;\n };\n } else {\n // assert(currentValue !== undefined, \"Empty ref!\");\n return currentValue;\n }\n }) as Ref<T>;\n}\n"],"mappings":"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"}
|