UNPKG

@angular/router

Version:
1,386 lines (1,375 loc) 178 kB
/** * @license Angular v21.0.5 * (c) 2010-2025 Google LLC. https://angular.dev/ * License: MIT */ import { DOCUMENT, Location } from '@angular/common'; import * as i0 from '@angular/core'; import { ɵisPromise as _isPromise, ɵRuntimeError as _RuntimeError, Injectable, ɵisNgModule as _isNgModule, isStandalone, createEnvironmentInjector, InjectionToken, EventEmitter, input, inject, ViewContainerRef, ChangeDetectorRef, Directive, Input, Output, reflectComponentType, Component, ɵisInjectable as _isInjectable, runInInjectionContext, makeEnvironmentProviders, Compiler, NgModuleFactory, ɵresolveComponentResources as _resolveComponentResources, afterNextRender, signal, EnvironmentInjector, DestroyRef, untracked, ɵConsole as _Console, ɵPendingTasksInternal as _PendingTasksInternal, ɵINTERNAL_APPLICATION_ERROR_HANDLER as _INTERNAL_APPLICATION_ERROR_HANDLER, ɵformatRuntimeError as _formatRuntimeError } from '@angular/core'; import { isObservable, from, of, BehaviorSubject, combineLatest, EmptyError, Observable, concat, defer, pipe, throwError, EMPTY, Subject, Subscription } from 'rxjs'; import { first, map, switchMap, take, startWith, filter, takeUntil, mergeMap, concatMap, tap, catchError, scan, defaultIfEmpty, last as last$1, takeLast, finalize } from 'rxjs/operators'; import * as i1 from '@angular/platform-browser'; const PRIMARY_OUTLET = 'primary'; const RouteTitleKey = /* @__PURE__ */Symbol('RouteTitle'); class ParamsAsMap { params; constructor(params) { this.params = params || {}; } has(name) { return Object.prototype.hasOwnProperty.call(this.params, name); } get(name) { if (this.has(name)) { const v = this.params[name]; return Array.isArray(v) ? v[0] : v; } return null; } getAll(name) { if (this.has(name)) { const v = this.params[name]; return Array.isArray(v) ? v : [v]; } return []; } get keys() { return Object.keys(this.params); } } function convertToParamMap(params) { return new ParamsAsMap(params); } function defaultUrlMatcher(segments, segmentGroup, route) { const parts = route.path.split('/'); if (parts.length > segments.length) { return null; } if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || parts.length < segments.length)) { return null; } const posParams = {}; for (let index = 0; index < parts.length; index++) { const part = parts[index]; const segment = segments[index]; const isParameter = part[0] === ':'; if (isParameter) { posParams[part.substring(1)] = segment; } else if (part !== segment.path) { return null; } } return { consumed: segments.slice(0, parts.length), posParams }; } function firstValueFrom(source) { return new Promise((resolve, reject) => { source.pipe(first()).subscribe({ next: value => resolve(value), error: err => reject(err) }); }); } function shallowEqualArrays(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; ++i) { if (!shallowEqual(a[i], b[i])) return false; } return true; } function shallowEqual(a, b) { const k1 = a ? getDataKeys(a) : undefined; const k2 = b ? getDataKeys(b) : undefined; if (!k1 || !k2 || k1.length != k2.length) { return false; } let key; for (let i = 0; i < k1.length; i++) { key = k1[i]; if (!equalArraysOrString(a[key], b[key])) { return false; } } return true; } function getDataKeys(obj) { return [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]; } function equalArraysOrString(a, b) { if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; const aSorted = [...a].sort(); const bSorted = [...b].sort(); return aSorted.every((val, index) => bSorted[index] === val); } else { return a === b; } } function last(a) { return a.length > 0 ? a[a.length - 1] : null; } function wrapIntoObservable(value) { if (isObservable(value)) { return value; } if (_isPromise(value)) { return from(Promise.resolve(value)); } return of(value); } function wrapIntoPromise(value) { if (isObservable(value)) { return firstValueFrom(value); } return Promise.resolve(value); } const pathCompareMap = { 'exact': equalSegmentGroups, 'subset': containsSegmentGroup }; const paramCompareMap = { 'exact': equalParams, 'subset': containsParams, 'ignored': () => true }; function containsTree(container, containee, options) { return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) && paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) && !(options.fragment === 'exact' && container.fragment !== containee.fragment); } function equalParams(container, containee) { return shallowEqual(container, containee); } function equalSegmentGroups(container, containee, matrixParams) { if (!equalPath(container.segments, containee.segments)) return false; if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) { return false; } if (container.numberOfChildren !== containee.numberOfChildren) return false; for (const c in containee.children) { if (!container.children[c]) return false; if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams)) return false; } return true; } function containsParams(container, containee) { return Object.keys(containee).length <= Object.keys(container).length && Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key])); } function containsSegmentGroup(container, containee, matrixParams) { return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams); } function containsSegmentGroupHelper(container, containee, containeePaths, matrixParams) { if (container.segments.length > containeePaths.length) { const current = container.segments.slice(0, containeePaths.length); if (!equalPath(current, containeePaths)) return false; if (containee.hasChildren()) return false; if (!matrixParamsMatch(current, containeePaths, matrixParams)) return false; return true; } else if (container.segments.length === containeePaths.length) { if (!equalPath(container.segments, containeePaths)) return false; if (!matrixParamsMatch(container.segments, containeePaths, matrixParams)) return false; for (const c in containee.children) { if (!container.children[c]) return false; if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) { return false; } } return true; } else { const current = containeePaths.slice(0, container.segments.length); const next = containeePaths.slice(container.segments.length); if (!equalPath(container.segments, current)) return false; if (!matrixParamsMatch(container.segments, current, matrixParams)) return false; if (!container.children[PRIMARY_OUTLET]) return false; return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams); } } function matrixParamsMatch(containerPaths, containeePaths, options) { return containeePaths.every((containeeSegment, i) => { return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters); }); } class UrlTree { root; queryParams; fragment; _queryParamMap; constructor(root = new UrlSegmentGroup([], {}), queryParams = {}, fragment = null) { this.root = root; this.queryParams = queryParams; this.fragment = fragment; if (typeof ngDevMode === 'undefined' || ngDevMode) { if (root.segments.length > 0) { throw new _RuntimeError(4015, 'The root `UrlSegmentGroup` should not contain `segments`. ' + 'Instead, these segments belong in the `children` so they can be associated with a named outlet.'); } } } get queryParamMap() { this._queryParamMap ??= convertToParamMap(this.queryParams); return this._queryParamMap; } toString() { return DEFAULT_SERIALIZER.serialize(this); } } class UrlSegmentGroup { segments; children; parent = null; constructor(segments, children) { this.segments = segments; this.children = children; Object.values(children).forEach(v => v.parent = this); } hasChildren() { return this.numberOfChildren > 0; } get numberOfChildren() { return Object.keys(this.children).length; } toString() { return serializePaths(this); } } class UrlSegment { path; parameters; _parameterMap; constructor(path, parameters) { this.path = path; this.parameters = parameters; } get parameterMap() { this._parameterMap ??= convertToParamMap(this.parameters); return this._parameterMap; } toString() { return serializePath(this); } } function equalSegments(as, bs) { return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters)); } function equalPath(as, bs) { if (as.length !== bs.length) return false; return as.every((a, i) => a.path === bs[i].path); } function mapChildrenIntoArray(segment, fn) { let res = []; Object.entries(segment.children).forEach(([childOutlet, child]) => { if (childOutlet === PRIMARY_OUTLET) { res = res.concat(fn(child, childOutlet)); } }); Object.entries(segment.children).forEach(([childOutlet, child]) => { if (childOutlet !== PRIMARY_OUTLET) { res = res.concat(fn(child, childOutlet)); } }); return res; } class UrlSerializer { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.5", ngImport: i0, type: UrlSerializer, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.5", ngImport: i0, type: UrlSerializer, providedIn: 'root', useFactory: () => new DefaultUrlSerializer() }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.5", ngImport: i0, type: UrlSerializer, decorators: [{ type: Injectable, args: [{ providedIn: 'root', useFactory: () => new DefaultUrlSerializer() }] }] }); class DefaultUrlSerializer { parse(url) { const p = new UrlParser(url); return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment()); } serialize(tree) { const segment = `/${serializeSegment(tree.root, true)}`; const query = serializeQueryParams(tree.queryParams); const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : ''; return `${segment}${query}${fragment}`; } } const DEFAULT_SERIALIZER = new DefaultUrlSerializer(); function serializePaths(segment) { return segment.segments.map(p => serializePath(p)).join('/'); } function serializeSegment(segment, root) { if (!segment.hasChildren()) { return serializePaths(segment); } if (root) { const primary = segment.children[PRIMARY_OUTLET] ? serializeSegment(segment.children[PRIMARY_OUTLET], false) : ''; const children = []; Object.entries(segment.children).forEach(([k, v]) => { if (k !== PRIMARY_OUTLET) { children.push(`${k}:${serializeSegment(v, false)}`); } }); return children.length > 0 ? `${primary}(${children.join('//')})` : primary; } else { const children = mapChildrenIntoArray(segment, (v, k) => { if (k === PRIMARY_OUTLET) { return [serializeSegment(segment.children[PRIMARY_OUTLET], false)]; } return [`${k}:${serializeSegment(v, false)}`]; }); if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) { return `${serializePaths(segment)}/${children[0]}`; } return `${serializePaths(segment)}/(${children.join('//')})`; } } function encodeUriString(s) { return encodeURIComponent(s).replace(/%40/g, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ','); } function encodeUriQuery(s) { return encodeUriString(s).replace(/%3B/gi, ';'); } function encodeUriFragment(s) { return encodeURI(s); } function encodeUriSegment(s) { return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&'); } function decode(s) { return decodeURIComponent(s); } function decodeQuery(s) { return decode(s.replace(/\+/g, '%20')); } function serializePath(path) { return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`; } function serializeMatrixParams(params) { return Object.entries(params).map(([key, value]) => `;${encodeUriSegment(key)}=${encodeUriSegment(value)}`).join(''); } function serializeQueryParams(params) { const strParams = Object.entries(params).map(([name, value]) => { return Array.isArray(value) ? value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') : `${encodeUriQuery(name)}=${encodeUriQuery(value)}`; }).filter(s => s); return strParams.length ? `?${strParams.join('&')}` : ''; } const SEGMENT_RE = /^[^\/()?;#]+/; function matchSegments(str) { const match = str.match(SEGMENT_RE); return match ? match[0] : ''; } const MATRIX_PARAM_SEGMENT_RE = /^[^\/()?;=#]+/; function matchMatrixKeySegments(str) { const match = str.match(MATRIX_PARAM_SEGMENT_RE); return match ? match[0] : ''; } const QUERY_PARAM_RE = /^[^=?&#]+/; function matchQueryParams(str) { const match = str.match(QUERY_PARAM_RE); return match ? match[0] : ''; } const QUERY_PARAM_VALUE_RE = /^[^&#]+/; function matchUrlQueryParamValue(str) { const match = str.match(QUERY_PARAM_VALUE_RE); return match ? match[0] : ''; } class UrlParser { url; remaining; constructor(url) { this.url = url; this.remaining = url; } parseRootSegment() { this.consumeOptional('/'); if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) { return new UrlSegmentGroup([], {}); } return new UrlSegmentGroup([], this.parseChildren()); } parseQueryParams() { const params = {}; if (this.consumeOptional('?')) { do { this.parseQueryParam(params); } while (this.consumeOptional('&')); } return params; } parseFragment() { return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null; } parseChildren() { if (this.remaining === '') { return {}; } this.consumeOptional('/'); const segments = []; if (!this.peekStartsWith('(')) { segments.push(this.parseSegment()); } while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) { this.capture('/'); segments.push(this.parseSegment()); } let children = {}; if (this.peekStartsWith('/(')) { this.capture('/'); children = this.parseParens(true); } let res = {}; if (this.peekStartsWith('(')) { res = this.parseParens(false); } if (segments.length > 0 || Object.keys(children).length > 0) { res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children); } return res; } parseSegment() { const path = matchSegments(this.remaining); if (path === '' && this.peekStartsWith(';')) { throw new _RuntimeError(4009, (typeof ngDevMode === 'undefined' || ngDevMode) && `Empty path url segment cannot have parameters: '${this.remaining}'.`); } this.capture(path); return new UrlSegment(decode(path), this.parseMatrixParams()); } parseMatrixParams() { const params = {}; while (this.consumeOptional(';')) { this.parseParam(params); } return params; } parseParam(params) { const key = matchMatrixKeySegments(this.remaining); if (!key) { return; } this.capture(key); let value = ''; if (this.consumeOptional('=')) { const valueMatch = matchSegments(this.remaining); if (valueMatch) { value = valueMatch; this.capture(value); } } params[decode(key)] = decode(value); } parseQueryParam(params) { const key = matchQueryParams(this.remaining); if (!key) { return; } this.capture(key); let value = ''; if (this.consumeOptional('=')) { const valueMatch = matchUrlQueryParamValue(this.remaining); if (valueMatch) { value = valueMatch; this.capture(value); } } const decodedKey = decodeQuery(key); const decodedVal = decodeQuery(value); if (params.hasOwnProperty(decodedKey)) { let currentVal = params[decodedKey]; if (!Array.isArray(currentVal)) { currentVal = [currentVal]; params[decodedKey] = currentVal; } currentVal.push(decodedVal); } else { params[decodedKey] = decodedVal; } } parseParens(allowPrimary) { const segments = {}; this.capture('('); while (!this.consumeOptional(')') && this.remaining.length > 0) { const path = matchSegments(this.remaining); const next = this.remaining[path.length]; if (next !== '/' && next !== ')' && next !== ';') { throw new _RuntimeError(4010, (typeof ngDevMode === 'undefined' || ngDevMode) && `Cannot parse url '${this.url}'`); } let outletName; if (path.indexOf(':') > -1) { outletName = path.slice(0, path.indexOf(':')); this.capture(outletName); this.capture(':'); } else if (allowPrimary) { outletName = PRIMARY_OUTLET; } const children = this.parseChildren(); segments[outletName ?? PRIMARY_OUTLET] = Object.keys(children).length === 1 && children[PRIMARY_OUTLET] ? children[PRIMARY_OUTLET] : new UrlSegmentGroup([], children); this.consumeOptional('//'); } return segments; } peekStartsWith(str) { return this.remaining.startsWith(str); } consumeOptional(str) { if (this.peekStartsWith(str)) { this.remaining = this.remaining.substring(str.length); return true; } return false; } capture(str) { if (!this.consumeOptional(str)) { throw new _RuntimeError(4011, (typeof ngDevMode === 'undefined' || ngDevMode) && `Expected "${str}".`); } } } function createRoot(rootCandidate) { return rootCandidate.segments.length > 0 ? new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) : rootCandidate; } function squashSegmentGroup(segmentGroup) { const newChildren = {}; for (const [childOutlet, child] of Object.entries(segmentGroup.children)) { const childCandidate = squashSegmentGroup(child); if (childOutlet === PRIMARY_OUTLET && childCandidate.segments.length === 0 && childCandidate.hasChildren()) { for (const [grandChildOutlet, grandChild] of Object.entries(childCandidate.children)) { newChildren[grandChildOutlet] = grandChild; } } else if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) { newChildren[childOutlet] = childCandidate; } } const s = new UrlSegmentGroup(segmentGroup.segments, newChildren); return mergeTrivialChildren(s); } function mergeTrivialChildren(s) { if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) { const c = s.children[PRIMARY_OUTLET]; return new UrlSegmentGroup(s.segments.concat(c.segments), c.children); } return s; } function isUrlTree(v) { return v instanceof UrlTree; } function createUrlTreeFromSnapshot(relativeTo, commands, queryParams = null, fragment = null, urlSerializer = new DefaultUrlSerializer()) { const relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeTo); return createUrlTreeFromSegmentGroup(relativeToUrlSegmentGroup, commands, queryParams, fragment, urlSerializer); } function createSegmentGroupFromRoute(route) { let targetGroup; function createSegmentGroupFromRouteRecursive(currentRoute) { const childOutlets = {}; for (const childSnapshot of currentRoute.children) { const root = createSegmentGroupFromRouteRecursive(childSnapshot); childOutlets[childSnapshot.outlet] = root; } const segmentGroup = new UrlSegmentGroup(currentRoute.url, childOutlets); if (currentRoute === route) { targetGroup = segmentGroup; } return segmentGroup; } const rootCandidate = createSegmentGroupFromRouteRecursive(route.root); const rootSegmentGroup = createRoot(rootCandidate); return targetGroup ?? rootSegmentGroup; } function createUrlTreeFromSegmentGroup(relativeTo, commands, queryParams, fragment, urlSerializer) { let root = relativeTo; while (root.parent) { root = root.parent; } if (commands.length === 0) { return tree(root, root, root, queryParams, fragment, urlSerializer); } const nav = computeNavigation(commands); if (nav.toRoot()) { return tree(root, root, new UrlSegmentGroup([], {}), queryParams, fragment, urlSerializer); } const position = findStartingPositionForTargetGroup(nav, root, relativeTo); const newSegmentGroup = position.processChildren ? updateSegmentGroupChildren(position.segmentGroup, position.index, nav.commands) : updateSegmentGroup(position.segmentGroup, position.index, nav.commands); return tree(root, position.segmentGroup, newSegmentGroup, queryParams, fragment, urlSerializer); } function isMatrixParams(command) { return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath; } function isCommandWithOutlets(command) { return typeof command === 'object' && command != null && command.outlets; } function normalizeQueryParams(k, v, urlSerializer) { k ||= 'ɵ'; const tree = new UrlTree(); tree.queryParams = { [k]: v }; return urlSerializer.parse(urlSerializer.serialize(tree)).queryParams[k]; } function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment, urlSerializer) { const qp = {}; for (const [key, value] of Object.entries(queryParams ?? {})) { qp[key] = Array.isArray(value) ? value.map(v => normalizeQueryParams(key, v, urlSerializer)) : normalizeQueryParams(key, value, urlSerializer); } let rootCandidate; if (oldRoot === oldSegmentGroup) { rootCandidate = newSegmentGroup; } else { rootCandidate = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup); } const newRoot = createRoot(squashSegmentGroup(rootCandidate)); return new UrlTree(newRoot, qp, fragment); } function replaceSegment(current, oldSegment, newSegment) { const children = {}; Object.entries(current.children).forEach(([outletName, c]) => { if (c === oldSegment) { children[outletName] = newSegment; } else { children[outletName] = replaceSegment(c, oldSegment, newSegment); } }); return new UrlSegmentGroup(current.segments, children); } class Navigation { isAbsolute; numberOfDoubleDots; commands; constructor(isAbsolute, numberOfDoubleDots, commands) { this.isAbsolute = isAbsolute; this.numberOfDoubleDots = numberOfDoubleDots; this.commands = commands; if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) { throw new _RuntimeError(4003, (typeof ngDevMode === 'undefined' || ngDevMode) && 'Root segment cannot have matrix parameters'); } const cmdWithOutlet = commands.find(isCommandWithOutlets); if (cmdWithOutlet && cmdWithOutlet !== last(commands)) { throw new _RuntimeError(4004, (typeof ngDevMode === 'undefined' || ngDevMode) && '{outlets:{}} has to be the last command'); } } toRoot() { return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/'; } } function computeNavigation(commands) { if (typeof commands[0] === 'string' && commands.length === 1 && commands[0] === '/') { return new Navigation(true, 0, commands); } let numberOfDoubleDots = 0; let isAbsolute = false; const res = commands.reduce((res, cmd, cmdIdx) => { if (typeof cmd === 'object' && cmd != null) { if (cmd.outlets) { const outlets = {}; Object.entries(cmd.outlets).forEach(([name, commands]) => { outlets[name] = typeof commands === 'string' ? commands.split('/') : commands; }); return [...res, { outlets }]; } if (cmd.segmentPath) { return [...res, cmd.segmentPath]; } } if (!(typeof cmd === 'string')) { return [...res, cmd]; } if (cmdIdx === 0) { cmd.split('/').forEach((urlPart, partIndex) => { if (partIndex == 0 && urlPart === '.') ; else if (partIndex == 0 && urlPart === '') { isAbsolute = true; } else if (urlPart === '..') { numberOfDoubleDots++; } else if (urlPart != '') { res.push(urlPart); } }); return res; } return [...res, cmd]; }, []); return new Navigation(isAbsolute, numberOfDoubleDots, res); } class Position { segmentGroup; processChildren; index; constructor(segmentGroup, processChildren, index) { this.segmentGroup = segmentGroup; this.processChildren = processChildren; this.index = index; } } function findStartingPositionForTargetGroup(nav, root, target) { if (nav.isAbsolute) { return new Position(root, true, 0); } if (!target) { return new Position(root, false, NaN); } if (target.parent === null) { return new Position(target, true, 0); } const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1; const index = target.segments.length - 1 + modifier; return createPositionApplyingDoubleDots(target, index, nav.numberOfDoubleDots); } function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) { let g = group; let ci = index; let dd = numberOfDoubleDots; while (dd > ci) { dd -= ci; g = g.parent; if (!g) { throw new _RuntimeError(4005, (typeof ngDevMode === 'undefined' || ngDevMode) && "Invalid number of '../'"); } ci = g.segments.length; } return new Position(g, false, ci - dd); } function getOutlets(commands) { if (isCommandWithOutlets(commands[0])) { return commands[0].outlets; } return { [PRIMARY_OUTLET]: commands }; } function updateSegmentGroup(segmentGroup, startIndex, commands) { segmentGroup ??= new UrlSegmentGroup([], {}); if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) { return updateSegmentGroupChildren(segmentGroup, startIndex, commands); } const m = prefixedWith(segmentGroup, startIndex, commands); const slicedCommands = commands.slice(m.commandIndex); if (m.match && m.pathIndex < segmentGroup.segments.length) { const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {}); g.children[PRIMARY_OUTLET] = new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children); return updateSegmentGroupChildren(g, 0, slicedCommands); } else if (m.match && slicedCommands.length === 0) { return new UrlSegmentGroup(segmentGroup.segments, {}); } else if (m.match && !segmentGroup.hasChildren()) { return createNewSegmentGroup(segmentGroup, startIndex, commands); } else if (m.match) { return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands); } else { return createNewSegmentGroup(segmentGroup, startIndex, commands); } } function updateSegmentGroupChildren(segmentGroup, startIndex, commands) { if (commands.length === 0) { return new UrlSegmentGroup(segmentGroup.segments, {}); } else { const outlets = getOutlets(commands); const children = {}; if (Object.keys(outlets).some(o => o !== PRIMARY_OUTLET) && segmentGroup.children[PRIMARY_OUTLET] && segmentGroup.numberOfChildren === 1 && segmentGroup.children[PRIMARY_OUTLET].segments.length === 0) { const childrenOfEmptyChild = updateSegmentGroupChildren(segmentGroup.children[PRIMARY_OUTLET], startIndex, commands); return new UrlSegmentGroup(segmentGroup.segments, childrenOfEmptyChild.children); } Object.entries(outlets).forEach(([outlet, commands]) => { if (typeof commands === 'string') { commands = [commands]; } if (commands !== null) { children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands); } }); Object.entries(segmentGroup.children).forEach(([childOutlet, child]) => { if (outlets[childOutlet] === undefined) { children[childOutlet] = child; } }); return new UrlSegmentGroup(segmentGroup.segments, children); } } function prefixedWith(segmentGroup, startIndex, commands) { let currentCommandIndex = 0; let currentPathIndex = startIndex; const noMatch = { match: false, pathIndex: 0, commandIndex: 0 }; while (currentPathIndex < segmentGroup.segments.length) { if (currentCommandIndex >= commands.length) return noMatch; const path = segmentGroup.segments[currentPathIndex]; const command = commands[currentCommandIndex]; if (isCommandWithOutlets(command)) { break; } const curr = `${command}`; const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null; if (currentPathIndex > 0 && curr === undefined) break; if (curr && next && typeof next === 'object' && next.outlets === undefined) { if (!compare(curr, next, path)) return noMatch; currentCommandIndex += 2; } else { if (!compare(curr, {}, path)) return noMatch; currentCommandIndex++; } currentPathIndex++; } return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex }; } function createNewSegmentGroup(segmentGroup, startIndex, commands) { const paths = segmentGroup.segments.slice(0, startIndex); let i = 0; while (i < commands.length) { const command = commands[i]; if (isCommandWithOutlets(command)) { const children = createNewSegmentChildren(command.outlets); return new UrlSegmentGroup(paths, children); } if (i === 0 && isMatrixParams(commands[0])) { const p = segmentGroup.segments[startIndex]; paths.push(new UrlSegment(p.path, stringify(commands[0]))); i++; continue; } const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`; const next = i < commands.length - 1 ? commands[i + 1] : null; if (curr && next && isMatrixParams(next)) { paths.push(new UrlSegment(curr, stringify(next))); i += 2; } else { paths.push(new UrlSegment(curr, {})); i++; } } return new UrlSegmentGroup(paths, {}); } function createNewSegmentChildren(outlets) { const children = {}; Object.entries(outlets).forEach(([outlet, commands]) => { if (typeof commands === 'string') { commands = [commands]; } if (commands !== null) { children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands); } }); return children; } function stringify(params) { const res = {}; Object.entries(params).forEach(([k, v]) => res[k] = `${v}`); return res; } function compare(path, params, segment) { return path == segment.path && shallowEqual(params, segment.parameters); } const IMPERATIVE_NAVIGATION = 'imperative'; var EventType; (function (EventType) { EventType[EventType["NavigationStart"] = 0] = "NavigationStart"; EventType[EventType["NavigationEnd"] = 1] = "NavigationEnd"; EventType[EventType["NavigationCancel"] = 2] = "NavigationCancel"; EventType[EventType["NavigationError"] = 3] = "NavigationError"; EventType[EventType["RoutesRecognized"] = 4] = "RoutesRecognized"; EventType[EventType["ResolveStart"] = 5] = "ResolveStart"; EventType[EventType["ResolveEnd"] = 6] = "ResolveEnd"; EventType[EventType["GuardsCheckStart"] = 7] = "GuardsCheckStart"; EventType[EventType["GuardsCheckEnd"] = 8] = "GuardsCheckEnd"; EventType[EventType["RouteConfigLoadStart"] = 9] = "RouteConfigLoadStart"; EventType[EventType["RouteConfigLoadEnd"] = 10] = "RouteConfigLoadEnd"; EventType[EventType["ChildActivationStart"] = 11] = "ChildActivationStart"; EventType[EventType["ChildActivationEnd"] = 12] = "ChildActivationEnd"; EventType[EventType["ActivationStart"] = 13] = "ActivationStart"; EventType[EventType["ActivationEnd"] = 14] = "ActivationEnd"; EventType[EventType["Scroll"] = 15] = "Scroll"; EventType[EventType["NavigationSkipped"] = 16] = "NavigationSkipped"; })(EventType || (EventType = {})); class RouterEvent { id; url; constructor(id, url) { this.id = id; this.url = url; } } class NavigationStart extends RouterEvent { type = EventType.NavigationStart; navigationTrigger; restoredState; constructor(id, url, navigationTrigger = 'imperative', restoredState = null) { super(id, url); this.navigationTrigger = navigationTrigger; this.restoredState = restoredState; } toString() { return `NavigationStart(id: ${this.id}, url: '${this.url}')`; } } class NavigationEnd extends RouterEvent { urlAfterRedirects; type = EventType.NavigationEnd; constructor(id, url, urlAfterRedirects) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; } toString() { return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`; } } var NavigationCancellationCode; (function (NavigationCancellationCode) { NavigationCancellationCode[NavigationCancellationCode["Redirect"] = 0] = "Redirect"; NavigationCancellationCode[NavigationCancellationCode["SupersededByNewNavigation"] = 1] = "SupersededByNewNavigation"; NavigationCancellationCode[NavigationCancellationCode["NoDataFromResolver"] = 2] = "NoDataFromResolver"; NavigationCancellationCode[NavigationCancellationCode["GuardRejected"] = 3] = "GuardRejected"; NavigationCancellationCode[NavigationCancellationCode["Aborted"] = 4] = "Aborted"; })(NavigationCancellationCode || (NavigationCancellationCode = {})); var NavigationSkippedCode; (function (NavigationSkippedCode) { NavigationSkippedCode[NavigationSkippedCode["IgnoredSameUrlNavigation"] = 0] = "IgnoredSameUrlNavigation"; NavigationSkippedCode[NavigationSkippedCode["IgnoredByUrlHandlingStrategy"] = 1] = "IgnoredByUrlHandlingStrategy"; })(NavigationSkippedCode || (NavigationSkippedCode = {})); class NavigationCancel extends RouterEvent { reason; code; type = EventType.NavigationCancel; constructor(id, url, reason, code) { super(id, url); this.reason = reason; this.code = code; } toString() { return `NavigationCancel(id: ${this.id}, url: '${this.url}')`; } } class NavigationSkipped extends RouterEvent { reason; code; type = EventType.NavigationSkipped; constructor(id, url, reason, code) { super(id, url); this.reason = reason; this.code = code; } } class NavigationError extends RouterEvent { error; target; type = EventType.NavigationError; constructor(id, url, error, target) { super(id, url); this.error = error; this.target = target; } toString() { return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`; } } class RoutesRecognized extends RouterEvent { urlAfterRedirects; state; type = EventType.RoutesRecognized; constructor(id, url, urlAfterRedirects, state) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; } toString() { return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } class GuardsCheckStart extends RouterEvent { urlAfterRedirects; state; type = EventType.GuardsCheckStart; constructor(id, url, urlAfterRedirects, state) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; } toString() { return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } class GuardsCheckEnd extends RouterEvent { urlAfterRedirects; state; shouldActivate; type = EventType.GuardsCheckEnd; constructor(id, url, urlAfterRedirects, state, shouldActivate) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; this.shouldActivate = shouldActivate; } toString() { return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`; } } class ResolveStart extends RouterEvent { urlAfterRedirects; state; type = EventType.ResolveStart; constructor(id, url, urlAfterRedirects, state) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; } toString() { return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } class ResolveEnd extends RouterEvent { urlAfterRedirects; state; type = EventType.ResolveEnd; constructor(id, url, urlAfterRedirects, state) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; } toString() { return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } class RouteConfigLoadStart { route; type = EventType.RouteConfigLoadStart; constructor(route) { this.route = route; } toString() { return `RouteConfigLoadStart(path: ${this.route.path})`; } } class RouteConfigLoadEnd { route; type = EventType.RouteConfigLoadEnd; constructor(route) { this.route = route; } toString() { return `RouteConfigLoadEnd(path: ${this.route.path})`; } } class ChildActivationStart { snapshot; type = EventType.ChildActivationStart; constructor(snapshot) { this.snapshot = snapshot; } toString() { const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return `ChildActivationStart(path: '${path}')`; } } class ChildActivationEnd { snapshot; type = EventType.ChildActivationEnd; constructor(snapshot) { this.snapshot = snapshot; } toString() { const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return `ChildActivationEnd(path: '${path}')`; } } class ActivationStart { snapshot; type = EventType.ActivationStart; constructor(snapshot) { this.snapshot = snapshot; } toString() { const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return `ActivationStart(path: '${path}')`; } } class ActivationEnd { snapshot; type = EventType.ActivationEnd; constructor(snapshot) { this.snapshot = snapshot; } toString() { const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return `ActivationEnd(path: '${path}')`; } } class Scroll { routerEvent; position; anchor; type = EventType.Scroll; constructor(routerEvent, position, anchor) { this.routerEvent = routerEvent; this.position = position; this.anchor = anchor; } toString() { const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null; return `Scroll(anchor: '${this.anchor}', position: '${pos}')`; } } class BeforeActivateRoutes {} class RedirectRequest { url; navigationBehaviorOptions; constructor(url, navigationBehaviorOptions) { this.url = url; this.navigationBehaviorOptions = navigationBehaviorOptions; } } function isPublicRouterEvent(e) { return !(e instanceof BeforeActivateRoutes) && !(e instanceof RedirectRequest); } function stringifyEvent(routerEvent) { switch (routerEvent.type) { case EventType.ActivationEnd: return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case EventType.ActivationStart: return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case EventType.ChildActivationEnd: return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case EventType.ChildActivationStart: return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case EventType.GuardsCheckEnd: return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`; case EventType.GuardsCheckStart: return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case EventType.NavigationCancel: return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case EventType.NavigationSkipped: return `NavigationSkipped(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case EventType.NavigationEnd: return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`; case EventType.NavigationError: return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`; case EventType.NavigationStart: return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case EventType.ResolveEnd: return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case EventType.ResolveStart: return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case EventType.RouteConfigLoadEnd: return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`; case EventType.RouteConfigLoadStart: return `RouteConfigLoadStart(path: ${routerEvent.route.path})`; case EventType.RoutesRecognized: return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case EventType.Scroll: const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null; return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`; } } function getOrCreateRouteInjectorIfNeeded(route, currentInjector) { if (route.providers && !route._injector) { route._injector = createEnvironmentInjector(route.providers, currentInjector, `Route: ${route.path}`); } return route._injector ?? currentInjector; } function validateConfig(config, parentPath = '', requireStandaloneComponents = false) { for (let i = 0; i < config.length; i++) { const route = config[i]; const fullPath = getFullPath(parentPath, route); validateNode(route, fullPath, requireStandaloneComponents); } } function assertStandalone(fullPath, component) { if (component && _isNgModule(component)) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}'. You are using 'loadComponent' with a module, ` + `but it must be used with standalone components. Use 'loadChildren' instead.`); } else if (component && !isStandalone(component)) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}'. The component must be standalone.`); } } function validateNode(route, fullPath, requireStandaloneComponents) { if (typeof ngDevMode === 'undefined' || ngDevMode) { if (!route) { throw new _RuntimeError(4014, ` Invalid configuration of route '${fullPath}': Encountered undefined route. The reason might be an extra comma. Example: const routes: Routes = [ { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, { path: 'dashboard', component: DashboardComponent },, << two commas { path: 'detail/:id', component: HeroDetailComponent } ]; `); } if (Array.isArray(route)) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': Array cannot be specified`); } if (!route.redirectTo && !route.component && !route.loadComponent && !route.children && !route.loadChildren && route.outlet && route.outlet !== PRIMARY_OUTLET) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`); } if (route.redirectTo && route.children) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`); } if (route.redirectTo && route.loadChildren) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`); } if (route.children && route.loadChildren) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`); } if (route.component && route.loadComponent) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': component and loadComponent cannot be used together`); } if (route.redirectTo) { if (route.component || route.loadComponent) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': redirectTo and component/loadComponent cannot be used together`); } if (route.canMatch || route.canActivate) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': redirectTo and ${route.canMatch ? 'canMatch' : 'canActivate'} cannot be used together.` + `Redirects happen before guards are executed.`); } } if (route.path && route.matcher) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': path and matcher cannot be used together`); } if (route.redirectTo === void 0 && !route.component && !route.loadComponent && !route.children && !route.loadChildren) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}'. One of the following must be provided: component, loadComponent, redirectTo, children or loadChildren`); } if (route.path === void 0 && route.matcher === void 0) { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`); } if (typeof route.path === 'string' && route.path.charAt(0) === '/') { throw new _RuntimeError(4014, `Invalid configuration of route '${fullPath}': path cannot start with a slash`); } if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) { const exp = `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`; throw new _RuntimeError(4014, `Invalid configuration of route '{path: "${fullPath}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`); } if (requireStandaloneComponents) { assertStandalone(fullPath, route.component); } } if (route.children) { validateConfig(route.children, fullPath, requireStandaloneComponents); } } function getFullPath(parentPath, currentRoute) { if (!currentRoute) { return parentPath; } if (!parentPath && !currentRoute.path) { return ''; } else if (parentPath && !currentRoute.path) { return `${parentPath}/`; } else if (!parentPath && currentRoute.path) { return currentRoute.path; } else { return `${parentPath}/${currentRoute.path}`; } } function getOutlet(route) { return route.outlet || PRIMARY_OUTLET; } function sortByMatchingOutlets(routes, outletName) { const sortedConfig = routes.filter(r => getOutlet(r) === outletName); sortedConfig.push(...routes.filter(r => getOutlet(r) !== outletName)); return sortedConfig; } function getClosestRouteInjector(snapshot) { if (!snapshot) return null; if (snapshot.routeConfig?._injector) { return snapshot.routeConfig._injector; } for (let s = snapshot.parent; s; s = s.parent) { const route = s.routeConfig; if (route?._loadedInjector) return route._loadedInjector; if (route?._injector) return route._injector; } return null; } class OutletContext { rootInjector; outlet = null; route = null; children; attachRef = null; get injector() { return getClosestRouteInjector(this.route?.snapshot) ?? this.rootInjector; } constructor(rootInjector) { this.rootInjector = rootInjector; this.children = new ChildrenOutletContexts(this.rootInjector); } } class ChildrenOutletContexts { rootInjector; contexts = new Map(); constructor(rootInjector) { this.rootInjector = rootInjector; } onChildOutletCreated(childName, outlet) { const context = this.getOrCreateContext(childName); context.outlet = outlet; this.contexts.set(childName, context); } onChildOutletDestroyed(childName) { const context = this.getContext(childName); if (context) { context.outlet = null; context.attachRef = null; } } onOutletDeactivated() { const contexts = this.contexts; this.contexts = new Map(); return contexts; } onOutletReAttached(contexts) { this.contexts = contexts; } getOrCreateContext(childName) { let context = this.getContext(childName); if (!context) { context = new OutletContext(this.rootInjector); this.contexts.set(childName, context); } return context; } getContext(childName) { return this.contexts.get(childName) || null; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.5", ngImport: i0, type: ChildrenOutletContexts, deps: [{ token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.5", ngImport: i0, type: ChildrenOutletContexts, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.5", ngImport: i0, type: ChildrenOutletContexts, decorators: [{