@tanstack/router-plugin 1.167.1 → 1.167.3
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/core/code-splitter/compilers.cjs +63 -34
- package/dist/cjs/core/code-splitter/compilers.cjs.map +1 -1
- package/dist/cjs/core/code-splitter/plugins/framework-plugins.cjs +7 -1
- package/dist/cjs/core/code-splitter/plugins/framework-plugins.cjs.map +1 -1
- package/dist/cjs/core/code-splitter/plugins/react-refresh-ignored-route-exports.cjs +49 -0
- package/dist/cjs/core/code-splitter/plugins/react-refresh-ignored-route-exports.cjs.map +1 -0
- package/dist/cjs/core/code-splitter/plugins/react-refresh-ignored-route-exports.d.cts +2 -0
- package/dist/cjs/core/code-splitter/plugins/react-refresh-route-components.cjs +24 -12
- package/dist/cjs/core/code-splitter/plugins/react-refresh-route-components.cjs.map +1 -1
- package/dist/cjs/core/code-splitter/plugins/react-stable-hmr-split-route-components.cjs +41 -0
- package/dist/cjs/core/code-splitter/plugins/react-stable-hmr-split-route-components.cjs.map +1 -0
- package/dist/cjs/core/code-splitter/plugins/react-stable-hmr-split-route-components.d.cts +2 -0
- package/dist/cjs/core/code-splitter/plugins.d.cts +13 -0
- package/dist/cjs/core/code-splitter/types.d.cts +9 -0
- package/dist/cjs/core/route-hmr-statement.cjs +58 -15
- package/dist/cjs/core/route-hmr-statement.cjs.map +1 -1
- package/dist/cjs/core/route-hmr-statement.d.cts +1 -1
- package/dist/cjs/core/router-code-splitter-plugin.cjs +3 -3
- package/dist/cjs/core/router-code-splitter-plugin.cjs.map +1 -1
- package/dist/cjs/core/router-hmr-plugin.cjs +2 -2
- package/dist/cjs/core/router-hmr-plugin.cjs.map +1 -1
- package/dist/cjs/core/utils.cjs +9 -1
- package/dist/cjs/core/utils.cjs.map +1 -1
- package/dist/cjs/core/utils.d.cts +1 -0
- package/dist/esm/core/code-splitter/compilers.js +64 -35
- package/dist/esm/core/code-splitter/compilers.js.map +1 -1
- package/dist/esm/core/code-splitter/plugins/framework-plugins.js +7 -1
- package/dist/esm/core/code-splitter/plugins/framework-plugins.js.map +1 -1
- package/dist/esm/core/code-splitter/plugins/react-refresh-ignored-route-exports.d.ts +2 -0
- package/dist/esm/core/code-splitter/plugins/react-refresh-ignored-route-exports.js +46 -0
- package/dist/esm/core/code-splitter/plugins/react-refresh-ignored-route-exports.js.map +1 -0
- package/dist/esm/core/code-splitter/plugins/react-refresh-route-components.js +25 -13
- package/dist/esm/core/code-splitter/plugins/react-refresh-route-components.js.map +1 -1
- package/dist/esm/core/code-splitter/plugins/react-stable-hmr-split-route-components.d.ts +2 -0
- package/dist/esm/core/code-splitter/plugins/react-stable-hmr-split-route-components.js +38 -0
- package/dist/esm/core/code-splitter/plugins/react-stable-hmr-split-route-components.js.map +1 -0
- package/dist/esm/core/code-splitter/plugins.d.ts +13 -0
- package/dist/esm/core/code-splitter/types.d.ts +9 -0
- package/dist/esm/core/route-hmr-statement.d.ts +1 -1
- package/dist/esm/core/route-hmr-statement.js +58 -15
- package/dist/esm/core/route-hmr-statement.js.map +1 -1
- package/dist/esm/core/router-code-splitter-plugin.js +3 -3
- package/dist/esm/core/router-code-splitter-plugin.js.map +1 -1
- package/dist/esm/core/router-hmr-plugin.js +3 -3
- package/dist/esm/core/router-hmr-plugin.js.map +1 -1
- package/dist/esm/core/utils.d.ts +1 -0
- package/dist/esm/core/utils.js +9 -2
- package/dist/esm/core/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/core/code-splitter/compilers.ts +118 -62
- package/src/core/code-splitter/plugins/framework-plugins.ts +7 -1
- package/src/core/code-splitter/plugins/react-refresh-ignored-route-exports.ts +65 -0
- package/src/core/code-splitter/plugins/react-refresh-route-components.ts +68 -39
- package/src/core/code-splitter/plugins/react-stable-hmr-split-route-components.ts +56 -0
- package/src/core/code-splitter/plugins.ts +18 -0
- package/src/core/code-splitter/types.ts +11 -0
- package/src/core/route-hmr-statement.ts +141 -25
- package/src/core/router-code-splitter-plugin.ts +2 -2
- package/src/core/router-hmr-plugin.ts +7 -6
- package/src/core/utils.ts +27 -2
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
parseAst,
|
|
9
9
|
} from '@tanstack/router-utils'
|
|
10
10
|
import { tsrShared, tsrSplit } from '../constants'
|
|
11
|
-
import {
|
|
11
|
+
import { createRouteHmrStatement } from '../route-hmr-statement'
|
|
12
|
+
import { getObjectPropertyKeyName } from '../utils'
|
|
12
13
|
import { createIdentifier } from './path-ids'
|
|
13
14
|
import { getFrameworkOptions } from './framework-options'
|
|
14
15
|
import type {
|
|
@@ -18,14 +19,8 @@ import type {
|
|
|
18
19
|
import type { GeneratorResult, ParseAstOptions } from '@tanstack/router-utils'
|
|
19
20
|
import type { CodeSplitGroupings, SplitRouteIdentNodes } from '../constants'
|
|
20
21
|
import type { Config, DeletableNodes } from '../config'
|
|
22
|
+
import type { SplitNodeMeta } from './types'
|
|
21
23
|
|
|
22
|
-
type SplitNodeMeta = {
|
|
23
|
-
routeIdent: SplitRouteIdentNodes
|
|
24
|
-
splitStrategy: 'lazyFn' | 'lazyRouteComponent'
|
|
25
|
-
localImporterIdent: string
|
|
26
|
-
exporterIdent: string
|
|
27
|
-
localExporterIdent: string
|
|
28
|
-
}
|
|
29
24
|
const SPLIT_NODES_CONFIG = new Map<SplitRouteIdentNodes, SplitNodeMeta>([
|
|
30
25
|
[
|
|
31
26
|
'loader',
|
|
@@ -78,6 +73,7 @@ const SPLIT_NODES_CONFIG = new Map<SplitRouteIdentNodes, SplitNodeMeta>([
|
|
|
78
73
|
},
|
|
79
74
|
],
|
|
80
75
|
])
|
|
76
|
+
|
|
81
77
|
const KNOWN_SPLIT_ROUTE_IDENTS = [...SPLIT_NODES_CONFIG.keys()] as const
|
|
82
78
|
|
|
83
79
|
function addSplitSearchParamToFilename(
|
|
@@ -297,10 +293,12 @@ export function computeSharedBindings(opts: {
|
|
|
297
293
|
const splitGroupsPresent = new Set<number>()
|
|
298
294
|
let hasNonSplit = false
|
|
299
295
|
for (const prop of routeOptions.properties) {
|
|
300
|
-
if (!t.isObjectProperty(prop)
|
|
301
|
-
|
|
296
|
+
if (!t.isObjectProperty(prop)) continue
|
|
297
|
+
const key = getObjectPropertyKeyName(prop)
|
|
298
|
+
if (!key) continue
|
|
299
|
+
if (key === 'codeSplitGroupings') continue
|
|
302
300
|
if (t.isIdentifier(prop.value) && prop.value.name === 'undefined') continue
|
|
303
|
-
const groupIndex = findIndexForSplitNode(
|
|
301
|
+
const groupIndex = findIndexForSplitNode(key) // -1 if non-split
|
|
304
302
|
if (groupIndex === -1) {
|
|
305
303
|
hasNonSplit = true
|
|
306
304
|
} else {
|
|
@@ -333,8 +331,9 @@ export function computeSharedBindings(opts: {
|
|
|
333
331
|
const refsByGroup = new Map<string, Set<number>>()
|
|
334
332
|
|
|
335
333
|
for (const prop of routeOptions.properties) {
|
|
336
|
-
if (!t.isObjectProperty(prop)
|
|
337
|
-
const key = prop
|
|
334
|
+
if (!t.isObjectProperty(prop)) continue
|
|
335
|
+
const key = getObjectPropertyKeyName(prop)
|
|
336
|
+
if (!key) continue
|
|
338
337
|
|
|
339
338
|
if (key === 'codeSplitGroupings') continue
|
|
340
339
|
|
|
@@ -665,6 +664,13 @@ export function compileCodeSplitReferenceRoute(
|
|
|
665
664
|
const PACKAGE = frameworkOptions.package
|
|
666
665
|
const LAZY_ROUTE_COMPONENT_IDENT = frameworkOptions.idents.lazyRouteComponent
|
|
667
666
|
const LAZY_FN_IDENT = frameworkOptions.idents.lazyFn
|
|
667
|
+
const stableRouteOptionKeys = [
|
|
668
|
+
...new Set(
|
|
669
|
+
(opts.compilerPlugins ?? []).flatMap(
|
|
670
|
+
(plugin) => plugin.getStableRouteOptionKeys?.() ?? [],
|
|
671
|
+
),
|
|
672
|
+
),
|
|
673
|
+
]
|
|
668
674
|
|
|
669
675
|
let createRouteFn: string
|
|
670
676
|
|
|
@@ -702,16 +708,48 @@ export function compileCodeSplitReferenceRoute(
|
|
|
702
708
|
return programPath.scope.hasBinding(name)
|
|
703
709
|
}
|
|
704
710
|
|
|
711
|
+
const addRouteHmr = (
|
|
712
|
+
insertionPath: babel.NodePath,
|
|
713
|
+
routeOptions: t.ObjectExpression,
|
|
714
|
+
) => {
|
|
715
|
+
if (!opts.addHmr || hmrAdded) {
|
|
716
|
+
return
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
opts.compilerPlugins?.forEach((plugin) => {
|
|
720
|
+
const pluginResult = plugin.onAddHmr?.({
|
|
721
|
+
programPath,
|
|
722
|
+
callExpressionPath: path,
|
|
723
|
+
insertionPath,
|
|
724
|
+
routeOptions,
|
|
725
|
+
createRouteFn,
|
|
726
|
+
opts: opts as CompileCodeSplitReferenceRouteOptions,
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
if (pluginResult?.modified) {
|
|
730
|
+
modified = true
|
|
731
|
+
}
|
|
732
|
+
})
|
|
733
|
+
|
|
734
|
+
programPath.pushContainer(
|
|
735
|
+
'body',
|
|
736
|
+
createRouteHmrStatement(stableRouteOptionKeys),
|
|
737
|
+
)
|
|
738
|
+
modified = true
|
|
739
|
+
hmrAdded = true
|
|
740
|
+
}
|
|
741
|
+
|
|
705
742
|
if (t.isObjectExpression(routeOptions)) {
|
|
743
|
+
const insertionPath = path.getStatementParent() ?? path
|
|
744
|
+
|
|
706
745
|
if (opts.deleteNodes && opts.deleteNodes.size > 0) {
|
|
707
746
|
routeOptions.properties = routeOptions.properties.filter(
|
|
708
747
|
(prop) => {
|
|
709
748
|
if (t.isObjectProperty(prop)) {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
749
|
+
const key = getObjectPropertyKeyName(prop)
|
|
750
|
+
if (key && opts.deleteNodes!.has(key as any)) {
|
|
751
|
+
modified = true
|
|
752
|
+
return false
|
|
715
753
|
}
|
|
716
754
|
}
|
|
717
755
|
return true
|
|
@@ -719,8 +757,6 @@ export function compileCodeSplitReferenceRoute(
|
|
|
719
757
|
)
|
|
720
758
|
}
|
|
721
759
|
if (!splittableCreateRouteFns.includes(createRouteFn)) {
|
|
722
|
-
const insertionPath = path.getStatementParent() ?? path
|
|
723
|
-
|
|
724
760
|
opts.compilerPlugins?.forEach((plugin) => {
|
|
725
761
|
const pluginResult = plugin.onUnsplittableRoute?.({
|
|
726
762
|
programPath,
|
|
@@ -737,19 +773,15 @@ export function compileCodeSplitReferenceRoute(
|
|
|
737
773
|
})
|
|
738
774
|
|
|
739
775
|
// we can't split this route but we still add HMR handling if enabled
|
|
740
|
-
|
|
741
|
-
programPath.pushContainer('body', routeHmrStatement)
|
|
742
|
-
modified = true
|
|
743
|
-
hmrAdded = true
|
|
744
|
-
}
|
|
776
|
+
addRouteHmr(insertionPath, routeOptions)
|
|
745
777
|
// exit traversal so this route is not split
|
|
746
778
|
return programPath.stop()
|
|
747
779
|
}
|
|
748
780
|
routeOptions.properties.forEach((prop) => {
|
|
749
781
|
if (t.isObjectProperty(prop)) {
|
|
750
|
-
|
|
751
|
-
const key = prop.key.name
|
|
782
|
+
const key = getObjectPropertyKeyName(prop)
|
|
752
783
|
|
|
784
|
+
if (key) {
|
|
753
785
|
// If the user has not specified a split grouping for this key
|
|
754
786
|
// then we should not split it
|
|
755
787
|
const codeSplitGroupingByKey = findIndexForSplitNode(key)
|
|
@@ -858,16 +890,42 @@ export function compileCodeSplitReferenceRoute(
|
|
|
858
890
|
])
|
|
859
891
|
}
|
|
860
892
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
893
|
+
const insertionPath = path.getStatementParent() ?? path
|
|
894
|
+
let splitPropValue: t.Expression | undefined
|
|
895
|
+
|
|
896
|
+
for (const plugin of opts.compilerPlugins ?? []) {
|
|
897
|
+
const pluginPropValue = plugin.onSplitRouteProperty?.(
|
|
898
|
+
{
|
|
899
|
+
programPath,
|
|
900
|
+
callExpressionPath: path,
|
|
901
|
+
insertionPath,
|
|
902
|
+
routeOptions,
|
|
903
|
+
prop,
|
|
904
|
+
splitNodeMeta,
|
|
905
|
+
lazyRouteComponentIdent:
|
|
906
|
+
LAZY_ROUTE_COMPONENT_IDENT,
|
|
907
|
+
},
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
if (!pluginPropValue) {
|
|
911
|
+
continue
|
|
912
|
+
}
|
|
864
913
|
|
|
865
|
-
// add HMR handling
|
|
866
|
-
if (opts.addHmr && !hmrAdded) {
|
|
867
|
-
programPath.pushContainer('body', routeHmrStatement)
|
|
868
914
|
modified = true
|
|
869
|
-
|
|
915
|
+
splitPropValue = pluginPropValue
|
|
916
|
+
break
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (splitPropValue) {
|
|
920
|
+
prop.value = splitPropValue
|
|
921
|
+
} else {
|
|
922
|
+
prop.value = template.expression(
|
|
923
|
+
`${LAZY_ROUTE_COMPONENT_IDENT}(${splitNodeMeta.localImporterIdent}, '${splitNodeMeta.exporterIdent}')`,
|
|
924
|
+
)()
|
|
870
925
|
}
|
|
926
|
+
|
|
927
|
+
// add HMR handling
|
|
928
|
+
addRouteHmr(insertionPath, routeOptions)
|
|
871
929
|
} else {
|
|
872
930
|
// if (splitNodeMeta.splitStrategy === 'lazyFn') {
|
|
873
931
|
const value = prop.value
|
|
@@ -937,6 +995,8 @@ export function compileCodeSplitReferenceRoute(
|
|
|
937
995
|
|
|
938
996
|
programPath.scope.crawl()
|
|
939
997
|
})
|
|
998
|
+
|
|
999
|
+
addRouteHmr(insertionPath, routeOptions)
|
|
940
1000
|
}
|
|
941
1001
|
}
|
|
942
1002
|
|
|
@@ -1128,10 +1188,7 @@ export function compileCodeSplitVirtualRoute(
|
|
|
1128
1188
|
// since we have special considerations that need
|
|
1129
1189
|
// to be accounted for like (not splitting exported identifiers)
|
|
1130
1190
|
KNOWN_SPLIT_ROUTE_IDENTS.forEach((splitType) => {
|
|
1131
|
-
if (
|
|
1132
|
-
!t.isIdentifier(prop.key) ||
|
|
1133
|
-
prop.key.name !== splitType
|
|
1134
|
-
) {
|
|
1191
|
+
if (getObjectPropertyKeyName(prop) !== splitType) {
|
|
1135
1192
|
return
|
|
1136
1193
|
}
|
|
1137
1194
|
|
|
@@ -1673,33 +1730,32 @@ export function detectCodeSplitGroupingsFromRoute(opts: ParseAstOptions): {
|
|
|
1673
1730
|
if (t.isObjectExpression(routeOptions)) {
|
|
1674
1731
|
routeOptions.properties.forEach((prop) => {
|
|
1675
1732
|
if (t.isObjectProperty(prop)) {
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1733
|
+
const key = getObjectPropertyKeyName(prop)
|
|
1734
|
+
if (key === 'codeSplitGroupings') {
|
|
1735
|
+
const value = prop.value
|
|
1736
|
+
|
|
1737
|
+
if (t.isArrayExpression(value)) {
|
|
1738
|
+
codeSplitGroupings = value.elements.map((group) => {
|
|
1739
|
+
if (t.isArrayExpression(group)) {
|
|
1740
|
+
return group.elements.map((node) => {
|
|
1741
|
+
if (!t.isStringLiteral(node)) {
|
|
1742
|
+
throw new Error(
|
|
1743
|
+
'You must provide a string literal for the codeSplitGroupings',
|
|
1744
|
+
)
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
return node.value
|
|
1748
|
+
}) as Array<SplitRouteIdentNodes>
|
|
1749
|
+
}
|
|
1679
1750
|
|
|
1680
|
-
if (t.isArrayExpression(value)) {
|
|
1681
|
-
codeSplitGroupings = value.elements.map((group) => {
|
|
1682
|
-
if (t.isArrayExpression(group)) {
|
|
1683
|
-
return group.elements.map((node) => {
|
|
1684
|
-
if (!t.isStringLiteral(node)) {
|
|
1685
|
-
throw new Error(
|
|
1686
|
-
'You must provide a string literal for the codeSplitGroupings',
|
|
1687
|
-
)
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
return node.value
|
|
1691
|
-
}) as Array<SplitRouteIdentNodes>
|
|
1692
|
-
}
|
|
1693
|
-
|
|
1694
|
-
throw new Error(
|
|
1695
|
-
'You must provide arrays with codeSplitGroupings options.',
|
|
1696
|
-
)
|
|
1697
|
-
})
|
|
1698
|
-
} else {
|
|
1699
1751
|
throw new Error(
|
|
1700
|
-
'You must provide
|
|
1752
|
+
'You must provide arrays with codeSplitGroupings options.',
|
|
1701
1753
|
)
|
|
1702
|
-
}
|
|
1754
|
+
})
|
|
1755
|
+
} else {
|
|
1756
|
+
throw new Error(
|
|
1757
|
+
'You must provide an array of arrays for the codeSplitGroupings.',
|
|
1758
|
+
)
|
|
1703
1759
|
}
|
|
1704
1760
|
}
|
|
1705
1761
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { createReactRefreshIgnoredRouteExportsPlugin } from './react-refresh-ignored-route-exports'
|
|
1
2
|
import { createReactRefreshRouteComponentsPlugin } from './react-refresh-route-components'
|
|
3
|
+
import { createReactStableHmrSplitRouteComponentsPlugin } from './react-stable-hmr-split-route-components'
|
|
2
4
|
import type { ReferenceRouteCompilerPlugin } from '../plugins'
|
|
3
5
|
import type { Config } from '../../config'
|
|
4
6
|
|
|
@@ -9,7 +11,11 @@ export function getReferenceRouteCompilerPlugins(opts: {
|
|
|
9
11
|
switch (opts.targetFramework) {
|
|
10
12
|
case 'react': {
|
|
11
13
|
if (opts.addHmr) {
|
|
12
|
-
return [
|
|
14
|
+
return [
|
|
15
|
+
createReactRefreshIgnoredRouteExportsPlugin(),
|
|
16
|
+
createReactRefreshRouteComponentsPlugin(),
|
|
17
|
+
createReactStableHmrSplitRouteComponentsPlugin(),
|
|
18
|
+
]
|
|
13
19
|
}
|
|
14
20
|
return undefined
|
|
15
21
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as template from '@babel/template'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
import { getUniqueProgramIdentifier } from '../../utils'
|
|
4
|
+
import type { ReferenceRouteCompilerPlugin } from '../plugins'
|
|
5
|
+
|
|
6
|
+
const buildReactRefreshIgnoredRouteExportsStatement = template.statement(
|
|
7
|
+
`
|
|
8
|
+
if (import.meta.hot && typeof window !== 'undefined') {
|
|
9
|
+
const tsrReactRefresh = window.__TSR_REACT_REFRESH__ ??= (() => {
|
|
10
|
+
const ignoredExportsById = new Map()
|
|
11
|
+
const previousGetIgnoredExports = window.__getReactRefreshIgnoredExports
|
|
12
|
+
|
|
13
|
+
window.__getReactRefreshIgnoredExports = (ctx) => {
|
|
14
|
+
const ignoredExports = previousGetIgnoredExports?.(ctx) ?? []
|
|
15
|
+
const moduleIgnored = ignoredExportsById.get(ctx.id) ?? []
|
|
16
|
+
return [...ignoredExports, ...moduleIgnored]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
ignoredExportsById,
|
|
21
|
+
}
|
|
22
|
+
})()
|
|
23
|
+
|
|
24
|
+
tsrReactRefresh.ignoredExportsById.set(%%moduleId%%, ['Route'])
|
|
25
|
+
}
|
|
26
|
+
`,
|
|
27
|
+
{ syntacticPlaceholders: true },
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A trivial component-shaped export that gives `@vitejs/plugin-react` a valid
|
|
32
|
+
* Fast Refresh boundary. Without at least one non-ignored component export,
|
|
33
|
+
* the module would be invalidated (full page reload) on every update even
|
|
34
|
+
* though our custom route HMR handler already manages the update.
|
|
35
|
+
*/
|
|
36
|
+
const buildRefreshAnchorStatement = template.statement(
|
|
37
|
+
`export function %%anchorName%%() { return null }`,
|
|
38
|
+
{ syntacticPlaceholders: true },
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
export function createReactRefreshIgnoredRouteExportsPlugin(): ReferenceRouteCompilerPlugin {
|
|
42
|
+
return {
|
|
43
|
+
name: 'react-refresh-ignored-route-exports',
|
|
44
|
+
onAddHmr(ctx) {
|
|
45
|
+
const anchorName = getUniqueProgramIdentifier(
|
|
46
|
+
ctx.programPath,
|
|
47
|
+
'TSRFastRefreshAnchor',
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
ctx.programPath.pushContainer(
|
|
51
|
+
'body',
|
|
52
|
+
buildReactRefreshIgnoredRouteExportsStatement({
|
|
53
|
+
moduleId: t.stringLiteral(ctx.opts.id),
|
|
54
|
+
}),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
ctx.programPath.pushContainer(
|
|
58
|
+
'body',
|
|
59
|
+
buildRefreshAnchorStatement({ anchorName }),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return { modified: true }
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -1,63 +1,92 @@
|
|
|
1
1
|
import * as t from '@babel/types'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getObjectPropertyKeyName,
|
|
4
|
+
getUniqueProgramIdentifier,
|
|
5
|
+
} from '../../utils'
|
|
3
6
|
import type { ReferenceRouteCompilerPlugin } from '../plugins'
|
|
4
7
|
|
|
5
8
|
const REACT_REFRESH_ROUTE_COMPONENT_IDENTS = new Set([
|
|
6
9
|
'component',
|
|
10
|
+
'shellComponent',
|
|
7
11
|
'pendingComponent',
|
|
8
12
|
'errorComponent',
|
|
9
13
|
'notFoundComponent',
|
|
10
14
|
])
|
|
11
15
|
|
|
16
|
+
function hoistInlineRouteComponents(ctx: {
|
|
17
|
+
programPath: Parameters<typeof getUniqueProgramIdentifier>[0]
|
|
18
|
+
insertionPath: { insertBefore: (nodes: Array<t.VariableDeclaration>) => void }
|
|
19
|
+
routeOptions: t.ObjectExpression
|
|
20
|
+
}) {
|
|
21
|
+
const hoistedDeclarations: Array<t.VariableDeclaration> = []
|
|
22
|
+
|
|
23
|
+
ctx.routeOptions.properties.forEach((prop) => {
|
|
24
|
+
if (!t.isObjectProperty(prop)) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const key = getObjectPropertyKeyName(prop)
|
|
29
|
+
|
|
30
|
+
if (!key || !REACT_REFRESH_ROUTE_COMPONENT_IDENTS.has(key)) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
!t.isArrowFunctionExpression(prop.value) &&
|
|
36
|
+
!t.isFunctionExpression(prop.value)
|
|
37
|
+
) {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const hoistedIdentifier = getUniqueProgramIdentifier(
|
|
42
|
+
ctx.programPath,
|
|
43
|
+
`TSR${key[0]!.toUpperCase()}${key.slice(1)}`,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
hoistedDeclarations.push(
|
|
47
|
+
t.variableDeclaration('const', [
|
|
48
|
+
t.variableDeclarator(hoistedIdentifier, t.cloneNode(prop.value, true)),
|
|
49
|
+
]),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
prop.value = t.cloneNode(hoistedIdentifier)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (hoistedDeclarations.length === 0) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
ctx.insertionPath.insertBefore(hoistedDeclarations)
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
|
|
12
63
|
export function createReactRefreshRouteComponentsPlugin(): ReferenceRouteCompilerPlugin {
|
|
13
64
|
return {
|
|
14
65
|
name: 'react-refresh-route-components',
|
|
66
|
+
getStableRouteOptionKeys() {
|
|
67
|
+
return [...REACT_REFRESH_ROUTE_COMPONENT_IDENTS]
|
|
68
|
+
},
|
|
15
69
|
onUnsplittableRoute(ctx) {
|
|
16
70
|
if (!ctx.opts.addHmr) {
|
|
17
71
|
return
|
|
18
72
|
}
|
|
19
73
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
!t.isArrowFunctionExpression(prop.value) &&
|
|
33
|
-
!t.isFunctionExpression(prop.value)
|
|
34
|
-
) {
|
|
35
|
-
return
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const hoistedIdentifier = getUniqueProgramIdentifier(
|
|
39
|
-
ctx.programPath,
|
|
40
|
-
`TSR${prop.key.name[0]!.toUpperCase()}${prop.key.name.slice(1)}`,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
hoistedDeclarations.push(
|
|
44
|
-
t.variableDeclaration('const', [
|
|
45
|
-
t.variableDeclarator(
|
|
46
|
-
hoistedIdentifier,
|
|
47
|
-
t.cloneNode(prop.value, true),
|
|
48
|
-
),
|
|
49
|
-
]),
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
prop.value = t.cloneNode(hoistedIdentifier)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
if (hoistedDeclarations.length === 0) {
|
|
74
|
+
if (hoistInlineRouteComponents(ctx)) {
|
|
75
|
+
return { modified: true }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return
|
|
79
|
+
},
|
|
80
|
+
onAddHmr(ctx) {
|
|
81
|
+
if (!ctx.opts.addHmr) {
|
|
56
82
|
return
|
|
57
83
|
}
|
|
58
84
|
|
|
59
|
-
ctx
|
|
60
|
-
|
|
85
|
+
if (hoistInlineRouteComponents(ctx)) {
|
|
86
|
+
return { modified: true }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return
|
|
61
90
|
},
|
|
62
91
|
}
|
|
63
92
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as template from '@babel/template'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
import { getUniqueProgramIdentifier } from '../../utils'
|
|
4
|
+
import type { ReferenceRouteCompilerPlugin } from '../plugins'
|
|
5
|
+
|
|
6
|
+
function capitalizeIdentifier(str: string) {
|
|
7
|
+
return str[0]!.toUpperCase() + str.slice(1)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function createHotDataKey(exportName: string) {
|
|
11
|
+
return `tsr-split-component:${exportName}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const buildStableSplitComponentStatements = template.statements(
|
|
15
|
+
`
|
|
16
|
+
const %%stableComponentIdent%% = import.meta.hot?.data?.[%%hotDataKey%%] ?? %%lazyRouteComponentIdent%%(%%localImporterIdent%%, %%exporterIdent%%)
|
|
17
|
+
if (import.meta.hot) {
|
|
18
|
+
import.meta.hot.data[%%hotDataKey%%] = %%stableComponentIdent%%
|
|
19
|
+
}
|
|
20
|
+
`,
|
|
21
|
+
{
|
|
22
|
+
syntacticPlaceholders: true,
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
export function createReactStableHmrSplitRouteComponentsPlugin(): ReferenceRouteCompilerPlugin {
|
|
27
|
+
return {
|
|
28
|
+
name: 'react-stable-hmr-split-route-components',
|
|
29
|
+
onSplitRouteProperty(ctx) {
|
|
30
|
+
if (ctx.splitNodeMeta.splitStrategy !== 'lazyRouteComponent') {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const stableComponentIdent = getUniqueProgramIdentifier(
|
|
35
|
+
ctx.programPath,
|
|
36
|
+
`TSRSplit${capitalizeIdentifier(ctx.splitNodeMeta.exporterIdent)}`,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
const hotDataKey = createHotDataKey(ctx.splitNodeMeta.exporterIdent)
|
|
40
|
+
|
|
41
|
+
ctx.insertionPath.insertBefore(
|
|
42
|
+
buildStableSplitComponentStatements({
|
|
43
|
+
stableComponentIdent,
|
|
44
|
+
hotDataKey: t.stringLiteral(hotDataKey),
|
|
45
|
+
lazyRouteComponentIdent: t.identifier(ctx.lazyRouteComponentIdent),
|
|
46
|
+
localImporterIdent: t.identifier(
|
|
47
|
+
ctx.splitNodeMeta.localImporterIdent,
|
|
48
|
+
),
|
|
49
|
+
exporterIdent: t.stringLiteral(ctx.splitNodeMeta.exporterIdent),
|
|
50
|
+
}),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return t.identifier(stableComponentIdent.name)
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -2,6 +2,7 @@ import type babel from '@babel/core'
|
|
|
2
2
|
import type * as t from '@babel/types'
|
|
3
3
|
import type { Config, DeletableNodes } from '../config'
|
|
4
4
|
import type { CodeSplitGroupings } from '../constants'
|
|
5
|
+
import type { SplitNodeMeta } from './types'
|
|
5
6
|
|
|
6
7
|
export type CompileCodeSplitReferenceRouteOptions = {
|
|
7
8
|
codeSplitGroupings: CodeSplitGroupings
|
|
@@ -22,13 +23,30 @@ export type ReferenceRouteCompilerPluginContext = {
|
|
|
22
23
|
opts: CompileCodeSplitReferenceRouteOptions
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
export type ReferenceRouteSplitPropertyCompilerPluginContext = {
|
|
27
|
+
programPath: babel.NodePath<t.Program>
|
|
28
|
+
callExpressionPath: babel.NodePath<t.CallExpression>
|
|
29
|
+
insertionPath: babel.NodePath
|
|
30
|
+
routeOptions: t.ObjectExpression
|
|
31
|
+
prop: t.ObjectProperty
|
|
32
|
+
splitNodeMeta: SplitNodeMeta
|
|
33
|
+
lazyRouteComponentIdent: string
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
export type ReferenceRouteCompilerPluginResult = {
|
|
26
37
|
modified?: boolean
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
export type ReferenceRouteCompilerPlugin = {
|
|
30
41
|
name: string
|
|
42
|
+
getStableRouteOptionKeys?: () => Array<string>
|
|
43
|
+
onAddHmr?: (
|
|
44
|
+
ctx: ReferenceRouteCompilerPluginContext,
|
|
45
|
+
) => void | ReferenceRouteCompilerPluginResult
|
|
31
46
|
onUnsplittableRoute?: (
|
|
32
47
|
ctx: ReferenceRouteCompilerPluginContext,
|
|
33
48
|
) => void | ReferenceRouteCompilerPluginResult
|
|
49
|
+
onSplitRouteProperty?: (
|
|
50
|
+
ctx: ReferenceRouteSplitPropertyCompilerPluginContext,
|
|
51
|
+
) => void | t.Expression
|
|
34
52
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SplitRouteIdentNodes } from '../constants'
|
|
2
|
+
|
|
3
|
+
export type SplitStrategy = 'lazyFn' | 'lazyRouteComponent'
|
|
4
|
+
|
|
5
|
+
export type SplitNodeMeta = {
|
|
6
|
+
routeIdent: SplitRouteIdentNodes
|
|
7
|
+
splitStrategy: SplitStrategy
|
|
8
|
+
localImporterIdent: string
|
|
9
|
+
exporterIdent: string
|
|
10
|
+
localExporterIdent: string
|
|
11
|
+
}
|