@tanstack/router-core 1.131.21 → 1.131.23
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/cjs/load-matches.cjs +70 -74
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/router.cjs +2 -2
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/utils.cjs +8 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +1 -0
- package/dist/esm/load-matches.js +70 -74
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/router.js +3 -3
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js +8 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/load-matches.ts +102 -112
- package/src/router.ts +5 -6
- package/src/utils.ts +11 -0
package/dist/esm/utils.js
CHANGED
|
@@ -159,10 +159,18 @@ function isPromise(value) {
|
|
|
159
159
|
value && typeof value === "object" && typeof value.then === "function"
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
|
+
function findLast(array, predicate) {
|
|
163
|
+
for (let i = array.length - 1; i >= 0; i--) {
|
|
164
|
+
const item = array[i];
|
|
165
|
+
if (predicate(item)) return item;
|
|
166
|
+
}
|
|
167
|
+
return void 0;
|
|
168
|
+
}
|
|
162
169
|
export {
|
|
163
170
|
createControlledPromise,
|
|
164
171
|
deepEqual,
|
|
165
172
|
escapeJSON,
|
|
173
|
+
findLast,
|
|
166
174
|
functionalUpdate,
|
|
167
175
|
isModuleNotFoundError,
|
|
168
176
|
isPlainArray,
|
package/dist/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type { RouteIds } from './routeInfo'\nimport type { AnyRouter } from './router'\n\nexport type Awaitable<T> = T | Promise<T>\nexport type NoInfer<T> = [T][T extends any ? 0 : never]\nexport type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue\n ? TYesResult\n : TNoResult\n\nexport type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<\n TValue,\n TKey\n> &\n Required<Pick<TValue, TKey>>\n\nexport type PickRequired<T> = {\n [K in keyof T as undefined extends T[K] ? never : K]: T[K]\n}\n\nexport type PickOptional<T> = {\n [K in keyof T as undefined extends T[K] ? K : never]: T[K]\n}\n\n// from https://stackoverflow.com/a/76458160\nexport type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never\n\nexport type Expand<T> = T extends object\n ? T extends infer O\n ? O extends Function\n ? O\n : { [K in keyof O]: O[K] }\n : never\n : T\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n\nexport type MakeDifferenceOptional<TLeft, TRight> = keyof TLeft &\n keyof TRight extends never\n ? TRight\n : Omit<TRight, keyof TLeft & keyof TRight> & {\n [K in keyof TLeft & keyof TRight]?: TRight[K]\n }\n\n// from https://stackoverflow.com/a/53955431\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport type IsUnion<T, U extends T = T> = (\n T extends any ? (U extends T ? false : true) : never\n) extends false\n ? false\n : true\n\nexport type IsNonEmptyObject<T> = T extends object\n ? keyof T extends never\n ? false\n : true\n : false\n\nexport type Assign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : keyof TLeft & keyof TRight extends never\n ? TLeft & TRight\n : Omit<TLeft, keyof TRight> & TRight\n : never\n : never\n\nexport type IntersectAssign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : TRight & TLeft\n : never\n : never\n\nexport type Timeout = ReturnType<typeof setTimeout>\n\nexport type Updater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev?: TPrevious) => TResult)\n\nexport type NonNullableUpdater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev: TPrevious) => TResult)\n\nexport type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive\n ? never\n : TUnion\n\nexport type PartialMergeAllObject<TUnion> =\n ExtractObjects<TUnion> extends infer TObj\n ? [TObj] extends [never]\n ? never\n : {\n [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any\n ? TKey extends keyof TObj\n ? TObj[TKey]\n : never\n : never\n }\n : never\n\nexport type MergeAllPrimitive =\n | ReadonlyArray<any>\n | number\n | string\n | bigint\n | boolean\n | symbol\n | undefined\n | null\n\nexport type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive\n ? TUnion\n : TUnion extends object\n ? never\n : TUnion\n\nexport type PartialMergeAll<TUnion> =\n | ExtractPrimitives<TUnion>\n | PartialMergeAllObject<TUnion>\n\nexport type Constrain<T, TConstraint, TDefault = TConstraint> =\n | (T extends TConstraint ? T : never)\n | TDefault\n\nexport type ConstrainLiteral<T, TConstraint, TDefault = TConstraint> =\n | (T & TConstraint)\n | TDefault\n\n/**\n * To be added to router types\n */\nexport type UnionToIntersection<T> = (\n T extends any ? (arg: T) => any : never\n) extends (arg: infer T) => any\n ? T\n : never\n\n/**\n * Merges everything in a union into one object.\n * This mapped type is homomorphic which means it preserves stuff! :)\n */\nexport type MergeAllObjects<\n TUnion,\n TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,\n> = [keyof TIntersected] extends [never]\n ? never\n : {\n [TKey in keyof TIntersected]: TUnion extends any\n ? TUnion[TKey & keyof TUnion]\n : never\n }\n\nexport type MergeAll<TUnion> =\n | MergeAllObjects<TUnion>\n | ExtractPrimitives<TUnion>\n\nexport type ValidateJSON<T> = ((...args: Array<any>) => any) extends T\n ? unknown extends T\n ? never\n : 'Function is not serializable'\n : { [K in keyof T]: ValidateJSON<T[K]> }\n\nexport type LooseReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn\n : never\n\nexport type LooseAsyncReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn extends Promise<infer TReturn>\n ? TReturn\n : TReturn\n : never\n\nexport function last<T>(arr: Array<T>) {\n return arr[arr.length - 1]\n}\n\nfunction isFunction(d: any): d is Function {\n return typeof d === 'function'\n}\n\nexport function functionalUpdate<TPrevious, TResult = TPrevious>(\n updater: Updater<TPrevious, TResult> | NonNullableUpdater<TPrevious, TResult>,\n previous: TPrevious,\n): TResult {\n if (isFunction(updater)) {\n return updater(previous)\n }\n\n return updater\n}\n\nexport function pick<TValue, TKey extends keyof TValue>(\n parent: TValue,\n keys: Array<TKey>,\n): Pick<TValue, TKey> {\n return keys.reduce((obj: any, key: TKey) => {\n obj[key] = parent[key]\n return obj\n }, {} as any)\n}\n\n/**\n * This function returns `prev` if `_next` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between immutable JSON values for example.\n * Do not use this with signals\n */\nexport function replaceEqualDeep<T>(prev: any, _next: T): T {\n if (prev === _next) {\n return prev\n }\n\n const next = _next as any\n\n const array = isPlainArray(prev) && isPlainArray(next)\n\n if (array || (isSimplePlainObject(prev) && isSimplePlainObject(next))) {\n const prevItems = array\n ? prev\n : (Object.keys(prev) as Array<unknown>).concat(\n Object.getOwnPropertySymbols(prev),\n )\n const prevSize = prevItems.length\n const nextItems = array\n ? next\n : (Object.keys(next) as Array<unknown>).concat(\n Object.getOwnPropertySymbols(next),\n )\n const nextSize = nextItems.length\n const copy: any = array ? [] : {}\n\n let equalItems = 0\n\n for (let i = 0; i < nextSize; i++) {\n const key = array ? i : (nextItems[i] as any)\n if (\n ((!array && prevItems.includes(key)) || array) &&\n prev[key] === undefined &&\n next[key] === undefined\n ) {\n copy[key] = undefined\n equalItems++\n } else {\n copy[key] = replaceEqualDeep(prev[key], next[key])\n if (copy[key] === prev[key] && prev[key] !== undefined) {\n equalItems++\n }\n }\n }\n\n return prevSize === nextSize && equalItems === prevSize ? prev : copy\n }\n\n return next\n}\n\n/**\n * A wrapper around `isPlainObject` with additional checks to ensure that it is not\n * only a plain object, but also one that is \"clone-friendly\" (doesn't have any\n * non-enumerable properties).\n */\nfunction isSimplePlainObject(o: any) {\n return (\n // all the checks from isPlainObject are more likely to hit so we perform them first\n isPlainObject(o) &&\n Object.getOwnPropertyNames(o).length === Object.keys(o).length\n )\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any) {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has modified constructor\n const ctor = o.constructor\n if (typeof ctor === 'undefined') {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any) {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\nexport function isPlainArray(value: unknown): value is Array<unknown> {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\nfunction getObjectKeys(obj: any, ignoreUndefined: boolean) {\n let keys = Object.keys(obj)\n if (ignoreUndefined) {\n keys = keys.filter((key) => obj[key] !== undefined)\n }\n return keys\n}\n\nexport function deepEqual(\n a: any,\n b: any,\n opts?: { partial?: boolean; ignoreUndefined?: boolean },\n): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const ignoreUndefined = opts?.ignoreUndefined ?? true\n const aKeys = getObjectKeys(a, ignoreUndefined)\n const bKeys = getObjectKeys(b, ignoreUndefined)\n\n if (!opts?.partial && aKeys.length !== bKeys.length) {\n return false\n }\n\n return bKeys.every((key) => deepEqual(a[key], b[key], opts))\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return false\n }\n return !a.some((item, index) => !deepEqual(item, b[index], opts))\n }\n\n return false\n}\n\nexport type StringLiteral<T> = T extends string\n ? string extends T\n ? string\n : T\n : never\n\nexport type ThrowOrOptional<T, TThrow extends boolean> = TThrow extends true\n ? T\n : T | undefined\n\nexport type StrictOrFrom<\n TRouter extends AnyRouter,\n TFrom,\n TStrict extends boolean = true,\n> = TStrict extends false\n ? {\n from?: never\n strict: TStrict\n }\n : {\n from: ConstrainLiteral<TFrom, RouteIds<TRouter['routeTree']>>\n strict?: TStrict\n }\n\nexport type ThrowConstraint<\n TStrict extends boolean,\n TThrow extends boolean,\n> = TStrict extends false ? (TThrow extends true ? never : TThrow) : TThrow\n\nexport type ControlledPromise<T> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value: any) => void\n status: 'pending' | 'resolved' | 'rejected'\n value?: T\n}\n\nexport function createControlledPromise<T>(onResolve?: (value: T) => void) {\n let resolveLoadPromise!: (value: T) => void\n let rejectLoadPromise!: (value: any) => void\n\n const controlledPromise = new Promise<T>((resolve, reject) => {\n resolveLoadPromise = resolve\n rejectLoadPromise = reject\n }) as ControlledPromise<T>\n\n controlledPromise.status = 'pending'\n\n controlledPromise.resolve = (value: T) => {\n controlledPromise.status = 'resolved'\n controlledPromise.value = value\n resolveLoadPromise(value)\n onResolve?.(value)\n }\n\n controlledPromise.reject = (e) => {\n controlledPromise.status = 'rejected'\n rejectLoadPromise(e)\n }\n\n return controlledPromise\n}\n\n/**\n *\n * @deprecated use `jsesc` instead\n */\nexport function escapeJSON(jsonString: string) {\n return jsonString\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n .replace(/'/g, \"\\\\'\") // Escape single quotes\n .replace(/\"/g, '\\\\\"') // Escape double quotes\n}\n\nexport function shallow<T>(objA: T, objB: T) {\n if (Object.is(objA, objB)) {\n return true\n }\n\n if (\n typeof objA !== 'object' ||\n objA === null ||\n typeof objB !== 'object' ||\n objB === null\n ) {\n return false\n }\n\n const keysA = Object.keys(objA)\n if (keysA.length !== Object.keys(objB).length) {\n return false\n }\n\n for (const item of keysA) {\n if (\n !Object.prototype.hasOwnProperty.call(objB, item) ||\n !Object.is(objA[item as keyof T], objB[item as keyof T])\n ) {\n return false\n }\n }\n return true\n}\n\nexport function isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function isPromise<T>(\n value: Promise<Awaited<T>> | T,\n): value is Promise<Awaited<T>> {\n return Boolean(\n value &&\n typeof value === 'object' &&\n typeof (value as Promise<T>).then === 'function',\n )\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type { RouteIds } from './routeInfo'\nimport type { AnyRouter } from './router'\n\nexport type Awaitable<T> = T | Promise<T>\nexport type NoInfer<T> = [T][T extends any ? 0 : never]\nexport type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue\n ? TYesResult\n : TNoResult\n\nexport type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<\n TValue,\n TKey\n> &\n Required<Pick<TValue, TKey>>\n\nexport type PickRequired<T> = {\n [K in keyof T as undefined extends T[K] ? never : K]: T[K]\n}\n\nexport type PickOptional<T> = {\n [K in keyof T as undefined extends T[K] ? K : never]: T[K]\n}\n\n// from https://stackoverflow.com/a/76458160\nexport type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never\n\nexport type Expand<T> = T extends object\n ? T extends infer O\n ? O extends Function\n ? O\n : { [K in keyof O]: O[K] }\n : never\n : T\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n\nexport type MakeDifferenceOptional<TLeft, TRight> = keyof TLeft &\n keyof TRight extends never\n ? TRight\n : Omit<TRight, keyof TLeft & keyof TRight> & {\n [K in keyof TLeft & keyof TRight]?: TRight[K]\n }\n\n// from https://stackoverflow.com/a/53955431\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport type IsUnion<T, U extends T = T> = (\n T extends any ? (U extends T ? false : true) : never\n) extends false\n ? false\n : true\n\nexport type IsNonEmptyObject<T> = T extends object\n ? keyof T extends never\n ? false\n : true\n : false\n\nexport type Assign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : keyof TLeft & keyof TRight extends never\n ? TLeft & TRight\n : Omit<TLeft, keyof TRight> & TRight\n : never\n : never\n\nexport type IntersectAssign<TLeft, TRight> = TLeft extends any\n ? TRight extends any\n ? IsNonEmptyObject<TLeft> extends false\n ? TRight\n : IsNonEmptyObject<TRight> extends false\n ? TLeft\n : TRight & TLeft\n : never\n : never\n\nexport type Timeout = ReturnType<typeof setTimeout>\n\nexport type Updater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev?: TPrevious) => TResult)\n\nexport type NonNullableUpdater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev: TPrevious) => TResult)\n\nexport type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive\n ? never\n : TUnion\n\nexport type PartialMergeAllObject<TUnion> =\n ExtractObjects<TUnion> extends infer TObj\n ? [TObj] extends [never]\n ? never\n : {\n [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any\n ? TKey extends keyof TObj\n ? TObj[TKey]\n : never\n : never\n }\n : never\n\nexport type MergeAllPrimitive =\n | ReadonlyArray<any>\n | number\n | string\n | bigint\n | boolean\n | symbol\n | undefined\n | null\n\nexport type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive\n ? TUnion\n : TUnion extends object\n ? never\n : TUnion\n\nexport type PartialMergeAll<TUnion> =\n | ExtractPrimitives<TUnion>\n | PartialMergeAllObject<TUnion>\n\nexport type Constrain<T, TConstraint, TDefault = TConstraint> =\n | (T extends TConstraint ? T : never)\n | TDefault\n\nexport type ConstrainLiteral<T, TConstraint, TDefault = TConstraint> =\n | (T & TConstraint)\n | TDefault\n\n/**\n * To be added to router types\n */\nexport type UnionToIntersection<T> = (\n T extends any ? (arg: T) => any : never\n) extends (arg: infer T) => any\n ? T\n : never\n\n/**\n * Merges everything in a union into one object.\n * This mapped type is homomorphic which means it preserves stuff! :)\n */\nexport type MergeAllObjects<\n TUnion,\n TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,\n> = [keyof TIntersected] extends [never]\n ? never\n : {\n [TKey in keyof TIntersected]: TUnion extends any\n ? TUnion[TKey & keyof TUnion]\n : never\n }\n\nexport type MergeAll<TUnion> =\n | MergeAllObjects<TUnion>\n | ExtractPrimitives<TUnion>\n\nexport type ValidateJSON<T> = ((...args: Array<any>) => any) extends T\n ? unknown extends T\n ? never\n : 'Function is not serializable'\n : { [K in keyof T]: ValidateJSON<T[K]> }\n\nexport type LooseReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn\n : never\n\nexport type LooseAsyncReturnType<T> = T extends (\n ...args: Array<any>\n) => infer TReturn\n ? TReturn extends Promise<infer TReturn>\n ? TReturn\n : TReturn\n : never\n\nexport function last<T>(arr: Array<T>) {\n return arr[arr.length - 1]\n}\n\nfunction isFunction(d: any): d is Function {\n return typeof d === 'function'\n}\n\nexport function functionalUpdate<TPrevious, TResult = TPrevious>(\n updater: Updater<TPrevious, TResult> | NonNullableUpdater<TPrevious, TResult>,\n previous: TPrevious,\n): TResult {\n if (isFunction(updater)) {\n return updater(previous)\n }\n\n return updater\n}\n\nexport function pick<TValue, TKey extends keyof TValue>(\n parent: TValue,\n keys: Array<TKey>,\n): Pick<TValue, TKey> {\n return keys.reduce((obj: any, key: TKey) => {\n obj[key] = parent[key]\n return obj\n }, {} as any)\n}\n\n/**\n * This function returns `prev` if `_next` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between immutable JSON values for example.\n * Do not use this with signals\n */\nexport function replaceEqualDeep<T>(prev: any, _next: T): T {\n if (prev === _next) {\n return prev\n }\n\n const next = _next as any\n\n const array = isPlainArray(prev) && isPlainArray(next)\n\n if (array || (isSimplePlainObject(prev) && isSimplePlainObject(next))) {\n const prevItems = array\n ? prev\n : (Object.keys(prev) as Array<unknown>).concat(\n Object.getOwnPropertySymbols(prev),\n )\n const prevSize = prevItems.length\n const nextItems = array\n ? next\n : (Object.keys(next) as Array<unknown>).concat(\n Object.getOwnPropertySymbols(next),\n )\n const nextSize = nextItems.length\n const copy: any = array ? [] : {}\n\n let equalItems = 0\n\n for (let i = 0; i < nextSize; i++) {\n const key = array ? i : (nextItems[i] as any)\n if (\n ((!array && prevItems.includes(key)) || array) &&\n prev[key] === undefined &&\n next[key] === undefined\n ) {\n copy[key] = undefined\n equalItems++\n } else {\n copy[key] = replaceEqualDeep(prev[key], next[key])\n if (copy[key] === prev[key] && prev[key] !== undefined) {\n equalItems++\n }\n }\n }\n\n return prevSize === nextSize && equalItems === prevSize ? prev : copy\n }\n\n return next\n}\n\n/**\n * A wrapper around `isPlainObject` with additional checks to ensure that it is not\n * only a plain object, but also one that is \"clone-friendly\" (doesn't have any\n * non-enumerable properties).\n */\nfunction isSimplePlainObject(o: any) {\n return (\n // all the checks from isPlainObject are more likely to hit so we perform them first\n isPlainObject(o) &&\n Object.getOwnPropertyNames(o).length === Object.keys(o).length\n )\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any) {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has modified constructor\n const ctor = o.constructor\n if (typeof ctor === 'undefined') {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any) {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\nexport function isPlainArray(value: unknown): value is Array<unknown> {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\nfunction getObjectKeys(obj: any, ignoreUndefined: boolean) {\n let keys = Object.keys(obj)\n if (ignoreUndefined) {\n keys = keys.filter((key) => obj[key] !== undefined)\n }\n return keys\n}\n\nexport function deepEqual(\n a: any,\n b: any,\n opts?: { partial?: boolean; ignoreUndefined?: boolean },\n): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const ignoreUndefined = opts?.ignoreUndefined ?? true\n const aKeys = getObjectKeys(a, ignoreUndefined)\n const bKeys = getObjectKeys(b, ignoreUndefined)\n\n if (!opts?.partial && aKeys.length !== bKeys.length) {\n return false\n }\n\n return bKeys.every((key) => deepEqual(a[key], b[key], opts))\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return false\n }\n return !a.some((item, index) => !deepEqual(item, b[index], opts))\n }\n\n return false\n}\n\nexport type StringLiteral<T> = T extends string\n ? string extends T\n ? string\n : T\n : never\n\nexport type ThrowOrOptional<T, TThrow extends boolean> = TThrow extends true\n ? T\n : T | undefined\n\nexport type StrictOrFrom<\n TRouter extends AnyRouter,\n TFrom,\n TStrict extends boolean = true,\n> = TStrict extends false\n ? {\n from?: never\n strict: TStrict\n }\n : {\n from: ConstrainLiteral<TFrom, RouteIds<TRouter['routeTree']>>\n strict?: TStrict\n }\n\nexport type ThrowConstraint<\n TStrict extends boolean,\n TThrow extends boolean,\n> = TStrict extends false ? (TThrow extends true ? never : TThrow) : TThrow\n\nexport type ControlledPromise<T> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value: any) => void\n status: 'pending' | 'resolved' | 'rejected'\n value?: T\n}\n\nexport function createControlledPromise<T>(onResolve?: (value: T) => void) {\n let resolveLoadPromise!: (value: T) => void\n let rejectLoadPromise!: (value: any) => void\n\n const controlledPromise = new Promise<T>((resolve, reject) => {\n resolveLoadPromise = resolve\n rejectLoadPromise = reject\n }) as ControlledPromise<T>\n\n controlledPromise.status = 'pending'\n\n controlledPromise.resolve = (value: T) => {\n controlledPromise.status = 'resolved'\n controlledPromise.value = value\n resolveLoadPromise(value)\n onResolve?.(value)\n }\n\n controlledPromise.reject = (e) => {\n controlledPromise.status = 'rejected'\n rejectLoadPromise(e)\n }\n\n return controlledPromise\n}\n\n/**\n *\n * @deprecated use `jsesc` instead\n */\nexport function escapeJSON(jsonString: string) {\n return jsonString\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n .replace(/'/g, \"\\\\'\") // Escape single quotes\n .replace(/\"/g, '\\\\\"') // Escape double quotes\n}\n\nexport function shallow<T>(objA: T, objB: T) {\n if (Object.is(objA, objB)) {\n return true\n }\n\n if (\n typeof objA !== 'object' ||\n objA === null ||\n typeof objB !== 'object' ||\n objB === null\n ) {\n return false\n }\n\n const keysA = Object.keys(objA)\n if (keysA.length !== Object.keys(objB).length) {\n return false\n }\n\n for (const item of keysA) {\n if (\n !Object.prototype.hasOwnProperty.call(objB, item) ||\n !Object.is(objA[item as keyof T], objB[item as keyof T])\n ) {\n return false\n }\n }\n return true\n}\n\nexport function isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function isPromise<T>(\n value: Promise<Awaited<T>> | T,\n): value is Promise<Awaited<T>> {\n return Boolean(\n value &&\n typeof value === 'object' &&\n typeof (value as Promise<T>).then === 'function',\n )\n}\n\nexport function findLast<T>(\n array: ReadonlyArray<T>,\n predicate: (item: T) => boolean,\n): T | undefined {\n for (let i = array.length - 1; i >= 0; i--) {\n const item = array[i]!\n if (predicate(item)) return item\n }\n return undefined\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/load-matches.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { isNotFound } from './not-found'
|
|
|
5
5
|
import { rootRouteId } from './root'
|
|
6
6
|
import { isRedirect } from './redirect'
|
|
7
7
|
import type { NotFoundError } from './not-found'
|
|
8
|
-
import type { ControlledPromise } from './utils'
|
|
9
8
|
import type { ParsedLocation } from './location'
|
|
10
9
|
import type {
|
|
11
10
|
AnyRoute,
|
|
@@ -56,12 +55,6 @@ const _handleNotFound = (inner: InnerLoadContext, err: NotFoundError) => {
|
|
|
56
55
|
// First check if a specific route is requested to show the error
|
|
57
56
|
const routeCursor =
|
|
58
57
|
inner.router.routesById[err.routeId ?? ''] ?? inner.router.routeTree
|
|
59
|
-
const matchesByRouteId: Record<string, AnyRouteMatch> = {}
|
|
60
|
-
|
|
61
|
-
// Setup routesByRouteId object for quick access
|
|
62
|
-
for (const match of inner.matches) {
|
|
63
|
-
matchesByRouteId[match.routeId] = match
|
|
64
|
-
}
|
|
65
58
|
|
|
66
59
|
// Ensure a NotFoundComponent exists on the route
|
|
67
60
|
if (
|
|
@@ -80,7 +73,7 @@ const _handleNotFound = (inner: InnerLoadContext, err: NotFoundError) => {
|
|
|
80
73
|
)
|
|
81
74
|
|
|
82
75
|
// Find the match for this route
|
|
83
|
-
const matchForRoute =
|
|
76
|
+
const matchForRoute = inner.matches.find((m) => m.routeId === routeCursor.id)
|
|
84
77
|
|
|
85
78
|
invariant(matchForRoute, 'Could not find match for route: ' + routeCursor.id)
|
|
86
79
|
|
|
@@ -246,7 +239,7 @@ const isBeforeLoadSsr = (
|
|
|
246
239
|
existingMatch.ssr = parentOverride(route.options.ssr)
|
|
247
240
|
return
|
|
248
241
|
}
|
|
249
|
-
const { search, params } =
|
|
242
|
+
const { search, params } = existingMatch
|
|
250
243
|
|
|
251
244
|
const ssrFnContext: SsrContextOptions<any, any, any> = {
|
|
252
245
|
search: makeMaybe(search, existingMatch.searchError),
|
|
@@ -280,8 +273,8 @@ const setupPendingTimeout = (
|
|
|
280
273
|
inner: InnerLoadContext,
|
|
281
274
|
matchId: string,
|
|
282
275
|
route: AnyRoute,
|
|
276
|
+
match: AnyRouteMatch,
|
|
283
277
|
): void => {
|
|
284
|
-
const match = inner.router.getMatch(matchId)!
|
|
285
278
|
if (match._nonReactive.pendingTimeout !== undefined) return
|
|
286
279
|
|
|
287
280
|
const pendingMs =
|
|
@@ -324,20 +317,20 @@ const shouldExecuteBeforeLoad = (
|
|
|
324
317
|
)
|
|
325
318
|
return true
|
|
326
319
|
|
|
327
|
-
setupPendingTimeout(inner, matchId, route)
|
|
320
|
+
setupPendingTimeout(inner, matchId, route, existingMatch)
|
|
328
321
|
|
|
329
322
|
const then = () => {
|
|
330
|
-
let
|
|
323
|
+
let result = true
|
|
331
324
|
const match = inner.router.getMatch(matchId)!
|
|
332
325
|
if (match.status === 'error') {
|
|
333
|
-
|
|
326
|
+
result = true
|
|
334
327
|
} else if (
|
|
335
328
|
match.preload &&
|
|
336
329
|
(match.status === 'redirected' || match.status === 'notFound')
|
|
337
330
|
) {
|
|
338
331
|
handleRedirectAndNotFound(inner, match, match.error)
|
|
339
332
|
}
|
|
340
|
-
return
|
|
333
|
+
return result
|
|
341
334
|
}
|
|
342
335
|
|
|
343
336
|
// Wait for the beforeLoad to resolve before we continue
|
|
@@ -354,7 +347,6 @@ const executeBeforeLoad = (
|
|
|
354
347
|
): void | Promise<void> => {
|
|
355
348
|
const match = inner.router.getMatch(matchId)!
|
|
356
349
|
|
|
357
|
-
match._nonReactive.beforeLoadPromise = createControlledPromise<void>()
|
|
358
350
|
// explicitly capture the previous loadPromise
|
|
359
351
|
const prevLoadPromise = match._nonReactive.loadPromise
|
|
360
352
|
match._nonReactive.loadPromise = createControlledPromise<void>(() => {
|
|
@@ -371,7 +363,7 @@ const executeBeforeLoad = (
|
|
|
371
363
|
handleSerialError(inner, index, searchError, 'VALIDATE_SEARCH')
|
|
372
364
|
}
|
|
373
365
|
|
|
374
|
-
setupPendingTimeout(inner, matchId, route)
|
|
366
|
+
setupPendingTimeout(inner, matchId, route, match)
|
|
375
367
|
|
|
376
368
|
const abortController = new AbortController()
|
|
377
369
|
|
|
@@ -415,6 +407,8 @@ const executeBeforeLoad = (
|
|
|
415
407
|
return
|
|
416
408
|
}
|
|
417
409
|
|
|
410
|
+
match._nonReactive.beforeLoadPromise = createControlledPromise<void>()
|
|
411
|
+
|
|
418
412
|
const { search, params, cause } = match
|
|
419
413
|
const preload = resolvePreload(inner, matchId)
|
|
420
414
|
const beforeLoadFnContext: BeforeLoadContextOptions<any, any, any, any, any> =
|
|
@@ -510,8 +504,8 @@ const handleBeforeLoad = (
|
|
|
510
504
|
: execute(shouldExecuteBeforeLoadResult)
|
|
511
505
|
}
|
|
512
506
|
|
|
513
|
-
const execute = (
|
|
514
|
-
if (
|
|
507
|
+
const execute = (shouldExec: boolean) => {
|
|
508
|
+
if (shouldExec) {
|
|
515
509
|
// If we are not in the middle of a load OR the previous load failed, start it
|
|
516
510
|
return executeBeforeLoad(inner, matchId, index, route)
|
|
517
511
|
}
|
|
@@ -567,14 +561,6 @@ const executeHead = (
|
|
|
567
561
|
})
|
|
568
562
|
}
|
|
569
563
|
|
|
570
|
-
const potentialPendingMinPromise = (
|
|
571
|
-
inner: InnerLoadContext,
|
|
572
|
-
matchId: string,
|
|
573
|
-
): void | ControlledPromise<void> => {
|
|
574
|
-
const latestMatch = inner.router.getMatch(matchId)!
|
|
575
|
-
return latestMatch._nonReactive.minPendingPromise
|
|
576
|
-
}
|
|
577
|
-
|
|
578
564
|
const getLoaderContext = (
|
|
579
565
|
inner: InnerLoadContext,
|
|
580
566
|
matchId: string,
|
|
@@ -592,7 +578,7 @@ const getLoaderContext = (
|
|
|
592
578
|
deps: loaderDeps,
|
|
593
579
|
preload: !!preload,
|
|
594
580
|
parentMatchPromise,
|
|
595
|
-
abortController
|
|
581
|
+
abortController,
|
|
596
582
|
context,
|
|
597
583
|
location: inner.location,
|
|
598
584
|
navigate: (opts) =>
|
|
@@ -618,12 +604,11 @@ const runLoader = async (
|
|
|
618
604
|
// before committing to the match and resolving
|
|
619
605
|
// the loadPromise
|
|
620
606
|
|
|
607
|
+
const match = inner.router.getMatch(matchId)!
|
|
608
|
+
|
|
621
609
|
// Actually run the loader and handle the result
|
|
622
610
|
try {
|
|
623
|
-
if (
|
|
624
|
-
!inner.router.isServer ||
|
|
625
|
-
inner.router.getMatch(matchId)!.ssr === true
|
|
626
|
-
) {
|
|
611
|
+
if (!inner.router.isServer || match.ssr === true) {
|
|
627
612
|
loadRouteChunk(route)
|
|
628
613
|
}
|
|
629
614
|
|
|
@@ -641,7 +626,7 @@ const runLoader = async (
|
|
|
641
626
|
route.options.head ||
|
|
642
627
|
route.options.scripts ||
|
|
643
628
|
route.options.headers ||
|
|
644
|
-
|
|
629
|
+
match._nonReactive.minPendingPromise
|
|
645
630
|
)
|
|
646
631
|
|
|
647
632
|
if (willLoadSomething) {
|
|
@@ -675,7 +660,7 @@ const runLoader = async (
|
|
|
675
660
|
if (route._lazyPromise) await route._lazyPromise
|
|
676
661
|
const headResult = executeHead(inner, matchId, route)
|
|
677
662
|
const head = headResult ? await headResult : undefined
|
|
678
|
-
const pendingPromise =
|
|
663
|
+
const pendingPromise = match._nonReactive.minPendingPromise
|
|
679
664
|
if (pendingPromise) await pendingPromise
|
|
680
665
|
|
|
681
666
|
// Last but not least, wait for the the components
|
|
@@ -692,7 +677,7 @@ const runLoader = async (
|
|
|
692
677
|
} catch (e) {
|
|
693
678
|
let error = e
|
|
694
679
|
|
|
695
|
-
const pendingPromise =
|
|
680
|
+
const pendingPromise = match._nonReactive.minPendingPromise
|
|
696
681
|
if (pendingPromise) await pendingPromise
|
|
697
682
|
|
|
698
683
|
handleRedirectAndNotFound(inner, inner.router.getMatch(matchId), e)
|
|
@@ -744,7 +729,6 @@ const loadRouteMatch = async (
|
|
|
744
729
|
let loaderIsRunningAsync = false
|
|
745
730
|
const route = inner.router.looseRoutesById[routeId]!
|
|
746
731
|
|
|
747
|
-
const prevMatch = inner.router.getMatch(matchId)!
|
|
748
732
|
if (shouldSkipLoader(inner, matchId)) {
|
|
749
733
|
if (inner.router.isServer) {
|
|
750
734
|
const headResult = executeHead(inner, matchId, route)
|
|
@@ -757,88 +741,92 @@ const loadRouteMatch = async (
|
|
|
757
741
|
}
|
|
758
742
|
return inner.router.getMatch(matchId)!
|
|
759
743
|
}
|
|
760
|
-
}
|
|
761
|
-
// there is a loaderPromise, so we are in the middle of a load
|
|
762
|
-
else if (prevMatch._nonReactive.loaderPromise) {
|
|
763
|
-
// do not block if we already have stale data we can show
|
|
764
|
-
// but only if the ongoing load is not a preload since error handling is different for preloads
|
|
765
|
-
// and we don't want to swallow errors
|
|
766
|
-
if (prevMatch.status === 'success' && !inner.sync && !prevMatch.preload) {
|
|
767
|
-
return inner.router.getMatch(matchId)!
|
|
768
|
-
}
|
|
769
|
-
await prevMatch._nonReactive.loaderPromise
|
|
770
|
-
const match = inner.router.getMatch(matchId)!
|
|
771
|
-
if (match.error) {
|
|
772
|
-
handleRedirectAndNotFound(inner, match, match.error)
|
|
773
|
-
}
|
|
774
744
|
} else {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
// Allow shouldReload to get the last say,
|
|
790
|
-
// if provided.
|
|
791
|
-
const shouldReload =
|
|
792
|
-
typeof shouldReloadOption === 'function'
|
|
793
|
-
? shouldReloadOption(getLoaderContext(inner, matchId, index, route))
|
|
794
|
-
: shouldReloadOption
|
|
795
|
-
|
|
796
|
-
const nextPreload =
|
|
797
|
-
!!preload && !inner.router.state.matches.some((d) => d.id === matchId)
|
|
798
|
-
const match = inner.router.getMatch(matchId)!
|
|
799
|
-
match._nonReactive.loaderPromise = createControlledPromise<void>()
|
|
800
|
-
if (nextPreload !== match.preload) {
|
|
801
|
-
inner.updateMatch(matchId, (prev) => ({
|
|
802
|
-
...prev,
|
|
803
|
-
preload: nextPreload,
|
|
804
|
-
}))
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
// If the route is successful and still fresh, just resolve
|
|
808
|
-
const { status, invalid } = inner.router.getMatch(matchId)!
|
|
809
|
-
loaderShouldRunAsync =
|
|
810
|
-
status === 'success' && (invalid || (shouldReload ?? age > staleAge))
|
|
811
|
-
if (preload && route.options.preload === false) {
|
|
812
|
-
// Do nothing
|
|
813
|
-
} else if (loaderShouldRunAsync && !inner.sync) {
|
|
814
|
-
loaderIsRunningAsync = true
|
|
815
|
-
;(async () => {
|
|
816
|
-
try {
|
|
817
|
-
await runLoader(inner, matchId, index, route)
|
|
818
|
-
const match = inner.router.getMatch(matchId)!
|
|
819
|
-
match._nonReactive.loaderPromise?.resolve()
|
|
820
|
-
match._nonReactive.loadPromise?.resolve()
|
|
821
|
-
match._nonReactive.loaderPromise = undefined
|
|
822
|
-
} catch (err) {
|
|
823
|
-
if (isRedirect(err)) {
|
|
824
|
-
await inner.router.navigate(err.options)
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
})()
|
|
828
|
-
} else if (status !== 'success' || (loaderShouldRunAsync && inner.sync)) {
|
|
829
|
-
await runLoader(inner, matchId, index, route)
|
|
745
|
+
const prevMatch = inner.router.getMatch(matchId)!
|
|
746
|
+
// there is a loaderPromise, so we are in the middle of a load
|
|
747
|
+
if (prevMatch._nonReactive.loaderPromise) {
|
|
748
|
+
// do not block if we already have stale data we can show
|
|
749
|
+
// but only if the ongoing load is not a preload since error handling is different for preloads
|
|
750
|
+
// and we don't want to swallow errors
|
|
751
|
+
if (prevMatch.status === 'success' && !inner.sync && !prevMatch.preload) {
|
|
752
|
+
return prevMatch
|
|
753
|
+
}
|
|
754
|
+
await prevMatch._nonReactive.loaderPromise
|
|
755
|
+
const match = inner.router.getMatch(matchId)!
|
|
756
|
+
if (match.error) {
|
|
757
|
+
handleRedirectAndNotFound(inner, match, match.error)
|
|
758
|
+
}
|
|
830
759
|
} else {
|
|
831
|
-
//
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const
|
|
835
|
-
|
|
836
|
-
|
|
760
|
+
// This is where all of the stale-while-revalidate magic happens
|
|
761
|
+
const age = Date.now() - prevMatch.updatedAt
|
|
762
|
+
|
|
763
|
+
const preload = resolvePreload(inner, matchId)
|
|
764
|
+
|
|
765
|
+
const staleAge = preload
|
|
766
|
+
? (route.options.preloadStaleTime ??
|
|
767
|
+
inner.router.options.defaultPreloadStaleTime ??
|
|
768
|
+
30_000) // 30 seconds for preloads by default
|
|
769
|
+
: (route.options.staleTime ??
|
|
770
|
+
inner.router.options.defaultStaleTime ??
|
|
771
|
+
0)
|
|
772
|
+
|
|
773
|
+
const shouldReloadOption = route.options.shouldReload
|
|
774
|
+
|
|
775
|
+
// Default to reloading the route all the time
|
|
776
|
+
// Allow shouldReload to get the last say,
|
|
777
|
+
// if provided.
|
|
778
|
+
const shouldReload =
|
|
779
|
+
typeof shouldReloadOption === 'function'
|
|
780
|
+
? shouldReloadOption(getLoaderContext(inner, matchId, index, route))
|
|
781
|
+
: shouldReloadOption
|
|
782
|
+
|
|
783
|
+
const nextPreload =
|
|
784
|
+
!!preload && !inner.router.state.matches.some((d) => d.id === matchId)
|
|
785
|
+
const match = inner.router.getMatch(matchId)!
|
|
786
|
+
match._nonReactive.loaderPromise = createControlledPromise<void>()
|
|
787
|
+
if (nextPreload !== match.preload) {
|
|
837
788
|
inner.updateMatch(matchId, (prev) => ({
|
|
838
789
|
...prev,
|
|
839
|
-
|
|
790
|
+
preload: nextPreload,
|
|
840
791
|
}))
|
|
841
792
|
}
|
|
793
|
+
|
|
794
|
+
// If the route is successful and still fresh, just resolve
|
|
795
|
+
const { status, invalid } = match
|
|
796
|
+
loaderShouldRunAsync =
|
|
797
|
+
status === 'success' && (invalid || (shouldReload ?? age > staleAge))
|
|
798
|
+
if (preload && route.options.preload === false) {
|
|
799
|
+
// Do nothing
|
|
800
|
+
} else if (loaderShouldRunAsync && !inner.sync) {
|
|
801
|
+
loaderIsRunningAsync = true
|
|
802
|
+
;(async () => {
|
|
803
|
+
try {
|
|
804
|
+
await runLoader(inner, matchId, index, route)
|
|
805
|
+
const match = inner.router.getMatch(matchId)!
|
|
806
|
+
match._nonReactive.loaderPromise?.resolve()
|
|
807
|
+
match._nonReactive.loadPromise?.resolve()
|
|
808
|
+
match._nonReactive.loaderPromise = undefined
|
|
809
|
+
} catch (err) {
|
|
810
|
+
if (isRedirect(err)) {
|
|
811
|
+
await inner.router.navigate(err.options)
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
})()
|
|
815
|
+
} else if (status !== 'success' || (loaderShouldRunAsync && inner.sync)) {
|
|
816
|
+
await runLoader(inner, matchId, index, route)
|
|
817
|
+
} else {
|
|
818
|
+
// if the loader did not run, still update head.
|
|
819
|
+
// reason: parent's beforeLoad may have changed the route context
|
|
820
|
+
// and only now do we know the route context (and that the loader would not run)
|
|
821
|
+
const headResult = executeHead(inner, matchId, route)
|
|
822
|
+
if (headResult) {
|
|
823
|
+
const head = await headResult
|
|
824
|
+
inner.updateMatch(matchId, (prev) => ({
|
|
825
|
+
...prev,
|
|
826
|
+
...head,
|
|
827
|
+
}))
|
|
828
|
+
}
|
|
829
|
+
}
|
|
842
830
|
}
|
|
843
831
|
}
|
|
844
832
|
const match = inner.router.getMatch(matchId)!
|
|
@@ -858,8 +846,10 @@ const loadRouteMatch = async (
|
|
|
858
846
|
isFetching: nextIsFetching,
|
|
859
847
|
invalid: false,
|
|
860
848
|
}))
|
|
849
|
+
return inner.router.getMatch(matchId)!
|
|
850
|
+
} else {
|
|
851
|
+
return match
|
|
861
852
|
}
|
|
862
|
-
return inner.router.getMatch(matchId)!
|
|
863
853
|
}
|
|
864
854
|
|
|
865
855
|
export async function loadMatches(arg: {
|
package/src/router.ts
CHANGED
|
@@ -8,6 +8,7 @@ import invariant from 'tiny-invariant'
|
|
|
8
8
|
import {
|
|
9
9
|
createControlledPromise,
|
|
10
10
|
deepEqual,
|
|
11
|
+
findLast,
|
|
11
12
|
functionalUpdate,
|
|
12
13
|
last,
|
|
13
14
|
pick,
|
|
@@ -1441,13 +1442,11 @@ export class RouterCore<
|
|
|
1441
1442
|
undefined,
|
|
1442
1443
|
).matchedRoutes
|
|
1443
1444
|
|
|
1444
|
-
const matchedFrom =
|
|
1445
|
-
.
|
|
1446
|
-
|
|
1447
|
-
return comparePaths(d.fullPath, fromPath)
|
|
1448
|
-
})
|
|
1445
|
+
const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
|
|
1446
|
+
return comparePaths(d.fullPath, fromPath)
|
|
1447
|
+
})
|
|
1449
1448
|
|
|
1450
|
-
const matchedCurrent =
|
|
1449
|
+
const matchedCurrent = findLast(allFromMatches, (d) => {
|
|
1451
1450
|
return comparePaths(d.fullPath, currentLocation.pathname)
|
|
1452
1451
|
})
|
|
1453
1452
|
|
package/src/utils.ts
CHANGED
|
@@ -483,3 +483,14 @@ export function isPromise<T>(
|
|
|
483
483
|
typeof (value as Promise<T>).then === 'function',
|
|
484
484
|
)
|
|
485
485
|
}
|
|
486
|
+
|
|
487
|
+
export function findLast<T>(
|
|
488
|
+
array: ReadonlyArray<T>,
|
|
489
|
+
predicate: (item: T) => boolean,
|
|
490
|
+
): T | undefined {
|
|
491
|
+
for (let i = array.length - 1; i >= 0; i--) {
|
|
492
|
+
const item = array[i]!
|
|
493
|
+
if (predicate(item)) return item
|
|
494
|
+
}
|
|
495
|
+
return undefined
|
|
496
|
+
}
|