diff --git a/packages/router-core/src/route.ts b/packages/router-core/src/route.ts index b67651cbb0f..4b4e4404cc3 100644 --- a/packages/router-core/src/route.ts +++ b/packages/router-core/src/route.ts @@ -1709,15 +1709,6 @@ export class BaseRoute< this._to = fullPath as TrimPathRight } - clone = (other: typeof this) => { - this._path = other._path - this._id = other._id - this._fullPath = other._fullPath - this._to = other._to - this.options.getParentRoute = other.options.getParentRoute - this.children = other.children - } - addChildren: RouteAddChildrenFn< TRegister, TParentRoute, diff --git a/packages/router-plugin/src/core/code-splitter/compilers.ts b/packages/router-plugin/src/core/code-splitter/compilers.ts index 5c2da5d3907..b5ade26a12d 100644 --- a/packages/router-plugin/src/core/code-splitter/compilers.ts +++ b/packages/router-plugin/src/core/code-splitter/compilers.ts @@ -133,6 +133,7 @@ export function compileCodeSplitReferenceRoute( let createRouteFn: string let modified = false as boolean + let hmrAdded = false as boolean babel.traverse(ast, { Program: { enter(programPath) { @@ -182,9 +183,10 @@ export function compileCodeSplitReferenceRoute( } if (!splittableCreateRouteFns.includes(createRouteFn)) { // we can't split this route but we still add HMR handling if enabled - if (opts.addHmr) { - modified = true + if (opts.addHmr && !hmrAdded) { programPath.pushContainer('body', routeHmrStatement) + modified = true + hmrAdded = true } // exit traversal so this route is not split return programPath.stop() @@ -307,8 +309,10 @@ export function compileCodeSplitReferenceRoute( )() // add HMR handling - if (opts.addHmr) { + if (opts.addHmr && !hmrAdded) { programPath.pushContainer('body', routeHmrStatement) + modified = true + hmrAdded = true } } else { // if (splitNodeMeta.splitStrategy === 'lazyFn') { diff --git a/packages/router-plugin/src/core/route-hmr-statement.ts b/packages/router-plugin/src/core/route-hmr-statement.ts index 2815b892729..3080c17a19a 100644 --- a/packages/router-plugin/src/core/route-hmr-statement.ts +++ b/packages/router-plugin/src/core/route-hmr-statement.ts @@ -1,13 +1,44 @@ import * as template from '@babel/template' +import type { AnyRoute } from '@tanstack/router-core' + +type AnyRouteWithPrivateProps = AnyRoute & { + _path: string + _id: string + _fullPath: string + _to: string +} + +function handleRouteUpdate( + oldRoute: AnyRouteWithPrivateProps, + newRoute: AnyRouteWithPrivateProps, +) { + newRoute._path = oldRoute._path + newRoute._id = oldRoute._id + newRoute._fullPath = oldRoute._fullPath + newRoute._to = oldRoute._to + newRoute.children = oldRoute.children + newRoute.parentRoute = oldRoute.parentRoute + + const router = window.__TSR_ROUTER__! + router.routesById[newRoute.id] = newRoute + router.routesByPath[newRoute.fullPath] = newRoute + const oldRouteIndex = router.flatRoutes.indexOf(oldRoute) + if (oldRouteIndex > -1) { + router.flatRoutes[oldRouteIndex] = newRoute + } + router.invalidate({ filter: (m) => m.routeId === oldRoute.id }) +} export const routeHmrStatement = template.statement( ` if (import.meta.hot) { import.meta.hot.accept((newModule) => { - if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { - newModule.Route.clone(Route) + if (Route && newModule && newModule.Route) { + (${handleRouteUpdate.toString()})(Route, newModule.Route) } }) } `, + // Disable placeholder parsing so identifiers like __TSR_ROUTER__ are treated as normal identifiers instead of template placeholders + { placeholderPattern: false }, )() diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx index c73adc15365..3ba73b49a83 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx @@ -9,8 +9,26 @@ export const Route = createFileRoute('/posts')({ }); if (import.meta.hot) { import.meta.hot.accept(newModule => { - if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { - newModule.Route.clone(Route); + if (Route && newModule && newModule.Route) { + (function handleRouteUpdate(oldRoute, newRoute) { + newRoute._path = oldRoute._path; + newRoute._id = oldRoute._id; + newRoute._fullPath = oldRoute._fullPath; + newRoute._to = oldRoute._to; + newRoute.children = oldRoute.children; + newRoute.parentRoute = oldRoute.parentRoute; + const router = window.__TSR_ROUTER__; + router.routesById[newRoute.id] = newRoute; + router.routesByPath[newRoute.fullPath] = newRoute; + const oldRouteIndex = router.flatRoutes.indexOf(oldRoute); + if (oldRouteIndex > -1) { + router.flatRoutes[oldRouteIndex] = newRoute; + } + ; + router.invalidate({ + filter: m => m.routeId === oldRoute.id + }); + })(Route, newModule.Route); } }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx index 9d1550f3671..534faf3c154 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx @@ -9,8 +9,26 @@ export const Route = createFileRoute('/posts')({ }); if (import.meta.hot) { import.meta.hot.accept(newModule => { - if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { - newModule.Route.clone(Route); + if (Route && newModule && newModule.Route) { + (function handleRouteUpdate(oldRoute, newRoute) { + newRoute._path = oldRoute._path; + newRoute._id = oldRoute._id; + newRoute._fullPath = oldRoute._fullPath; + newRoute._to = oldRoute._to; + newRoute.children = oldRoute.children; + newRoute.parentRoute = oldRoute.parentRoute; + const router = window.__TSR_ROUTER__; + router.routesById[newRoute.id] = newRoute; + router.routesByPath[newRoute.fullPath] = newRoute; + const oldRouteIndex = router.flatRoutes.indexOf(oldRoute); + if (oldRouteIndex > -1) { + router.flatRoutes[oldRouteIndex] = newRoute; + } + ; + router.invalidate({ + filter: m => m.routeId === oldRoute.id + }); + })(Route, newModule.Route); } }); } \ No newline at end of file diff --git a/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx index faada7e55bb..3b42bca7c45 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx @@ -8,8 +8,26 @@ export const Route = createFileRoute('/posts')({ }); if (import.meta.hot) { import.meta.hot.accept(newModule => { - if (newModule && newModule.Route && typeof newModule.Route.clone === 'function') { - newModule.Route.clone(Route); + if (Route && newModule && newModule.Route) { + (function handleRouteUpdate(oldRoute, newRoute) { + newRoute._path = oldRoute._path; + newRoute._id = oldRoute._id; + newRoute._fullPath = oldRoute._fullPath; + newRoute._to = oldRoute._to; + newRoute.children = oldRoute.children; + newRoute.parentRoute = oldRoute.parentRoute; + const router = window.__TSR_ROUTER__; + router.routesById[newRoute.id] = newRoute; + router.routesByPath[newRoute.fullPath] = newRoute; + const oldRouteIndex = router.flatRoutes.indexOf(oldRoute); + if (oldRouteIndex > -1) { + router.flatRoutes[oldRouteIndex] = newRoute; + } + ; + router.invalidate({ + filter: m => m.routeId === oldRoute.id + }); + })(Route, newModule.Route); } }); } \ No newline at end of file