]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
refactor: rename matcher to resolver
authorEduardo San Martin Morote <posva13@gmail.com>
Wed, 8 Jan 2025 09:48:35 +0000 (10:48 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Wed, 8 Jan 2025 09:48:35 +0000 (10:48 +0100)
12 files changed:
packages/router/src/experimental/router.ts
packages/router/src/matcher/index.ts
packages/router/src/matcher/pathParserRanker.ts
packages/router/src/new-route-resolver/index.ts
packages/router/src/new-route-resolver/matcher-location.ts
packages/router/src/new-route-resolver/matcher-pattern.ts
packages/router/src/new-route-resolver/matcher-resolve.spec.ts
packages/router/src/new-route-resolver/matcher.spec.ts
packages/router/src/new-route-resolver/matcher.test-d.ts
packages/router/src/new-route-resolver/matchers/test-utils.ts
packages/router/src/new-route-resolver/resolver.ts [moved from packages/router/src/new-route-resolver/matcher.ts with 85% similarity]
packages/router/src/utils/index.ts

index 95762e89996dc3746f3c8696496a6c658ed902a5..9cf885cc1dfc6f0d63589a325635d03ef73f7f0f 100644 (file)
@@ -27,8 +27,8 @@ import type {
   NEW_LocationResolved,
   NEW_MatcherRecord,
   NEW_MatcherRecordRaw,
-  NEW_RouterMatcher,
-} from '../new-route-resolver/matcher'
+  NEW_RouterResolver,
+} from '../new-route-resolver/resolver'
 import {
   parseQuery as originalParseQuery,
   stringifyQuery as originalStringifyQuery,
@@ -51,7 +51,6 @@ import type {
   RouteLocationAsRelative,
   RouteLocationAsRelativeTyped,
   RouteLocationAsString,
-  RouteLocationGeneric,
   RouteLocationNormalized,
   RouteLocationNormalizedLoaded,
   RouteLocationRaw,
@@ -191,7 +190,7 @@ export interface EXPERIMENTAL_RouterOptions<
    * Matcher to use to resolve routes.
    * @experimental
    */
-  matcher: NEW_RouterMatcher<NEW_MatcherRecordRaw, TMatcherRecord>
+  matcher: NEW_RouterResolver<NEW_MatcherRecordRaw, TMatcherRecord>
 }
 
 /**
@@ -407,6 +406,9 @@ export interface EXPERIMENTAL_RouteRecordRaw extends NEW_MatcherRecordRaw {
 
 // TODO: is it worth to have 2 types for the undefined values?
 export interface EXPERIMENTAL_RouteRecordNormalized extends NEW_MatcherRecord {
+  /**
+   * Arbitrary data attached to the record.
+   */
   meta: RouteMeta
 }
 
@@ -468,7 +470,7 @@ export function experimental_createRouter(
       | EXPERIMENTAL_RouteRecordRaw,
     route?: EXPERIMENTAL_RouteRecordRaw
   ) {
-    let parent: Parameters<(typeof matcher)['addRoute']>[1] | undefined
+    let parent: Parameters<(typeof matcher)['addMatcher']>[1] | undefined
     let rawRecord: EXPERIMENTAL_RouteRecordRaw
 
     if (isRouteName(parentOrRoute)) {
@@ -486,20 +488,20 @@ export function experimental_createRouter(
       rawRecord = parentOrRoute
     }
 
-    const addedRecord = matcher.addRoute(
+    const addedRecord = matcher.addMatcher(
       normalizeRouteRecord(rawRecord),
       parent
     )
 
     return () => {
-      matcher.removeRoute(addedRecord)
+      matcher.removeMatcher(addedRecord)
     }
   }
 
   function removeRoute(name: NonNullable<RouteRecordNameGeneric>) {
     const recordMatcher = matcher.getMatcher(name)
     if (recordMatcher) {
-      matcher.removeRoute(recordMatcher)
+      matcher.removeMatcher(recordMatcher)
     } else if (__DEV__) {
       warn(`Cannot remove non-existent route "${String(name)}"`)
     }
@@ -1219,7 +1221,7 @@ export function experimental_createRouter(
 
     addRoute,
     removeRoute,
-    clearRoutes: matcher.clearRoutes,
+    clearRoutes: matcher.clearMatchers,
     hasRoute,
     getRoutes,
     resolve,
index fe951f7ad179eb5f7ae3bb1a7eeaa672190a0259..1a2541d72e0cba5e18a4e8c3a2100e9a9bda7d3d 100644 (file)
@@ -14,10 +14,13 @@ import type {
   _PathParserOptions,
 } from './pathParserRanker'
 
-import { comparePathParserScore } from './pathParserRanker'
+import {
+  comparePathParserScore,
+  PATH_PARSER_OPTIONS_DEFAULTS,
+} from './pathParserRanker'
 
 import { warn } from '../warning'
-import { assign, noop } from '../utils'
+import { assign, mergeOptions, noop } from '../utils'
 import type { RouteRecordNameGeneric, _RouteRecordProps } from '../typed-routes'
 
 /**
@@ -64,8 +67,8 @@ export function createRouterMatcher(
     NonNullable<RouteRecordNameGeneric>,
     RouteRecordMatcher
   >()
-  globalOptions = mergeOptions(
-    { strict: false, end: true, sensitive: false } as PathParserOptions,
+  globalOptions = mergeOptions<PathParserOptions>(
+    PATH_PARSER_OPTIONS_DEFAULTS,
     globalOptions
   )
 
@@ -429,7 +432,7 @@ export function normalizeRouteRecord(
  * components. Also accept a boolean for components.
  * @param record
  */
-function normalizeRecordProps(
+export function normalizeRecordProps(
   record: RouteRecordRaw
 ): Record<string, _RouteRecordProps> {
   const propsObject = {} as Record<string, _RouteRecordProps>
@@ -472,18 +475,6 @@ function mergeMetaFields(matched: MatcherLocation['matched']) {
   )
 }
 
-function mergeOptions<T extends object>(
-  defaults: T,
-  partialOptions: Partial<T>
-): T {
-  const options = {} as T
-  for (const key in defaults) {
-    options[key] = key in partialOptions ? partialOptions[key]! : defaults[key]
-  }
-
-  return options
-}
-
 type ParamKey = RouteRecordMatcher['keys'][number]
 
 function isSameParam(a: ParamKey, b: ParamKey): boolean {
@@ -521,7 +512,7 @@ function checkSameParams(a: RouteRecordMatcher, b: RouteRecordMatcher) {
  * @param mainNormalizedRecord - RouteRecordNormalized
  * @param parent - RouteRecordMatcher
  */
-function checkChildMissingNameWithEmptyPath(
+export function checkChildMissingNameWithEmptyPath(
   mainNormalizedRecord: RouteRecordNormalized,
   parent?: RouteRecordMatcher
 ) {
index 81b0776427444c3e15f265fe29ca630666af746d..b2c0b40a050a7565b4a3a8d8ae51f583feea0adb 100644 (file)
@@ -367,3 +367,8 @@ function isLastScoreNegative(score: PathParser['score']): boolean {
   const last = score[score.length - 1]
   return score.length > 0 && last[last.length - 1] < 0
 }
+export const PATH_PARSER_OPTIONS_DEFAULTS: PathParserOptions = {
+  strict: false,
+  end: true,
+  sensitive: false,
+}
index 17910f62f7e1aee13533da35926ccbe6bb971a5d..4c07b32cc21943cc5c6c78142aadc0a3afba2315 100644 (file)
@@ -1 +1 @@
-export { createCompiledMatcher } from './matcher'
+export { createCompiledMatcher } from './resolver'
index b9ca1ab0cc2948f9fe37163b3123e9a463525cf4..f597df07ff49dfe7c764da4179231c501c765300 100644 (file)
@@ -1,5 +1,5 @@
 import type { LocationQueryRaw } from '../query'
-import type { MatcherName } from './matcher'
+import type { MatcherName } from './resolver'
 
 /**
  * Generic object of params that can be passed to a matcher.
index c627c3bffa853b4add402096a3828a17efbbf18b..0f7d8c1920df4ed442b403c32a49aae99e078444 100644 (file)
@@ -1,4 +1,4 @@
-import { decode, MatcherQueryParams } from './matcher'
+import { decode, MatcherQueryParams } from './resolver'
 import { EmptyParams, MatcherParamsFormatted } from './matcher-location'
 import { miss } from './matchers/errors'
 
index 91fb8fb2442692ee3fa4433b78860b83b5807e04..6f9914af2b5de9d03d512c24a71cec5106ce7a5e 100644 (file)
@@ -1,6 +1,5 @@
 import { createRouterMatcher, normalizeRouteRecord } from '../matcher'
 import { RouteComponent, RouteRecordRaw, MatcherLocation } from '../types'
-import { MatcherLocationNormalizedLoose } from '../../__tests__/utils'
 import { defineComponent } from 'vue'
 import { START_LOCATION_NORMALIZED } from '../location'
 import { describe, expect, it } from 'vitest'
@@ -10,7 +9,8 @@ import {
   MatcherLocationRaw,
   NEW_MatcherRecordRaw,
   NEW_LocationResolved,
-} from './matcher'
+  NEW_MatcherRecord,
+} from './resolver'
 import { PathParams, tokensToParser } from '../matcher/pathParserRanker'
 import { tokenizePath } from '../matcher/pathTokenizer'
 import { miss } from './matchers/errors'
@@ -63,22 +63,23 @@ function compileRouteRecord(
 
 describe('RouterMatcher.resolve', () => {
   mockWarn()
-  type Matcher = ReturnType<typeof createRouterMatcher>
+  type Matcher = ReturnType<typeof createCompiledMatcher>
   type MatcherResolvedLocation = ReturnType<Matcher['resolve']>
 
-  const START_LOCATION: NEW_LocationResolved = {
+  const START_LOCATION: MatcherResolvedLocation = {
     name: Symbol('START'),
-    fullPath: '/',
-    path: '/',
     params: {},
+    path: '/',
+    fullPath: '/',
     query: {},
     hash: '',
     matched: [],
+    // meta: {},
   }
 
   function isMatcherLocationResolved(
     location: unknown
-  ): location is NEW_LocationResolved {
+  ): location is NEW_LocationResolved<NEW_MatcherRecord> {
     return !!(
       location &&
       typeof location === 'object' &&
@@ -95,16 +96,16 @@ describe('RouterMatcher.resolve', () => {
     toLocation: MatcherLocationRaw,
     expectedLocation: Partial<MatcherResolvedLocation>,
     fromLocation:
-      | NEW_LocationResolved
+      | NEW_LocationResolved<NEW_MatcherRecord>
       | Exclude<MatcherLocationRaw, string>
       | `/${string}` = START_LOCATION
   ) {
     const records = (Array.isArray(record) ? record : [record]).map(
       (record): NEW_MatcherRecordRaw => compileRouteRecord(record)
     )
-    const matcher = createCompiledMatcher()
+    const matcher = createCompiledMatcher<NEW_MatcherRecord>()
     for (const record of records) {
-      matcher.addRoute(record)
+      matcher.addMatcher(record)
     }
 
     const resolved: MatcherResolvedLocation = {
@@ -137,60 +138,6 @@ describe('RouterMatcher.resolve', () => {
     })
   }
 
-  function _assertRecordMatch(
-    record: RouteRecordRaw | RouteRecordRaw[],
-    location: MatcherLocationRaw,
-    resolved: Partial<MatcherLocationNormalizedLoose>,
-    start: MatcherLocation = START_LOCATION_NORMALIZED
-  ) {
-    record = Array.isArray(record) ? record : [record]
-    const matcher = createRouterMatcher(record, {})
-
-    if (!('meta' in resolved)) {
-      resolved.meta = record[0].meta || {}
-    }
-
-    if (!('name' in resolved)) {
-      resolved.name = undefined
-    }
-
-    // add location if provided as it should be the same value
-    if ('path' in location && !('path' in resolved)) {
-      resolved.path = location.path
-    }
-
-    if ('redirect' in record) {
-      throw new Error('not handled')
-    } else {
-      // use one single record
-      if (!resolved.matched) resolved.matched = record.map(normalizeRouteRecord)
-      // allow passing an expect.any(Array)
-      else if (Array.isArray(resolved.matched))
-        resolved.matched = resolved.matched.map(m => ({
-          ...normalizeRouteRecord(m as any),
-          aliasOf: m.aliasOf,
-        }))
-    }
-
-    // allows not passing params
-    resolved.params =
-      resolved.params || ('params' in location ? location.params : {})
-
-    const startCopy: MatcherLocation = {
-      ...start,
-      matched: start.matched.map(m => ({
-        ...normalizeRouteRecord(m),
-        aliasOf: m.aliasOf,
-      })) as MatcherLocation['matched'],
-    }
-
-    // make matched non enumerable
-    Object.defineProperty(startCopy, 'matched', { enumerable: false })
-
-    const result = matcher.resolve(location, startCopy)
-    expect(result).toEqual(resolved)
-  }
-
   /**
    *
    * @param record - Record or records we are testing the matcher against
index a0c59e6d96176bd0a8bad625cf21a956ad9293c5..335ddb83d763182eda4a1b471c926979ac52f0a4 100644 (file)
@@ -3,7 +3,7 @@ import {
   createCompiledMatcher,
   NO_MATCH_LOCATION,
   pathEncoded,
-} from './matcher'
+} from './resolver'
 import {
   MatcherPatternParams_Base,
   MatcherPatternPath,
@@ -11,7 +11,7 @@ import {
   MatcherPatternPathStatic,
   MatcherPatternPathDynamic,
 } from './matcher-pattern'
-import { NEW_MatcherRecord } from './matcher'
+import { NEW_MatcherRecord } from './resolver'
 import { miss } from './matchers/errors'
 import { EmptyParams } from './matcher-location'
 
@@ -133,25 +133,25 @@ describe('RouterMatcher', () => {
   describe('adding and removing', () => {
     it('add static path', () => {
       const matcher = createCompiledMatcher()
-      matcher.addRoute(EMPTY_PATH_ROUTE)
+      matcher.addMatcher(EMPTY_PATH_ROUTE)
     })
 
     it('adds dynamic path', () => {
       const matcher = createCompiledMatcher()
-      matcher.addRoute(USER_ID_ROUTE)
+      matcher.addMatcher(USER_ID_ROUTE)
     })
 
     it('removes static path', () => {
       const matcher = createCompiledMatcher()
-      matcher.addRoute(EMPTY_PATH_ROUTE)
-      matcher.removeRoute(EMPTY_PATH_ROUTE)
+      matcher.addMatcher(EMPTY_PATH_ROUTE)
+      matcher.removeMatcher(EMPTY_PATH_ROUTE)
       // Add assertions to verify the route was removed
     })
 
     it('removes dynamic path', () => {
       const matcher = createCompiledMatcher()
-      matcher.addRoute(USER_ID_ROUTE)
-      matcher.removeRoute(USER_ID_ROUTE)
+      matcher.addMatcher(USER_ID_ROUTE)
+      matcher.removeMatcher(USER_ID_ROUTE)
       // Add assertions to verify the route was removed
     })
   })
index 8ea5b771d7fc76acc9ab733947fd6e285441dc63..26060c3a69f8df7f2fbef2f62464d2259e293e3d 100644 (file)
@@ -2,15 +2,15 @@ import { describe, expectTypeOf, it } from 'vitest'
 import {
   NEW_LocationResolved,
   NEW_MatcherRecordRaw,
-  NEW_RouterMatcher,
-} from './matcher'
+  NEW_RouterResolver,
+} from './resolver'
 import { EXPERIMENTAL_RouteRecordNormalized } from '../experimental/router'
 
 describe('Matcher', () => {
   type TMatcherRecordRaw = NEW_MatcherRecordRaw
   type TMatcherRecord = EXPERIMENTAL_RouteRecordNormalized
 
-  const matcher: NEW_RouterMatcher<TMatcherRecordRaw, TMatcherRecord> =
+  const matcher: NEW_RouterResolver<TMatcherRecordRaw, TMatcherRecord> =
     {} as any
 
   describe('matcher.resolve()', () => {
index e922e721731b527785e6c6a9f35693f021c34378..250efafd919fcc2d03a92073d9326782e8cac068 100644 (file)
@@ -4,7 +4,7 @@ import {
   MatcherPatternQuery,
   MatcherPatternParams_Base,
 } from '../matcher-pattern'
-import { NEW_MatcherRecord } from '../matcher'
+import { NEW_MatcherRecord } from '../resolver'
 import { miss } from './errors'
 
 export const ANY_PATH_PATTERN_MATCHER: MatcherPatternPath<{
similarity index 85%
rename from packages/router/src/new-route-resolver/matcher.ts
rename to packages/router/src/new-route-resolver/resolver.ts
index c0ba504ced0e8d63c87179aa92a3bcee78ab4946..17ceecf02ffe0d14fed475136bb3f6c61647f0a2 100644 (file)
@@ -27,11 +27,13 @@ import { _RouteRecordProps } from '../typed-routes'
 export type MatcherName = string | symbol
 
 /**
- * Manage and resolve routes. Also handles the encoding, decoding, parsing and serialization of params, query, and hash.
- * `TMatcherRecordRaw` represents the raw record type passed to {@link addRoute}.
- * `TMatcherRecord` represents the normalized record type.
+ * Manage and resolve routes. Also handles the encoding, decoding, parsing and
+ * serialization of params, query, and hash.
+ *
+ * - `TMatcherRecordRaw` represents the raw record type passed to {@link addMatcher}.
+ * - `TMatcherRecord` represents the normalized record type returned by {@link getMatchers}.
  */
-export interface NEW_RouterMatcher<TMatcherRecordRaw, TMatcherRecord> {
+export interface NEW_RouterResolver<TMatcherRecordRaw, TMatcherRecord> {
   /**
    * Resolves an absolute location (like `/path/to/somewhere`).
    */
@@ -80,9 +82,26 @@ export interface NEW_RouterMatcher<TMatcherRecordRaw, TMatcherRecord> {
     currentLocation: NEW_LocationResolved<TMatcherRecord>
   ): NEW_LocationResolved<TMatcherRecord>
 
-  addRoute(matcher: TMatcherRecordRaw, parent?: TMatcherRecord): TMatcherRecord
-  removeRoute(matcher: TMatcherRecord): void
-  clearRoutes(): void
+  /**
+   * Add a matcher record. Previously named `addRoute()`.
+   * @param matcher - The matcher record to add.
+   * @param parent - The parent matcher record if this is a child.
+   */
+  addMatcher(
+    matcher: TMatcherRecordRaw,
+    parent?: TMatcherRecord
+  ): TMatcherRecord
+
+  /**
+   * Remove a matcher by its name. Previously named `removeRoute()`.
+   * @param matcher - The matcher (returned by {@link addMatcher}) to remove.
+   */
+  removeMatcher(matcher: TMatcherRecord): void
+
+  /**
+   * Remove all matcher records. Prevoisly named `clearRoutes()`.
+   */
+  clearMatchers(): void
 
   /**
    * Get a list of all matchers.
@@ -98,7 +117,7 @@ export interface NEW_RouterMatcher<TMatcherRecordRaw, TMatcherRecord> {
 }
 
 /**
- * Allowed location objects to be passed to {@link NEW_RouterMatcher['resolve']}
+ * Allowed location objects to be passed to {@link NEW_RouterResolver['resolve']}
  */
 export type MatcherLocationRaw =
   | `/${string}`
@@ -108,20 +127,6 @@ export type MatcherLocationRaw =
   | MatcherLocationAsPathRelative
   | MatcherLocationAsRelative
 
-/**
- * Matcher capable of adding and removing routes at runtime.
- */
-export interface NEW_Matcher_Dynamic {
-  addRoute(record: TODO, parent?: TODO): () => void
-
-  removeRoute(record: TODO): void
-  removeRoute(name: MatcherName): void
-
-  clearRoutes(): void
-}
-
-type TODO = any
-
 export interface NEW_LocationResolved<TMatched> {
   // FIXME: remove `undefined`
   name: MatcherName | undefined
@@ -234,7 +239,7 @@ export const NO_MATCH_LOCATION = {
 // FIXME: later on, the MatcherRecord should be compatible with RouteRecordRaw (which can miss a path, have children, etc)
 
 /**
- * Experiment new matcher record base type.
+ * Experimental new matcher record base type.
  *
  * @experimental
  */
@@ -267,10 +272,7 @@ export interface NEW_MatcherRecordRaw {
   children?: NEW_MatcherRecordRaw[]
 }
 
-/**
- * Normalized version of a {@link NEW_MatcherRecordRaw} record.
- */
-export interface NEW_MatcherRecord {
+export interface NEW_MatcherRecordBase<T> {
   /**
    * Name of the matcher. Unique across all matchers.
    */
@@ -280,9 +282,15 @@ export interface NEW_MatcherRecord {
   query?: MatcherPatternQuery
   hash?: MatcherPatternHash
 
-  parent?: NEW_MatcherRecord
+  parent?: T
 }
 
+/**
+ * Normalized version of a {@link NEW_MatcherRecordRaw} record.
+ */
+export interface NEW_MatcherRecord
+  extends NEW_MatcherRecordBase<NEW_MatcherRecord> {}
+
 /**
  * Tagged template helper to encode params into a path. Doesn't work with null
  */
@@ -310,9 +318,9 @@ export function pathEncoded(
 /**
  * Build the `matched` array of a record that includes all parent records from the root to the current one.
  */
-function buildMatched(record: NEW_MatcherRecord): NEW_MatcherRecord[] {
-  const matched: NEW_MatcherRecord[] = []
-  let node: NEW_MatcherRecord | undefined = record
+function buildMatched<T extends NEW_MatcherRecordBase<T>>(record: T): T[] {
+  const matched: T[] = []
+  let node: T | undefined = record
   while (node) {
     matched.unshift(node)
     node = node.parent
@@ -320,11 +328,13 @@ function buildMatched(record: NEW_MatcherRecord): NEW_MatcherRecord[] {
   return matched
 }
 
-export function createCompiledMatcher(
+export function createCompiledMatcher<
+  TMatcherRecord extends NEW_MatcherRecordBase<TMatcherRecord>
+>(
   records: NEW_MatcherRecordRaw[] = []
-): NEW_RouterMatcher<NEW_MatcherRecordRaw, NEW_MatcherRecord> {
+): NEW_RouterResolver<NEW_MatcherRecordRaw, TMatcherRecord> {
   // TODO: we also need an array that has the correct order
-  const matchers = new Map<MatcherName, NEW_MatcherRecord>()
+  const matchers = new Map<MatcherName, TMatcherRecord>()
 
   // TODO: allow custom encode/decode functions
   // const encodeParams = applyToParams.bind(null, encodeParam)
@@ -340,26 +350,26 @@ export function createCompiledMatcher(
   type MatcherResolveArgs =
     | [
         absoluteLocation: `/${string}`,
-        currentLocation?: undefined | NEW_LocationResolved<NEW_MatcherRecord>
+        currentLocation?: undefined | NEW_LocationResolved<TMatcherRecord>
       ]
     | [
         relativeLocation: string,
-        currentLocation: NEW_LocationResolved<NEW_MatcherRecord>
+        currentLocation: NEW_LocationResolved<TMatcherRecord>
       ]
     | [absoluteLocation: MatcherLocationAsPathAbsolute]
     | [
         relativeLocation: MatcherLocationAsPathRelative,
-        currentLocation: NEW_LocationResolved<NEW_MatcherRecord>
+        currentLocation: NEW_LocationResolved<TMatcherRecord>
       ]
     | [location: MatcherLocationAsNamed]
     | [
         relativeLocation: MatcherLocationAsRelative,
-        currentLocation: NEW_LocationResolved<NEW_MatcherRecord>
+        currentLocation: NEW_LocationResolved<TMatcherRecord>
       ]
 
   function resolve(
     ...args: MatcherResolveArgs
-  ): NEW_LocationResolved<NEW_MatcherRecord> {
+  ): NEW_LocationResolved<TMatcherRecord> {
     const [location, currentLocation] = args
 
     // string location, e.g. '/foo', '../bar', 'baz', '?page=1'
@@ -367,10 +377,8 @@ export function createCompiledMatcher(
       // parseURL handles relative paths
       const url = parseURL(parseQuery, location, currentLocation?.path)
 
-      let matcher: NEW_MatcherRecord | undefined
-      let matched:
-        | NEW_LocationResolved<NEW_MatcherRecord>['matched']
-        | undefined
+      let matcher: TMatcherRecord | undefined
+      let matched: NEW_LocationResolved<TMatcherRecord>['matched'] | undefined
       let parsedParams: MatcherParamsFormatted | null | undefined
 
       for (matcher of matchers.values()) {
@@ -475,10 +483,11 @@ export function createCompiledMatcher(
     }
   }
 
-  function addRoute(record: NEW_MatcherRecordRaw, parent?: NEW_MatcherRecord) {
+  function addRoute(record: NEW_MatcherRecordRaw, parent?: TMatcherRecord) {
     const name = record.name ?? (__DEV__ ? Symbol('unnamed-route') : Symbol())
     // FIXME: proper normalization of the record
-    const normalizedRecord: NEW_MatcherRecord = {
+    // @ts-expect-error: we are not properly normalizing the record yet
+    const normalizedRecord: TMatcherRecord = {
       ...record,
       name,
       parent,
@@ -491,7 +500,7 @@ export function createCompiledMatcher(
     addRoute(record)
   }
 
-  function removeRoute(matcher: NEW_MatcherRecord) {
+  function removeRoute(matcher: TMatcherRecord) {
     matchers.delete(matcher.name)
     // TODO: delete children and aliases
   }
@@ -511,9 +520,9 @@ export function createCompiledMatcher(
   return {
     resolve,
 
-    addRoute,
-    removeRoute,
-    clearRoutes,
+    addMatcher: addRoute,
+    removeMatcher: removeRoute,
+    clearMatchers: clearRoutes,
     getMatcher,
     getMatchers,
   }
index a7c42f4cff551f320e4b2e2f35fc97fd1c72f153..c6d622095ab50002a261a60fbbe83a527ce1c49f 100644 (file)
@@ -58,3 +58,15 @@ export const noop = () => {}
  */
 export const isArray: (arg: ArrayLike<any> | any) => arg is ReadonlyArray<any> =
   Array.isArray
+
+export function mergeOptions<T extends object>(
+  defaults: T,
+  partialOptions: Partial<T>
+): T {
+  const options = {} as T
+  for (const key in defaults) {
+    options[key] = key in partialOptions ? partialOptions[key]! : defaults[key]
+  }
+
+  return options
+}