{"version":3,"file":"route-hmr-statement.cjs","names":[],"sources":["../../../src/core/route-hmr-statement.ts"],"sourcesContent":["import * as template from '@babel/template'\nimport { createHmrHotExpressionAst } from './hmr-hot-expression'\nimport type * as t from '@babel/types'\nimport type {\n  AnyRoute,\n  AnyRouteMatch,\n  AnyRouter,\n  RouterWritableStore,\n} from '@tanstack/router-core'\n\ntype AnyRouteWithPrivateProps = AnyRoute & {\n  options: Record<string, unknown>\n  _componentsPromise?: Promise<void>\n  _lazyPromise?: Promise<void>\n  update: (options: Record<string, unknown>) => unknown\n  _path: string\n  _id: string\n  _fullPath: string\n  _to: string\n}\n\ntype AnyRouterWithPrivateMaps = AnyRouter & {\n  routesById: Record<string, AnyRoute>\n  routesByPath: Record<string, AnyRoute>\n  stores: AnyRouter['stores'] & {\n    cachedMatchStores: Map<\n      string,\n      Pick<RouterWritableStore<AnyRouteMatch>, 'set'>\n    >\n    pendingMatchStores: Map<\n      string,\n      Pick<RouterWritableStore<AnyRouteMatch>, 'set'>\n    >\n    matchStores: Map<string, Pick<RouterWritableStore<AnyRouteMatch>, 'set'>>\n  }\n}\n\ntype AnyRouteMatchWithPrivateProps = AnyRouteMatch & {\n  __beforeLoadContext?: unknown\n}\n\nfunction handleRouteUpdate(\n  routeId: string,\n  newRoute: AnyRouteWithPrivateProps,\n) {\n  const router = window.__TSR_ROUTER__ as AnyRouterWithPrivateMaps\n  const oldRoute = router.routesById[routeId] as\n    | AnyRouteWithPrivateProps\n    | undefined\n\n  if (!oldRoute) {\n    return\n  }\n\n  // Keys whose identity must remain stable to prevent React from\n  // unmounting/remounting the component tree.  React Fast Refresh already\n  // handles hot-updating the function bodies of these components — our job\n  // is only to update non-component route options (loader, head, etc.).\n  // For code-split (splittable) routes, the lazyRouteComponent wrapper is\n  // already cached in the bundler hot data so its identity is stable.\n  // For unsplittable routes (e.g. root routes), the component is a plain\n  // function reference that gets recreated on every module re-execution,\n  // so we must explicitly preserve the old reference.\n  const removedKeys = new Set<string>()\n  Object.keys(oldRoute.options).forEach((key) => {\n    if (!(key in newRoute.options)) {\n      removedKeys.add(key)\n      delete oldRoute.options[key]\n    }\n  })\n\n  // Preserve component identity so React doesn't remount.\n  // React Fast Refresh patches the function bodies in-place.\n  const componentKeys = '__TSR_COMPONENT_TYPES__' as unknown as Array<string>\n  componentKeys.forEach((key) => {\n    if (key in oldRoute.options && key in newRoute.options) {\n      newRoute.options[key] = oldRoute.options[key]\n    }\n  })\n\n  oldRoute.options = newRoute.options\n  oldRoute.update(newRoute.options)\n  oldRoute._componentsPromise = undefined\n  oldRoute._lazyPromise = undefined\n\n  router.routesById[oldRoute.id] = oldRoute\n  router.routesByPath[oldRoute.fullPath] = oldRoute\n\n  router.processedTree.matchCache.clear()\n  router.processedTree.flatCache?.clear()\n  router.processedTree.singleCache.clear()\n  router.resolvePathCache.clear()\n  walkReplaceSegmentTree(oldRoute, router.processedTree.segmentTree)\n\n  const filter = (m: AnyRouteMatch) => m.routeId === oldRoute.id\n  const activeMatch = router.stores.matches.get().find(filter)\n  const pendingMatch = router.stores.pendingMatches.get().find(filter)\n  const cachedMatches = router.stores.cachedMatches.get().filter(filter)\n\n  if (activeMatch || pendingMatch || cachedMatches.length > 0) {\n    // Clear stale match data for removed route options BEFORE invalidating.\n    // Without this, router.invalidate() -> matchRoutes() reuses the existing\n    // match from the store (via ...existingMatch spread) and the stale\n    // loaderData / __beforeLoadContext survives the reload cycle.\n    //\n    // We must update the store directly (not via router.updateMatch) because\n    // updateMatch wraps in startTransition which may defer the state update,\n    // and we need the clear to be visible before invalidate reads the store.\n    if (removedKeys.has('loader') || removedKeys.has('beforeLoad')) {\n      const matchIds = [\n        activeMatch?.id,\n        pendingMatch?.id,\n        ...cachedMatches.map((match) => match.id),\n      ].filter(Boolean) as Array<string>\n      router.batch(() => {\n        for (const matchId of matchIds) {\n          const store =\n            router.stores.pendingMatchStores.get(matchId) ||\n            router.stores.matchStores.get(matchId) ||\n            router.stores.cachedMatchStores.get(matchId)\n          if (store) {\n            store.set((prev) => {\n              const next: AnyRouteMatchWithPrivateProps = { ...prev }\n\n              if (removedKeys.has('loader')) {\n                next.loaderData = undefined\n              }\n              if (removedKeys.has('beforeLoad')) {\n                next.__beforeLoadContext = undefined\n              }\n\n              return next\n            })\n          }\n        }\n      })\n    }\n\n    router.invalidate({ filter, sync: true })\n  }\n\n  function walkReplaceSegmentTree(\n    route: AnyRouteWithPrivateProps,\n    node: AnyRouter['processedTree']['segmentTree'],\n  ) {\n    if (node.route?.id === route.id) node.route = route\n    if (node.index) walkReplaceSegmentTree(route, node.index)\n    node.static?.forEach((child) => walkReplaceSegmentTree(route, child))\n    node.staticInsensitive?.forEach((child) =>\n      walkReplaceSegmentTree(route, child),\n    )\n    node.dynamic?.forEach((child) => walkReplaceSegmentTree(route, child))\n    node.optional?.forEach((child) => walkReplaceSegmentTree(route, child))\n    node.wildcard?.forEach((child) => walkReplaceSegmentTree(route, child))\n  }\n}\n\nconst handleRouteUpdateStr = handleRouteUpdate.toString()\n\nexport function createRouteHmrStatement(\n  stableRouteOptionKeys: Array<string>,\n  opts?: { hotExpression?: string },\n): t.Statement {\n  return template.statement(\n    `\nif (%%hotExpression%%) {\n  const hot = %%hotExpression%%\n  const hotData = hot.data ??= {}\n  hot.accept((newModule) => {\n    if (Route && newModule && newModule.Route) {\n      const routeId = hotData['tsr-route-id'] ?? Route.id\n      if (routeId) {\n        hotData['tsr-route-id'] = routeId\n      }\n      (${handleRouteUpdateStr.replace(\n        /['\"]__TSR_COMPONENT_TYPES__['\"]/,\n        JSON.stringify(stableRouteOptionKeys),\n      )})(routeId, newModule.Route)\n    }\n    })\n}\n`,\n    {\n      syntacticPlaceholders: true,\n    },\n  )({\n    hotExpression: createHmrHotExpressionAst(opts?.hotExpression),\n  })\n}\n"],"mappings":";;;;;AAyCA,SAAS,kBACP,SACA,UACA;CACA,MAAM,SAAS,OAAO;CACtB,MAAM,WAAW,OAAO,WAAW;AAInC,KAAI,CAAC,SACH;CAYF,MAAM,8BAAc,IAAI,KAAa;AACrC,QAAO,KAAK,SAAS,QAAQ,CAAC,SAAS,QAAQ;AAC7C,MAAI,EAAE,OAAO,SAAS,UAAU;AAC9B,eAAY,IAAI,IAAI;AACpB,UAAO,SAAS,QAAQ;;GAE1B;AAIoB,2BACR,SAAS,QAAQ;AAC7B,MAAI,OAAO,SAAS,WAAW,OAAO,SAAS,QAC7C,UAAS,QAAQ,OAAO,SAAS,QAAQ;GAE3C;AAEF,UAAS,UAAU,SAAS;AAC5B,UAAS,OAAO,SAAS,QAAQ;AACjC,UAAS,qBAAqB,KAAA;AAC9B,UAAS,eAAe,KAAA;AAExB,QAAO,WAAW,SAAS,MAAM;AACjC,QAAO,aAAa,SAAS,YAAY;AAEzC,QAAO,cAAc,WAAW,OAAO;AACvC,QAAO,cAAc,WAAW,OAAO;AACvC,QAAO,cAAc,YAAY,OAAO;AACxC,QAAO,iBAAiB,OAAO;AAC/B,wBAAuB,UAAU,OAAO,cAAc,YAAY;CAElE,MAAM,UAAU,MAAqB,EAAE,YAAY,SAAS;CAC5D,MAAM,cAAc,OAAO,OAAO,QAAQ,KAAK,CAAC,KAAK,OAAO;CAC5D,MAAM,eAAe,OAAO,OAAO,eAAe,KAAK,CAAC,KAAK,OAAO;CACpE,MAAM,gBAAgB,OAAO,OAAO,cAAc,KAAK,CAAC,OAAO,OAAO;AAEtE,KAAI,eAAe,gBAAgB,cAAc,SAAS,GAAG;AAS3D,MAAI,YAAY,IAAI,SAAS,IAAI,YAAY,IAAI,aAAa,EAAE;GAC9D,MAAM,WAAW;IACf,aAAa;IACb,cAAc;IACd,GAAG,cAAc,KAAK,UAAU,MAAM,GAAG;IAC1C,CAAC,OAAO,QAAQ;AACjB,UAAO,YAAY;AACjB,SAAK,MAAM,WAAW,UAAU;KAC9B,MAAM,QACJ,OAAO,OAAO,mBAAmB,IAAI,QAAQ,IAC7C,OAAO,OAAO,YAAY,IAAI,QAAQ,IACtC,OAAO,OAAO,kBAAkB,IAAI,QAAQ;AAC9C,SAAI,MACF,OAAM,KAAK,SAAS;MAClB,MAAM,OAAsC,EAAE,GAAG,MAAM;AAEvD,UAAI,YAAY,IAAI,SAAS,CAC3B,MAAK,aAAa,KAAA;AAEpB,UAAI,YAAY,IAAI,aAAa,CAC/B,MAAK,sBAAsB,KAAA;AAG7B,aAAO;OACP;;KAGN;;AAGJ,SAAO,WAAW;GAAE;GAAQ,MAAM;GAAM,CAAC;;CAG3C,SAAS,uBACP,OACA,MACA;AACA,MAAI,KAAK,OAAO,OAAO,MAAM,GAAI,MAAK,QAAQ;AAC9C,MAAI,KAAK,MAAO,wBAAuB,OAAO,KAAK,MAAM;AACzD,OAAK,QAAQ,SAAS,UAAU,uBAAuB,OAAO,MAAM,CAAC;AACrE,OAAK,mBAAmB,SAAS,UAC/B,uBAAuB,OAAO,MAAM,CACrC;AACD,OAAK,SAAS,SAAS,UAAU,uBAAuB,OAAO,MAAM,CAAC;AACtE,OAAK,UAAU,SAAS,UAAU,uBAAuB,OAAO,MAAM,CAAC;AACvE,OAAK,UAAU,SAAS,UAAU,uBAAuB,OAAO,MAAM,CAAC;;;AAI3E,IAAM,uBAAuB,kBAAkB,UAAU;AAEzD,SAAgB,wBACd,uBACA,MACa;AACb,QAAO,gBAAS,UACd;;;;;;;;;;SAUK,qBAAqB,QACtB,mCACA,KAAK,UAAU,sBAAsB,CACtC,CAAC;;;;GAKJ,EACE,uBAAuB,MACxB,CACF,CAAC,EACA,eAAe,2BAAA,0BAA0B,MAAM,cAAc,EAC9D,CAAC"}