@tanstack/router-plugin
Version:
Modern and scalable routing for React applications
74 lines (64 loc) • 2.29 kB
text/typescript
import * as template from '@babel/template'
import * as t from '@babel/types'
import { createHmrHotExpressionAst } from '../../hmr-hot-expression'
import { getUniqueProgramIdentifier } from '../../utils'
import type { ReferenceRouteCompilerPlugin } from '../plugins'
const buildReactRefreshIgnoredRouteExportsStatements = template.statements(
`
const hot = %%hotExpression%%
if (hot && typeof window !== 'undefined') {
;(hot.data ??= {})
const tsrReactRefresh = window.__TSR_REACT_REFRESH__ ??= (() => {
const ignoredExportsById = new Map()
const previousGetIgnoredExports = window.__getReactRefreshIgnoredExports
window.__getReactRefreshIgnoredExports = (ctx) => {
const ignoredExports = previousGetIgnoredExports?.(ctx) ?? []
const moduleIgnored = ignoredExportsById.get(ctx.id) ?? []
return [...ignoredExports, ...moduleIgnored]
}
return {
ignoredExportsById,
}
})()
tsrReactRefresh.ignoredExportsById.set(%%moduleId%%, ['Route'])
}
`,
{ syntacticPlaceholders: true },
)
/**
* A trivial component-shaped export that gives `@vitejs/plugin-react` a valid
* Fast Refresh boundary. Without at least one non-ignored component export,
* the module would be invalidated (full page reload) on every update even
* though our custom route HMR handler already manages the update.
*/
const buildRefreshAnchorStatement = template.statement(
`export function %%anchorName%%() { return null }`,
{ syntacticPlaceholders: true },
)
export function createReactRefreshIgnoredRouteExportsPlugin(opts?: {
hotExpression?: string
}): ReferenceRouteCompilerPlugin {
return {
name: 'react-refresh-ignored-route-exports',
onAddHmr(ctx) {
const anchorName = getUniqueProgramIdentifier(
ctx.programPath,
'TSRFastRefreshAnchor',
)
ctx.programPath.pushContainer(
'body',
buildReactRefreshIgnoredRouteExportsStatements({
hotExpression: createHmrHotExpressionAst(
opts?.hotExpression ?? ctx.opts.hmrHotExpression,
),
moduleId: t.stringLiteral(ctx.opts.id),
}),
)
ctx.programPath.pushContainer(
'body',
buildRefreshAnchorStatement({ anchorName }),
)
return { modified: true }
},
}
}