@tanstack/router-core 1.166.6 → 1.166.7
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/new-process-route-tree.cjs +7 -3
- package/dist/cjs/new-process-route-tree.cjs.map +1 -1
- package/dist/cjs/path.cjs +1 -1
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/qss.cjs +1 -1
- package/dist/cjs/qss.cjs.map +1 -1
- package/dist/cjs/router.cjs +22 -13
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/utils.cjs +6 -3
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +2 -1
- package/dist/esm/new-process-route-tree.js +7 -3
- package/dist/esm/new-process-route-tree.js.map +1 -1
- package/dist/esm/path.js +1 -1
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/qss.js +1 -1
- package/dist/esm/qss.js.map +1 -1
- package/dist/esm/router.js +23 -14
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils.d.ts +2 -1
- package/dist/esm/utils.js +6 -3
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/new-process-route-tree.ts +8 -4
- package/src/path.ts +1 -1
- package/src/qss.ts +1 -1
- package/src/router.ts +24 -14
- package/src/utils.ts +12 -3
package/dist/esm/path.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.js","sources":["../../src/path.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { last } from './utils'\nimport {\n SEGMENT_TYPE_OPTIONAL_PARAM,\n SEGMENT_TYPE_PARAM,\n SEGMENT_TYPE_PATHNAME,\n SEGMENT_TYPE_WILDCARD,\n parseSegment,\n} from './new-process-route-tree'\nimport type { LRUCache } from './lru-cache'\n\n/** Join path segments, cleaning duplicate slashes between parts. */\nexport function joinPaths(paths: Array<string | undefined>) {\n return cleanPath(\n paths\n .filter((val) => {\n return val !== undefined\n })\n .join('/'),\n )\n}\n\n/** Remove repeated slashes from a path string. */\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\n/** Trim leading slashes (except preserving root '/'). */\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\n/** Trim trailing slashes (except preserving root '/'). */\nexport function trimPathRight(path: string) {\n const len = path.length\n return len > 1 && path[len - 1] === '/' ? path.replace(/\\/{1,}$/, '') : path\n}\n\n/** Trim both leading and trailing slashes. */\nexport function trimPath(path: string) {\n return trimPathRight(trimPathLeft(path))\n}\n\n/** Remove a trailing slash from value when appropriate for comparisons. */\nexport function removeTrailingSlash(value: string, basepath: string): string {\n if (value?.endsWith('/') && value !== '/' && value !== `${basepath}/`) {\n return value.slice(0, -1)\n }\n return value\n}\n\n// intended to only compare path name\n// see the usage in the isActive under useLinkProps\n// /sample/path1 = /sample/path1/\n// /sample/path1/some <> /sample/path1\n/**\n * Compare two pathnames for exact equality after normalizing trailing slashes\n * relative to the provided `basepath`.\n */\nexport function exactPathTest(\n pathName1: string,\n pathName2: string,\n basepath: string,\n): boolean {\n return (\n removeTrailingSlash(pathName1, basepath) ===\n removeTrailingSlash(pathName2, basepath)\n )\n}\n\n// When resolving relative paths, we treat all paths as if they are trailing slash\n// documents. All trailing slashes are removed after the path is resolved.\n// Here are a few examples:\n//\n// /a/b/c + ./d = /a/b/c/d\n// /a/b/c + ../d = /a/b/d\n// /a/b/c + ./d/ = /a/b/c/d\n// /a/b/c + ../d/ = /a/b/d\n// /a/b/c + ./ = /a/b/c\n//\n// Absolute paths that start with `/` short circuit the resolution process to the root\n// path.\n//\n// Here are some examples:\n//\n// /a/b/c + /d = /d\n// /a/b/c + /d/ = /d\n// /a/b/c + / = /\n//\n// Non-.-prefixed paths are still treated as relative paths, resolved like `./`\n//\n// Here are some examples:\n//\n// /a/b/c + d = /a/b/c/d\n// /a/b/c + d/ = /a/b/c/d\n// /a/b/c + d/e = /a/b/c/d/e\ninterface ResolvePathOptions {\n base: string\n to: string\n trailingSlash?: 'always' | 'never' | 'preserve'\n cache?: LRUCache<string, string>\n}\n\n/**\n * Resolve a destination path against a base, honoring trailing-slash policy\n * and supporting relative segments (`.`/`..`) and absolute `to` values.\n */\nexport function resolvePath({\n base,\n to,\n trailingSlash = 'never',\n cache,\n}: ResolvePathOptions) {\n const isAbsolute = to.startsWith('/')\n const isBase = !isAbsolute && to === '.'\n\n let key\n if (cache) {\n // `trailingSlash` is static per router, so it doesn't need to be part of the cache key\n key = isAbsolute ? to : isBase ? base : base + '\\0' + to\n const cached = cache.get(key)\n if (cached) return cached\n }\n\n let baseSegments: Array<string>\n if (isBase) {\n baseSegments = base.split('/')\n } else if (isAbsolute) {\n baseSegments = to.split('/')\n } else {\n baseSegments = base.split('/')\n while (baseSegments.length > 1 && last(baseSegments) === '') {\n baseSegments.pop()\n }\n\n const toSegments = to.split('/')\n for (let index = 0, length = toSegments.length; index < length; index++) {\n const value = toSegments[index]!\n if (value === '') {\n if (!index) {\n // Leading slash\n baseSegments = [value]\n } else if (index === length - 1) {\n // Trailing Slash\n baseSegments.push(value)\n } else {\n // ignore inter-slashes\n }\n } else if (value === '..') {\n baseSegments.pop()\n } else if (value === '.') {\n // ignore\n } else {\n baseSegments.push(value)\n }\n }\n }\n\n if (baseSegments.length > 1) {\n if (last(baseSegments) === '') {\n if (trailingSlash === 'never') {\n baseSegments.pop()\n }\n } else if (trailingSlash === 'always') {\n baseSegments.push('')\n }\n }\n\n let segment\n let joined = ''\n for (let i = 0; i < baseSegments.length; i++) {\n if (i > 0) joined += '/'\n const part = baseSegments[i]!\n if (!part) continue\n segment = parseSegment(part, 0, segment)\n const kind = segment[0]\n if (kind === SEGMENT_TYPE_PATHNAME) {\n joined += part\n continue\n }\n const end = segment[5]\n const prefix = part.substring(0, segment[1])\n const suffix = part.substring(segment[4], end)\n const value = part.substring(segment[2], segment[3])\n if (kind === SEGMENT_TYPE_PARAM) {\n joined += prefix || suffix ? `${prefix}{$${value}}${suffix}` : `$${value}`\n } else if (kind === SEGMENT_TYPE_WILDCARD) {\n joined += prefix || suffix ? `${prefix}{$}${suffix}` : '$'\n } else {\n // SEGMENT_TYPE_OPTIONAL_PARAM\n joined += `${prefix}{-$${value}}${suffix}`\n }\n }\n joined = cleanPath(joined)\n const result = joined || '/'\n if (key && cache) cache.set(key, result)\n return result\n}\n\n/**\n * Create a pre-compiled decode config from allowed characters.\n * This should be called once at router initialization.\n */\nexport function compileDecodeCharMap(\n pathParamsAllowedCharacters: ReadonlyArray<string>,\n) {\n const charMap = new Map(\n pathParamsAllowedCharacters.map((char) => [encodeURIComponent(char), char]),\n )\n // Escape special regex characters and join with |\n const pattern = Array.from(charMap.keys())\n .map((key) => key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('|')\n const regex = new RegExp(pattern, 'g')\n return (encoded: string) =>\n encoded.replace(regex, (match) => charMap.get(match) ?? match)\n}\n\ninterface InterpolatePathOptions {\n path?: string\n params: Record<string, unknown>\n /**\n * A function that decodes a path parameter value.\n * Obtained from `compileDecodeCharMap(pathParamsAllowedCharacters)`.\n */\n decoder?: (encoded: string) => string\n /**\n * @internal\n * For testing only, in development mode we use the router.isServer value\n */\n server?: boolean\n}\n\ntype InterPolatePathResult = {\n interpolatedPath: string\n usedParams: Record<string, unknown>\n isMissingParams: boolean // true if any params were not available when being looked up in the params object\n}\n\nfunction encodeParam(\n key: string,\n params: InterpolatePathOptions['params'],\n decoder: InterpolatePathOptions['decoder'],\n): any {\n const value = params[key]\n if (typeof value !== 'string') return value\n\n if (key === '_splat') {\n // Early return if value only contains URL-safe characters (performance optimization)\n if (/^[a-zA-Z0-9\\-._~!/]*$/.test(value)) return value\n // the splat/catch-all routes shouldn't have the '/' encoded out\n // Use encodeURIComponent for each segment to properly encode spaces,\n // plus signs, and other special characters that encodeURI leaves unencoded\n return value\n .split('/')\n .map((segment) => encodePathParam(segment, decoder))\n .join('/')\n } else {\n return encodePathParam(value, decoder)\n }\n}\n\n/**\n * Interpolate params and wildcards into a route path template.\n *\n * - Encodes params safely (configurable allowed characters)\n * - Supports `{-$optional}` segments, `{prefix{$id}suffix}` and `{$}` wildcards\n */\nexport function interpolatePath({\n path,\n params,\n decoder,\n // `server` is marked @internal and stripped from .d.ts by `stripInternal`.\n // We avoid destructuring it in the function signature so the emitted\n // declaration doesn't reference a property that no longer exists.\n ...rest\n}: InterpolatePathOptions): InterPolatePathResult {\n // Tracking if any params are missing in the `params` object\n // when interpolating the path\n let isMissingParams = false\n const usedParams: Record<string, unknown> = {}\n\n if (!path || path === '/')\n return { interpolatedPath: '/', usedParams, isMissingParams }\n if (!path.includes('$'))\n return { interpolatedPath: path, usedParams, isMissingParams }\n\n if (isServer ?? rest.server) {\n // Fast path for common templates like `/posts/$id` or `/files/$`.\n // Braced segments (`{...}`) are more complex (prefix/suffix/optional) and are\n // handled by the general parser below.\n if (path.indexOf('{') === -1) {\n const length = path.length\n let cursor = 0\n let joined = ''\n\n while (cursor < length) {\n // Skip slashes between segments. '/' code is 47\n while (cursor < length && path.charCodeAt(cursor) === 47) cursor++\n if (cursor >= length) break\n\n const start = cursor\n let end = path.indexOf('/', cursor)\n if (end === -1) end = length\n cursor = end\n\n const part = path.substring(start, end)\n if (!part) continue\n\n // `$id` or `$` (splat). '$' code is 36\n if (part.charCodeAt(0) === 36) {\n if (part.length === 1) {\n const splat = params._splat\n usedParams._splat = splat\n // TODO: Deprecate *\n usedParams['*'] = splat\n\n if (!splat) {\n isMissingParams = true\n continue\n }\n\n const value = encodeParam('_splat', params, decoder)\n joined += '/' + value\n } else {\n const key = part.substring(1)\n if (!isMissingParams && !(key in params)) {\n isMissingParams = true\n }\n usedParams[key] = params[key]\n\n const value = encodeParam(key, params, decoder) ?? 'undefined'\n joined += '/' + value\n }\n } else {\n joined += '/' + part\n }\n }\n\n if (path.endsWith('/')) joined += '/'\n\n const interpolatedPath = joined || '/'\n return { usedParams, interpolatedPath, isMissingParams }\n }\n }\n\n const length = path.length\n let cursor = 0\n let segment\n let joined = ''\n while (cursor < length) {\n const start = cursor\n segment = parseSegment(path, start, segment)\n const end = segment[5]\n cursor = end + 1\n\n if (start === end) continue\n\n const kind = segment[0]\n\n if (kind === SEGMENT_TYPE_PATHNAME) {\n joined += '/' + path.substring(start, end)\n continue\n }\n\n if (kind === SEGMENT_TYPE_WILDCARD) {\n const splat = params._splat\n usedParams._splat = splat\n // TODO: Deprecate *\n usedParams['*'] = splat\n\n const prefix = path.substring(start, segment[1])\n const suffix = path.substring(segment[4], end)\n\n // Check if _splat parameter is missing. _splat could be missing if undefined or an empty string or some other falsy value.\n if (!splat) {\n isMissingParams = true\n // For missing splat parameters, just return the prefix and suffix without the wildcard\n // If there is a prefix or suffix, return them joined, otherwise omit the segment\n if (prefix || suffix) {\n joined += '/' + prefix + suffix\n }\n continue\n }\n\n const value = encodeParam('_splat', params, decoder)\n joined += '/' + prefix + value + suffix\n continue\n }\n\n if (kind === SEGMENT_TYPE_PARAM) {\n const key = path.substring(segment[2], segment[3])\n if (!isMissingParams && !(key in params)) {\n isMissingParams = true\n }\n usedParams[key] = params[key]\n\n const prefix = path.substring(start, segment[1])\n const suffix = path.substring(segment[4], end)\n const value = encodeParam(key, params, decoder) ?? 'undefined'\n joined += '/' + prefix + value + suffix\n continue\n }\n\n if (kind === SEGMENT_TYPE_OPTIONAL_PARAM) {\n const key = path.substring(segment[2], segment[3])\n const valueRaw = params[key]\n\n // Check if optional parameter is missing or undefined\n if (valueRaw == null) continue\n\n usedParams[key] = valueRaw\n\n const prefix = path.substring(start, segment[1])\n const suffix = path.substring(segment[4], end)\n const value = encodeParam(key, params, decoder) ?? ''\n joined += '/' + prefix + value + suffix\n continue\n }\n }\n\n if (path.endsWith('/')) joined += '/'\n\n const interpolatedPath = joined || '/'\n\n return { usedParams, interpolatedPath, isMissingParams }\n}\n\nfunction encodePathParam(\n value: string,\n decoder?: InterpolatePathOptions['decoder'],\n) {\n const encoded = encodeURIComponent(value)\n return decoder?.(encoded) ?? encoded\n}\n"],"names":["length","cursor","joined","interpolatedPath"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"path.js","sources":["../../src/path.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { last } from './utils'\nimport {\n SEGMENT_TYPE_OPTIONAL_PARAM,\n SEGMENT_TYPE_PARAM,\n SEGMENT_TYPE_PATHNAME,\n SEGMENT_TYPE_WILDCARD,\n parseSegment,\n} from './new-process-route-tree'\nimport type { LRUCache } from './lru-cache'\n\n/** Join path segments, cleaning duplicate slashes between parts. */\nexport function joinPaths(paths: Array<string | undefined>) {\n return cleanPath(\n paths\n .filter((val) => {\n return val !== undefined\n })\n .join('/'),\n )\n}\n\n/** Remove repeated slashes from a path string. */\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\n/** Trim leading slashes (except preserving root '/'). */\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\n/** Trim trailing slashes (except preserving root '/'). */\nexport function trimPathRight(path: string) {\n const len = path.length\n return len > 1 && path[len - 1] === '/' ? path.replace(/\\/{1,}$/, '') : path\n}\n\n/** Trim both leading and trailing slashes. */\nexport function trimPath(path: string) {\n return trimPathRight(trimPathLeft(path))\n}\n\n/** Remove a trailing slash from value when appropriate for comparisons. */\nexport function removeTrailingSlash(value: string, basepath: string): string {\n if (value?.endsWith('/') && value !== '/' && value !== `${basepath}/`) {\n return value.slice(0, -1)\n }\n return value\n}\n\n// intended to only compare path name\n// see the usage in the isActive under useLinkProps\n// /sample/path1 = /sample/path1/\n// /sample/path1/some <> /sample/path1\n/**\n * Compare two pathnames for exact equality after normalizing trailing slashes\n * relative to the provided `basepath`.\n */\nexport function exactPathTest(\n pathName1: string,\n pathName2: string,\n basepath: string,\n): boolean {\n return (\n removeTrailingSlash(pathName1, basepath) ===\n removeTrailingSlash(pathName2, basepath)\n )\n}\n\n// When resolving relative paths, we treat all paths as if they are trailing slash\n// documents. All trailing slashes are removed after the path is resolved.\n// Here are a few examples:\n//\n// /a/b/c + ./d = /a/b/c/d\n// /a/b/c + ../d = /a/b/d\n// /a/b/c + ./d/ = /a/b/c/d\n// /a/b/c + ../d/ = /a/b/d\n// /a/b/c + ./ = /a/b/c\n//\n// Absolute paths that start with `/` short circuit the resolution process to the root\n// path.\n//\n// Here are some examples:\n//\n// /a/b/c + /d = /d\n// /a/b/c + /d/ = /d\n// /a/b/c + / = /\n//\n// Non-.-prefixed paths are still treated as relative paths, resolved like `./`\n//\n// Here are some examples:\n//\n// /a/b/c + d = /a/b/c/d\n// /a/b/c + d/ = /a/b/c/d\n// /a/b/c + d/e = /a/b/c/d/e\ninterface ResolvePathOptions {\n base: string\n to: string\n trailingSlash?: 'always' | 'never' | 'preserve'\n cache?: LRUCache<string, string>\n}\n\n/**\n * Resolve a destination path against a base, honoring trailing-slash policy\n * and supporting relative segments (`.`/`..`) and absolute `to` values.\n */\nexport function resolvePath({\n base,\n to,\n trailingSlash = 'never',\n cache,\n}: ResolvePathOptions) {\n const isAbsolute = to.startsWith('/')\n const isBase = !isAbsolute && to === '.'\n\n let key\n if (cache) {\n // `trailingSlash` is static per router, so it doesn't need to be part of the cache key\n key = isAbsolute ? to : isBase ? base : base + '\\0' + to\n const cached = cache.get(key)\n if (cached) return cached\n }\n\n let baseSegments: Array<string>\n if (isBase) {\n baseSegments = base.split('/')\n } else if (isAbsolute) {\n baseSegments = to.split('/')\n } else {\n baseSegments = base.split('/')\n while (baseSegments.length > 1 && last(baseSegments) === '') {\n baseSegments.pop()\n }\n\n const toSegments = to.split('/')\n for (let index = 0, length = toSegments.length; index < length; index++) {\n const value = toSegments[index]!\n if (value === '') {\n if (!index) {\n // Leading slash\n baseSegments = [value]\n } else if (index === length - 1) {\n // Trailing Slash\n baseSegments.push(value)\n } else {\n // ignore inter-slashes\n }\n } else if (value === '..') {\n baseSegments.pop()\n } else if (value === '.') {\n // ignore\n } else {\n baseSegments.push(value)\n }\n }\n }\n\n if (baseSegments.length > 1) {\n if (last(baseSegments) === '') {\n if (trailingSlash === 'never') {\n baseSegments.pop()\n }\n } else if (trailingSlash === 'always') {\n baseSegments.push('')\n }\n }\n\n let segment\n let joined = ''\n for (let i = 0; i < baseSegments.length; i++) {\n if (i > 0) joined += '/'\n const part = baseSegments[i]!\n if (!part) continue\n segment = parseSegment(part, 0, segment)\n const kind = segment[0]\n if (kind === SEGMENT_TYPE_PATHNAME) {\n joined += part\n continue\n }\n const end = segment[5]\n const prefix = part.substring(0, segment[1])\n const suffix = part.substring(segment[4], end)\n const value = part.substring(segment[2], segment[3])\n if (kind === SEGMENT_TYPE_PARAM) {\n joined += prefix || suffix ? `${prefix}{$${value}}${suffix}` : `$${value}`\n } else if (kind === SEGMENT_TYPE_WILDCARD) {\n joined += prefix || suffix ? `${prefix}{$}${suffix}` : '$'\n } else {\n // SEGMENT_TYPE_OPTIONAL_PARAM\n joined += `${prefix}{-$${value}}${suffix}`\n }\n }\n joined = cleanPath(joined)\n const result = joined || '/'\n if (key && cache) cache.set(key, result)\n return result\n}\n\n/**\n * Create a pre-compiled decode config from allowed characters.\n * This should be called once at router initialization.\n */\nexport function compileDecodeCharMap(\n pathParamsAllowedCharacters: ReadonlyArray<string>,\n) {\n const charMap = new Map(\n pathParamsAllowedCharacters.map((char) => [encodeURIComponent(char), char]),\n )\n // Escape special regex characters and join with |\n const pattern = Array.from(charMap.keys())\n .map((key) => key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('|')\n const regex = new RegExp(pattern, 'g')\n return (encoded: string) =>\n encoded.replace(regex, (match) => charMap.get(match) ?? match)\n}\n\ninterface InterpolatePathOptions {\n path?: string\n params: Record<string, unknown>\n /**\n * A function that decodes a path parameter value.\n * Obtained from `compileDecodeCharMap(pathParamsAllowedCharacters)`.\n */\n decoder?: (encoded: string) => string\n /**\n * @internal\n * For testing only, in development mode we use the router.isServer value\n */\n server?: boolean\n}\n\ntype InterPolatePathResult = {\n interpolatedPath: string\n usedParams: Record<string, unknown>\n isMissingParams: boolean // true if any params were not available when being looked up in the params object\n}\n\nfunction encodeParam(\n key: string,\n params: InterpolatePathOptions['params'],\n decoder: InterpolatePathOptions['decoder'],\n): any {\n const value = params[key]\n if (typeof value !== 'string') return value\n\n if (key === '_splat') {\n // Early return if value only contains URL-safe characters (performance optimization)\n if (/^[a-zA-Z0-9\\-._~!/]*$/.test(value)) return value\n // the splat/catch-all routes shouldn't have the '/' encoded out\n // Use encodeURIComponent for each segment to properly encode spaces,\n // plus signs, and other special characters that encodeURI leaves unencoded\n return value\n .split('/')\n .map((segment) => encodePathParam(segment, decoder))\n .join('/')\n } else {\n return encodePathParam(value, decoder)\n }\n}\n\n/**\n * Interpolate params and wildcards into a route path template.\n *\n * - Encodes params safely (configurable allowed characters)\n * - Supports `{-$optional}` segments, `{prefix{$id}suffix}` and `{$}` wildcards\n */\nexport function interpolatePath({\n path,\n params,\n decoder,\n // `server` is marked @internal and stripped from .d.ts by `stripInternal`.\n // We avoid destructuring it in the function signature so the emitted\n // declaration doesn't reference a property that no longer exists.\n ...rest\n}: InterpolatePathOptions): InterPolatePathResult {\n // Tracking if any params are missing in the `params` object\n // when interpolating the path\n let isMissingParams = false\n const usedParams: Record<string, unknown> = Object.create(null)\n\n if (!path || path === '/')\n return { interpolatedPath: '/', usedParams, isMissingParams }\n if (!path.includes('$'))\n return { interpolatedPath: path, usedParams, isMissingParams }\n\n if (isServer ?? rest.server) {\n // Fast path for common templates like `/posts/$id` or `/files/$`.\n // Braced segments (`{...}`) are more complex (prefix/suffix/optional) and are\n // handled by the general parser below.\n if (path.indexOf('{') === -1) {\n const length = path.length\n let cursor = 0\n let joined = ''\n\n while (cursor < length) {\n // Skip slashes between segments. '/' code is 47\n while (cursor < length && path.charCodeAt(cursor) === 47) cursor++\n if (cursor >= length) break\n\n const start = cursor\n let end = path.indexOf('/', cursor)\n if (end === -1) end = length\n cursor = end\n\n const part = path.substring(start, end)\n if (!part) continue\n\n // `$id` or `$` (splat). '$' code is 36\n if (part.charCodeAt(0) === 36) {\n if (part.length === 1) {\n const splat = params._splat\n usedParams._splat = splat\n // TODO: Deprecate *\n usedParams['*'] = splat\n\n if (!splat) {\n isMissingParams = true\n continue\n }\n\n const value = encodeParam('_splat', params, decoder)\n joined += '/' + value\n } else {\n const key = part.substring(1)\n if (!isMissingParams && !(key in params)) {\n isMissingParams = true\n }\n usedParams[key] = params[key]\n\n const value = encodeParam(key, params, decoder) ?? 'undefined'\n joined += '/' + value\n }\n } else {\n joined += '/' + part\n }\n }\n\n if (path.endsWith('/')) joined += '/'\n\n const interpolatedPath = joined || '/'\n return { usedParams, interpolatedPath, isMissingParams }\n }\n }\n\n const length = path.length\n let cursor = 0\n let segment\n let joined = ''\n while (cursor < length) {\n const start = cursor\n segment = parseSegment(path, start, segment)\n const end = segment[5]\n cursor = end + 1\n\n if (start === end) continue\n\n const kind = segment[0]\n\n if (kind === SEGMENT_TYPE_PATHNAME) {\n joined += '/' + path.substring(start, end)\n continue\n }\n\n if (kind === SEGMENT_TYPE_WILDCARD) {\n const splat = params._splat\n usedParams._splat = splat\n // TODO: Deprecate *\n usedParams['*'] = splat\n\n const prefix = path.substring(start, segment[1])\n const suffix = path.substring(segment[4], end)\n\n // Check if _splat parameter is missing. _splat could be missing if undefined or an empty string or some other falsy value.\n if (!splat) {\n isMissingParams = true\n // For missing splat parameters, just return the prefix and suffix without the wildcard\n // If there is a prefix or suffix, return them joined, otherwise omit the segment\n if (prefix || suffix) {\n joined += '/' + prefix + suffix\n }\n continue\n }\n\n const value = encodeParam('_splat', params, decoder)\n joined += '/' + prefix + value + suffix\n continue\n }\n\n if (kind === SEGMENT_TYPE_PARAM) {\n const key = path.substring(segment[2], segment[3])\n if (!isMissingParams && !(key in params)) {\n isMissingParams = true\n }\n usedParams[key] = params[key]\n\n const prefix = path.substring(start, segment[1])\n const suffix = path.substring(segment[4], end)\n const value = encodeParam(key, params, decoder) ?? 'undefined'\n joined += '/' + prefix + value + suffix\n continue\n }\n\n if (kind === SEGMENT_TYPE_OPTIONAL_PARAM) {\n const key = path.substring(segment[2], segment[3])\n const valueRaw = params[key]\n\n // Check if optional parameter is missing or undefined\n if (valueRaw == null) continue\n\n usedParams[key] = valueRaw\n\n const prefix = path.substring(start, segment[1])\n const suffix = path.substring(segment[4], end)\n const value = encodeParam(key, params, decoder) ?? ''\n joined += '/' + prefix + value + suffix\n continue\n }\n }\n\n if (path.endsWith('/')) joined += '/'\n\n const interpolatedPath = joined || '/'\n\n return { usedParams, interpolatedPath, isMissingParams }\n}\n\nfunction encodePathParam(\n value: string,\n decoder?: InterpolatePathOptions['decoder'],\n) {\n const encoded = encodeURIComponent(value)\n return decoder?.(encoded) ?? encoded\n}\n"],"names":["length","cursor","joined","interpolatedPath"],"mappings":"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"}
|
package/dist/esm/qss.js
CHANGED
|
@@ -16,7 +16,7 @@ function toValue(str) {
|
|
|
16
16
|
}
|
|
17
17
|
function decode(str) {
|
|
18
18
|
const searchParams = new URLSearchParams(str);
|
|
19
|
-
const result =
|
|
19
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
20
20
|
for (const [key, value] of searchParams.entries()) {
|
|
21
21
|
const previousValue = result[key];
|
|
22
22
|
if (previousValue == null) {
|
package/dist/esm/qss.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qss.js","sources":["../../src/qss.ts"],"sourcesContent":["/**\n * Program is a reimplementation of the `qss` package:\n * Copyright (c) Luke Edwards luke.edwards05@gmail.com, MIT License\n * https://github.com/lukeed/qss/blob/master/license.md\n *\n * This reimplementation uses modern browser APIs\n * (namely URLSearchParams) and TypeScript while still\n * maintaining the original functionality and interface.\n *\n * Update: this implementation has also been mangled to\n * fit exactly our use-case (single value per key in encoding).\n */\n\n/**\n * Encodes an object into a query string.\n * @param obj - The object to encode into a query string.\n * @param stringify - An optional custom stringify function.\n * @returns The encoded query string.\n * @example\n * ```\n * // Example input: encode({ token: 'foo', key: 'value' })\n * // Expected output: \"token=foo&key=value\"\n * ```\n */\nexport function encode(\n obj: Record<string, any>,\n stringify: (value: any) => string = String,\n): string {\n const result = new URLSearchParams()\n\n for (const key in obj) {\n const val = obj[key]\n if (val !== undefined) {\n result.set(key, stringify(val))\n }\n }\n\n return result.toString()\n}\n\n/**\n * Converts a string value to its appropriate type (string, number, boolean).\n * @param mix - The string value to convert.\n * @returns The converted value.\n * @example\n * // Example input: toValue(\"123\")\n * // Expected output: 123\n */\nfunction toValue(str: unknown) {\n if (!str) return ''\n\n if (str === 'false') return false\n if (str === 'true') return true\n return +str * 0 === 0 && +str + '' === str ? +str : str\n}\n/**\n * Decodes a query string into an object.\n * @param str - The query string to decode.\n * @returns The decoded key-value pairs in an object format.\n * @example\n * // Example input: decode(\"token=foo&key=value\")\n * // Expected output: { \"token\": \"foo\", \"key\": \"value\" }\n */\nexport function decode(str: any): any {\n const searchParams = new URLSearchParams(str)\n\n const result: Record<string, unknown> =
|
|
1
|
+
{"version":3,"file":"qss.js","sources":["../../src/qss.ts"],"sourcesContent":["/**\n * Program is a reimplementation of the `qss` package:\n * Copyright (c) Luke Edwards luke.edwards05@gmail.com, MIT License\n * https://github.com/lukeed/qss/blob/master/license.md\n *\n * This reimplementation uses modern browser APIs\n * (namely URLSearchParams) and TypeScript while still\n * maintaining the original functionality and interface.\n *\n * Update: this implementation has also been mangled to\n * fit exactly our use-case (single value per key in encoding).\n */\n\n/**\n * Encodes an object into a query string.\n * @param obj - The object to encode into a query string.\n * @param stringify - An optional custom stringify function.\n * @returns The encoded query string.\n * @example\n * ```\n * // Example input: encode({ token: 'foo', key: 'value' })\n * // Expected output: \"token=foo&key=value\"\n * ```\n */\nexport function encode(\n obj: Record<string, any>,\n stringify: (value: any) => string = String,\n): string {\n const result = new URLSearchParams()\n\n for (const key in obj) {\n const val = obj[key]\n if (val !== undefined) {\n result.set(key, stringify(val))\n }\n }\n\n return result.toString()\n}\n\n/**\n * Converts a string value to its appropriate type (string, number, boolean).\n * @param mix - The string value to convert.\n * @returns The converted value.\n * @example\n * // Example input: toValue(\"123\")\n * // Expected output: 123\n */\nfunction toValue(str: unknown) {\n if (!str) return ''\n\n if (str === 'false') return false\n if (str === 'true') return true\n return +str * 0 === 0 && +str + '' === str ? +str : str\n}\n/**\n * Decodes a query string into an object.\n * @param str - The query string to decode.\n * @returns The decoded key-value pairs in an object format.\n * @example\n * // Example input: decode(\"token=foo&key=value\")\n * // Expected output: { \"token\": \"foo\", \"key\": \"value\" }\n */\nexport function decode(str: any): any {\n const searchParams = new URLSearchParams(str)\n\n const result: Record<string, unknown> = Object.create(null)\n\n for (const [key, value] of searchParams.entries()) {\n const previousValue = result[key]\n if (previousValue == null) {\n result[key] = toValue(value)\n } else if (Array.isArray(previousValue)) {\n previousValue.push(toValue(value))\n } else {\n result[key] = [previousValue, toValue(value)]\n }\n }\n\n return result\n}\n"],"names":[],"mappings":"AAwBO,SAAS,OACd,KACA,YAAoC,QAC5B;AACR,QAAM,SAAS,IAAI,gBAAA;AAEnB,aAAW,OAAO,KAAK;AACrB,UAAM,MAAM,IAAI,GAAG;AACnB,QAAI,QAAQ,QAAW;AACrB,aAAO,IAAI,KAAK,UAAU,GAAG,CAAC;AAAA,IAChC;AAAA,EACF;AAEA,SAAO,OAAO,SAAA;AAChB;AAUA,SAAS,QAAQ,KAAc;AAC7B,MAAI,CAAC,IAAK,QAAO;AAEjB,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,QAAQ,OAAQ,QAAO;AAC3B,SAAO,CAAC,MAAM,MAAM,KAAK,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM;AACtD;AASO,SAAS,OAAO,KAAe;AACpC,QAAM,eAAe,IAAI,gBAAgB,GAAG;AAE5C,QAAM,SAAkC,uBAAO,OAAO,IAAI;AAE1D,aAAW,CAAC,KAAK,KAAK,KAAK,aAAa,WAAW;AACjD,UAAM,gBAAgB,OAAO,GAAG;AAChC,QAAI,iBAAiB,MAAM;AACzB,aAAO,GAAG,IAAI,QAAQ,KAAK;AAAA,IAC7B,WAAW,MAAM,QAAQ,aAAa,GAAG;AACvC,oBAAc,KAAK,QAAQ,KAAK,CAAC;AAAA,IACnC,OAAO;AACL,aAAO,GAAG,IAAI,CAAC,eAAe,QAAQ,KAAK,CAAC;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO;AACT;"}
|
package/dist/esm/router.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createStore } from "@tanstack/store";
|
|
|
2
2
|
import { createBrowserHistory, parseHref } from "@tanstack/history";
|
|
3
3
|
import { isServer } from "@tanstack/router-core/isServer";
|
|
4
4
|
import { batch } from "./utils/batch.js";
|
|
5
|
-
import { createControlledPromise, isDangerousProtocol, deepEqual, DEFAULT_PROTOCOL_ALLOWLIST, replaceEqualDeep, last, decodePath, functionalUpdate, findLast, encodePathLikeUrl } from "./utils.js";
|
|
5
|
+
import { createControlledPromise, isDangerousProtocol, deepEqual, DEFAULT_PROTOCOL_ALLOWLIST, nullReplaceEqualDeep, replaceEqualDeep, last, decodePath, functionalUpdate, findLast, encodePathLikeUrl } from "./utils.js";
|
|
6
6
|
import { processRouteTree, processRouteMasks, findSingleMatch, findRouteMatch, findFlatMatch } from "./new-process-route-tree.js";
|
|
7
7
|
import { compileDecodeCharMap, trimPath, resolvePath, cleanPath, trimPathRight, interpolatePath } from "./path.js";
|
|
8
8
|
import { createLRUCache } from "./lru-cache.js";
|
|
@@ -233,7 +233,7 @@ class RouterCore {
|
|
|
233
233
|
pathname: decodePath(pathname).path,
|
|
234
234
|
external: false,
|
|
235
235
|
searchStr: searchStr2,
|
|
236
|
-
search:
|
|
236
|
+
search: nullReplaceEqualDeep(
|
|
237
237
|
previousLocation?.search,
|
|
238
238
|
parsedSearch2
|
|
239
239
|
),
|
|
@@ -253,7 +253,10 @@ class RouterCore {
|
|
|
253
253
|
pathname: decodePath(url.pathname).path,
|
|
254
254
|
external: !!this.rewrite && url.origin !== this.origin,
|
|
255
255
|
searchStr,
|
|
256
|
-
search:
|
|
256
|
+
search: nullReplaceEqualDeep(
|
|
257
|
+
previousLocation?.search,
|
|
258
|
+
parsedSearch
|
|
259
|
+
),
|
|
257
260
|
hash: decodePath(url.hash.slice(1)).path,
|
|
258
261
|
state: replaceEqualDeep(previousLocation?.state, state)
|
|
259
262
|
};
|
|
@@ -342,9 +345,12 @@ class RouterCore {
|
|
|
342
345
|
const defaultedFromPath = dest.unsafeRelative === "path" ? currentLocation.pathname : dest.from ?? lightweightResult.fullPath;
|
|
343
346
|
const fromPath = this.resolvePathWithBase(defaultedFromPath, ".");
|
|
344
347
|
const fromSearch = lightweightResult.search;
|
|
345
|
-
const fromParams =
|
|
348
|
+
const fromParams = Object.assign(
|
|
349
|
+
/* @__PURE__ */ Object.create(null),
|
|
350
|
+
lightweightResult.params
|
|
351
|
+
);
|
|
346
352
|
const nextTo = dest.to ? this.resolvePathWithBase(fromPath, `${dest.to}`) : this.resolvePathWithBase(fromPath, ".");
|
|
347
|
-
const nextParams = dest.params === false || dest.params === null ?
|
|
353
|
+
const nextParams = dest.params === false || dest.params === null ? /* @__PURE__ */ Object.create(null) : (dest.params ?? true) === true ? fromParams : Object.assign(
|
|
348
354
|
fromParams,
|
|
349
355
|
functionalUpdate(dest.params, fromParams)
|
|
350
356
|
);
|
|
@@ -402,7 +408,7 @@ class RouterCore {
|
|
|
402
408
|
destRoutes,
|
|
403
409
|
_includeValidateSearch: opts._includeValidateSearch
|
|
404
410
|
});
|
|
405
|
-
nextSearch =
|
|
411
|
+
nextSearch = nullReplaceEqualDeep(fromSearch, nextSearch);
|
|
406
412
|
const searchStr = this.options.stringifySearch(nextSearch);
|
|
407
413
|
const hash = dest.hash === true ? currentLocation.hash : dest.hash ? functionalUpdate(dest.hash, currentLocation.hash) : void 0;
|
|
408
414
|
const hashStr = hash ? `#${hash}` : "";
|
|
@@ -442,7 +448,7 @@ class RouterCore {
|
|
|
442
448
|
const next = build(dest);
|
|
443
449
|
let maskedNext = maskedDest ? build(maskedDest) : void 0;
|
|
444
450
|
if (!maskedNext) {
|
|
445
|
-
const params =
|
|
451
|
+
const params = /* @__PURE__ */ Object.create(null);
|
|
446
452
|
if (this.options.routeMasks) {
|
|
447
453
|
const match = findFlatMatch(
|
|
448
454
|
next.pathname,
|
|
@@ -455,7 +461,7 @@ class RouterCore {
|
|
|
455
461
|
params: maskParams,
|
|
456
462
|
...maskProps
|
|
457
463
|
} = match.route;
|
|
458
|
-
const nextParams = maskParams === false || maskParams === null ?
|
|
464
|
+
const nextParams = maskParams === false || maskParams === null ? /* @__PURE__ */ Object.create(null) : (maskParams ?? true) === true ? params : Object.assign(params, functionalUpdate(maskParams, params));
|
|
459
465
|
maskedDest = {
|
|
460
466
|
from: opts.from,
|
|
461
467
|
...maskProps,
|
|
@@ -1223,7 +1229,7 @@ class RouterCore {
|
|
|
1223
1229
|
cause,
|
|
1224
1230
|
params: previousMatch?.params ?? routeParams,
|
|
1225
1231
|
_strictParams: strictParams,
|
|
1226
|
-
search: previousMatch ?
|
|
1232
|
+
search: previousMatch ? nullReplaceEqualDeep(previousMatch.search, preMatchSearch) : nullReplaceEqualDeep(existingMatch.search, preMatchSearch),
|
|
1227
1233
|
_strictSearch: strictMatchSearch
|
|
1228
1234
|
};
|
|
1229
1235
|
} else {
|
|
@@ -1237,7 +1243,7 @@ class RouterCore {
|
|
|
1237
1243
|
_strictParams: strictParams,
|
|
1238
1244
|
pathname: interpolatedPath,
|
|
1239
1245
|
updatedAt: Date.now(),
|
|
1240
|
-
search: previousMatch ?
|
|
1246
|
+
search: previousMatch ? nullReplaceEqualDeep(previousMatch.search, preMatchSearch) : preMatchSearch,
|
|
1241
1247
|
_strictSearch: strictMatchSearch,
|
|
1242
1248
|
searchError: void 0,
|
|
1243
1249
|
status,
|
|
@@ -1281,7 +1287,7 @@ class RouterCore {
|
|
|
1281
1287
|
const route = this.looseRoutesById[match.routeId];
|
|
1282
1288
|
const existingMatch = this.getMatch(match.id);
|
|
1283
1289
|
const previousMatch = previousMatchesByRouteId.get(match.routeId);
|
|
1284
|
-
match.params = previousMatch ?
|
|
1290
|
+
match.params = previousMatch ? nullReplaceEqualDeep(previousMatch.params, routeParams) : routeParams;
|
|
1285
1291
|
if (!existingMatch) {
|
|
1286
1292
|
const parentMatch = matches[index - 1];
|
|
1287
1293
|
const parentContext = this.getParentContext(parentMatch);
|
|
@@ -1336,7 +1342,10 @@ class RouterCore {
|
|
|
1336
1342
|
if (canReuseParams) {
|
|
1337
1343
|
params = lastStateMatch.params;
|
|
1338
1344
|
} else {
|
|
1339
|
-
const strictParams =
|
|
1345
|
+
const strictParams = Object.assign(
|
|
1346
|
+
/* @__PURE__ */ Object.create(null),
|
|
1347
|
+
routeParams
|
|
1348
|
+
);
|
|
1340
1349
|
for (const route of matchedRoutes) {
|
|
1341
1350
|
try {
|
|
1342
1351
|
extractStrictParams(
|
|
@@ -1411,7 +1420,7 @@ function getMatchedRoutes({
|
|
|
1411
1420
|
routesById,
|
|
1412
1421
|
processedTree
|
|
1413
1422
|
}) {
|
|
1414
|
-
const routeParams =
|
|
1423
|
+
const routeParams = /* @__PURE__ */ Object.create(null);
|
|
1415
1424
|
const trimmedPath = trimPathRight(pathname);
|
|
1416
1425
|
let foundRoute = void 0;
|
|
1417
1426
|
let parsedParams = void 0;
|
|
@@ -1419,7 +1428,7 @@ function getMatchedRoutes({
|
|
|
1419
1428
|
if (match) {
|
|
1420
1429
|
foundRoute = match.route;
|
|
1421
1430
|
Object.assign(routeParams, match.rawParams);
|
|
1422
|
-
parsedParams = Object.assign(
|
|
1431
|
+
parsedParams = Object.assign(/* @__PURE__ */ Object.create(null), match.parsedParams);
|
|
1423
1432
|
}
|
|
1424
1433
|
const matchedRoutes = match?.branch || [routesById[rootRouteId]];
|
|
1425
1434
|
return { matchedRoutes, routeParams, foundRoute, parsedParams };
|